Compare commits

..

1 Commits

Author SHA1 Message Date
Daniel Martí
8f4ec9e084 implement CidFromReader
And reuse two CidFromBytes tests for it, which includes both CIDv0 and
CIDv1 cases as inputs, as well as some inputs that should error.

Fixes #126.
2021-07-14 23:28:25 +01:00
19 changed files with 263 additions and 473 deletions

View File

@@ -1,11 +1,51 @@
# File managed by web3-bot. DO NOT EDIT. # File managed by web3-bot. DO NOT EDIT.
# See https://github.com/protocol/.github/ for details. # See https://github.com/protocol/.github/ for details.
name: Automerge # Automatically merge pull requests opened by web3-bot, as soon as (and only if) all tests pass.
# This reduces the friction associated with updating with our workflows.
on: [ pull_request ] on: [ pull_request ]
name: Automerge
jobs: jobs:
automerge-check:
if: github.event.pull_request.user.login == 'web3-bot'
runs-on: ubuntu-latest
outputs:
status: ${{ steps.should-automerge.outputs.status }}
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Check if we should automerge
id: should-automerge
run: |
for commit in $(git rev-list --first-parent origin/${{ github.event.pull_request.base.ref }}..${{ github.event.pull_request.head.sha }}); do
committer=$(git show --format=$'%ce' -s $commit)
echo "Committer: $committer"
if [[ "$committer" != "web3-bot@users.noreply.github.com" ]]; then
echo "Commit $commit wasn't committed by web3-bot, but by $committer."
echo "::set-output name=status::false"
exit
fi
done
echo "::set-output name=status::true"
automerge: automerge:
uses: protocol/.github/.github/workflows/automerge.yml@master needs: automerge-check
with: runs-on: ubuntu-latest
job: 'automerge' if: ${{ needs.automerge-check.outputs.status == 'true' }}
steps:
- name: Wait on tests
uses: lewagon/wait-on-check-action@bafe56a6863672c681c3cf671f5e10b20abf2eaa # v0.2
with:
ref: ${{ github.event.pull_request.head.sha }}
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
running-workflow-name: 'automerge' # the name of this job
- name: Merge PR
uses: pascalgn/automerge-action@741c311a47881be9625932b0a0de1b0937aab1ae # v0.13.1
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
MERGE_LABELS: ""
MERGE_METHOD: "squash"
MERGE_DELETE_BRANCH: true

View File

@@ -9,21 +9,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: All name: All
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
submodules: recursive submodules: recursive
- id: config - uses: actions/setup-go@v2
uses: protocol/.github/.github/actions/read-config@master
- uses: actions/setup-go@v3
with: with:
go-version: 1.20.x go-version: "1.16.x"
- name: Run repo-specific setup
uses: ./.github/actions/go-check-setup
if: hashFiles('./.github/actions/go-check-setup') != ''
- name: Install staticcheck - name: Install staticcheck
run: go install honnef.co/go/tools/cmd/staticcheck@4970552d932f48b71485287748246cf3237cebdf # 2023.1 (v0.4.0) run: go install honnef.co/go/tools/cmd/staticcheck@434f5f3816b358fe468fa83dcba62d794e7fe04b # 2021.1 (v0.2.0)
- name: Check that go.mod is tidy - name: Check that go.mod is tidy
uses: protocol/multiple-go-modules@v1.2 uses: protocol/multiple-go-modules@v1.0
with: with:
run: | run: |
go mod tidy go mod tidy
@@ -33,7 +28,7 @@ jobs:
fi fi
git diff --exit-code -- go.sum go.mod git diff --exit-code -- go.sum go.mod
- name: gofmt - name: gofmt
if: success() || failure() # run this step even if the previous one failed if: ${{ success() || failure() }} # run this step even if the previous one failed
run: | run: |
out=$(gofmt -s -l .) out=$(gofmt -s -l .)
if [[ -n "$out" ]]; then if [[ -n "$out" ]]; then
@@ -41,27 +36,15 @@ jobs:
exit 1 exit 1
fi fi
- name: go vet - name: go vet
if: success() || failure() # run this step even if the previous one failed if: ${{ success() || failure() }} # run this step even if the previous one failed
uses: protocol/multiple-go-modules@v1.2 uses: protocol/multiple-go-modules@v1.0
with: with:
run: go vet ./... run: go vet ./...
- name: staticcheck - name: staticcheck
if: success() || failure() # run this step even if the previous one failed if: ${{ success() || failure() }} # run this step even if the previous one failed
uses: protocol/multiple-go-modules@v1.2 uses: protocol/multiple-go-modules@v1.0
with: with:
run: | run: |
set -o pipefail set -o pipefail
staticcheck ./... | sed -e 's@\(.*\)\.go@./\1.go@g' staticcheck ./... | sed -e 's@\(.*\)\.go@./\1.go@g'
- name: go generate
uses: protocol/multiple-go-modules@v1.2
if: (success() || failure()) && fromJSON(steps.config.outputs.json).gogenerate == true
with:
run: |
git clean -fd # make sure there aren't untracked files / directories
go generate -x ./...
# check if go generate modified or added any files
if ! $(git add . && git diff-index HEAD --exit-code --quiet); then
echo "go generated caused changes to the repository:"
git status --short
exit 1
fi

View File

@@ -10,67 +10,38 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ "ubuntu", "windows", "macos" ] os: [ "ubuntu", "windows", "macos" ]
go: ["1.19.x","1.20.x"] go: [ "1.15.x", "1.16.x" ]
env: runs-on: ${{ matrix.os }}-latest
COVERAGES: "" name: ${{ matrix.os}} (go ${{ matrix.go }})
runs-on: ${{ fromJSON(vars[format('UCI_GO_TEST_RUNNER_{0}', matrix.os)] || format('"{0}-latest"', matrix.os)) }}
name: ${{ matrix.os }} (go ${{ matrix.go }})
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
submodules: recursive submodules: recursive
- id: config - uses: actions/setup-go@v2
uses: protocol/.github/.github/actions/read-config@master
- uses: actions/setup-go@v3
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
- name: Go information - name: Go information
run: | run: |
go version go version
go env go env
- name: Use msys2 on windows
if: matrix.os == 'windows'
shell: bash
# The executable for msys2 is also called bash.cmd
# https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#shells
# If we prepend its location to the PATH
# subsequent 'shell: bash' steps will use msys2 instead of gitbash
run: echo "C:/msys64/usr/bin" >> $GITHUB_PATH
- name: Run repo-specific setup
uses: ./.github/actions/go-test-setup
if: hashFiles('./.github/actions/go-test-setup') != ''
- name: Run tests - name: Run tests
if: contains(fromJSON(steps.config.outputs.json).skipOSes, matrix.os) == false uses: protocol/multiple-go-modules@v1.0
uses: protocol/multiple-go-modules@v1.2
with: with:
# Use -coverpkg=./..., so that we include cross-package coverage. run: go test -v -coverprofile coverage.txt ./...
# If package ./A imports ./B, and ./A's tests also cover ./B,
# this means ./B's coverage will be significantly higher than 0%.
run: go test -v -shuffle=on -coverprofile=module-coverage.txt -coverpkg=./... ./...
- name: Run tests (32 bit) - name: Run tests (32 bit)
# can't run 32 bit tests on OSX. if: ${{ matrix.os != 'macos' }} # can't run 32 bit tests on OSX.
if: matrix.os != 'macos' && uses: protocol/multiple-go-modules@v1.0
fromJSON(steps.config.outputs.json).skip32bit != true &&
contains(fromJSON(steps.config.outputs.json).skipOSes, matrix.os) == false
uses: protocol/multiple-go-modules@v1.2
env: env:
GOARCH: 386 GOARCH: 386
with: with:
run: | run: go test -v ./...
export "PATH=$PATH_386:$PATH"
go test -v -shuffle=on ./...
- name: Run tests with race detector - name: Run tests with race detector
# speed things up. Windows and OSX VMs are slow if: ${{ matrix.os == 'ubuntu' }} # speed things up. Windows and OSX VMs are slow
if: matrix.os == 'ubuntu' && uses: protocol/multiple-go-modules@v1.0
contains(fromJSON(steps.config.outputs.json).skipOSes, matrix.os) == false
uses: protocol/multiple-go-modules@v1.2
with: with:
run: go test -v -race ./... run: go test -v -race ./...
- name: Collect coverage files
shell: bash
run: echo "COVERAGES=$(find . -type f -name 'module-coverage.txt' | tr -s '\n' ',' | sed 's/,$//')" >> $GITHUB_ENV
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1 uses: codecov/codecov-action@a1ed4b322b4b38cb846afb5a0ebfa17086917d27 # v1.5.0
with: with:
files: '${{ env.COVERAGES }}' file: coverage.txt
env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }} env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }}

View File

@@ -1,13 +0,0 @@
# File managed by web3-bot. DO NOT EDIT.
# See https://github.com/protocol/.github/ for details.
name: Release Checker
on:
pull_request_target:
paths: [ 'version.json' ]
jobs:
release-check:
uses: protocol/.github/.github/workflows/release-check.yml@master
with:
go-version: 1.20.x

View File

@@ -1,11 +0,0 @@
# File managed by web3-bot. DO NOT EDIT.
# See https://github.com/protocol/.github/ for details.
name: Releaser
on:
push:
paths: [ 'version.json' ]
jobs:
releaser:
uses: protocol/.github/.github/workflows/releaser.yml@master

View File

@@ -1,26 +0,0 @@
name: Close and mark stale issue
on:
schedule:
- cron: '0 0 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Oops, seems like we needed more information for this issue, please comment with more details or this issue will be closed in 7 days.'
close-issue-message: 'This issue was closed because it is missing author input.'
stale-issue-label: 'kind/stale'
any-of-labels: 'need/author-input'
exempt-issue-labels: 'need/triage,need/community-input,need/maintainer-input,need/maintainers-input,need/analysis,status/blocked,status/in-progress,status/ready,status/deferred,status/inactive'
days-before-issue-stale: 6
days-before-issue-close: 7
enable-statistics: true

View File

@@ -1,12 +0,0 @@
# File managed by web3-bot. DO NOT EDIT.
# See https://github.com/protocol/.github/ for details.
name: Tag Push Checker
on:
push:
tags:
- v*
jobs:
releaser:
uses: protocol/.github/.github/workflows/tagpush.yml@master

View File

@@ -59,17 +59,10 @@ fmt.Println("Got CID: ", c)
#### Creating a CID from scratch #### Creating a CID from scratch
```go ```go
import (
cid "github.com/ipfs/go-cid"
mc "github.com/multiformats/go-multicodec"
mh "github.com/multiformats/go-multihash"
)
// Create a cid manually by specifying the 'prefix' parameters // Create a cid manually by specifying the 'prefix' parameters
pref := cid.Prefix{ pref := cid.Prefix{
Version: 1, Version: 1,
Codec: uint64(mc.Raw), Codec: cid.Raw,
MhType: mh.SHA2_256, MhType: mh.SHA2_256,
MhLength: -1, // default length MhLength: -1, // default length
} }

View File

@@ -39,8 +39,7 @@ type Cid interface {
// and the Multihash length. It does not contains // and the Multihash length. It does not contains
// any actual content information. // any actual content information.
// NOTE: The use -1 in MhLength to mean default length is deprecated, // NOTE: The use -1 in MhLength to mean default length is deprecated,
// // use the V0Builder or V1Builder structures instead
// use the V0Builder or V1Builder structures instead
type Prefix struct { type Prefix struct {
Version uint64 Version uint64
Codec uint64 Codec uint64

View File

@@ -12,14 +12,14 @@ import (
// //
// Sample results on linux amd64 go1.11beta: // Sample results on linux amd64 go1.11beta:
// //
// BenchmarkCidMap_CidStr-8 100000 16317 ns/op // BenchmarkCidMap_CidStr-8 100000 16317 ns/op
// BenchmarkCidMap_CidIface-8 100000 20516 ns/op // BenchmarkCidMap_CidIface-8 100000 20516 ns/op
// //
// With benchmem on: // With benchmem on:
// //
// BenchmarkCidMap_CidStr-8 100000 15579 ns/op 11223 B/op 207 allocs/op // BenchmarkCidMap_CidStr-8 100000 15579 ns/op 11223 B/op 207 allocs/op
// BenchmarkCidMap_CidIface-8 100000 19500 ns/op 12824 B/op 307 allocs/op // BenchmarkCidMap_CidIface-8 100000 19500 ns/op 12824 B/op 307 allocs/op
// BenchmarkCidMap_StrPlusHax-8 200000 10451 ns/op 7589 B/op 202 allocs/op // BenchmarkCidMap_StrPlusHax-8 200000 10451 ns/op 7589 B/op 202 allocs/op
// //
// We can see here that the impact of interface boxing is significant: // We can see here that the impact of interface boxing is significant:
// it increases the time taken to do the inserts to 133%, largely because // it increases the time taken to do the inserts to 133%, largely because
@@ -36,6 +36,7 @@ import (
// re-arranges itself, it involves more or less an O(n) copy of the content // re-arranges itself, it involves more or less an O(n) copy of the content
// in addition to the alloc itself). This isn't topical to the question of // in addition to the alloc itself). This isn't topical to the question of
// whether or not interfaces are a good idea; just for contextualizing. // whether or not interfaces are a good idea; just for contextualizing.
//
func BenchmarkCidMap_CidStr(b *testing.B) { func BenchmarkCidMap_CidStr(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
mp := map[CidStr]int{} mp := map[CidStr]int{}

View File

@@ -114,7 +114,7 @@ func NewCidStr(version uint64, codecType uint64, mhash mh.Multihash) CidStr {
// //
// For CidV1, the data buffer is in the form: // For CidV1, the data buffer is in the form:
// //
// <version><codec-type><multihash> // <version><codec-type><multihash>
// //
// CidV0 are also supported. In particular, data buffers starting // CidV0 are also supported. In particular, data buffers starting
// with length 34 bytes, which starts with bytes [18,32...] are considered // with length 34 bytes, which starts with bytes [18,32...] are considered

View File

@@ -110,7 +110,7 @@ func (c CidStruct) Prefix() Prefix {
// //
// For CidV1, the data buffer is in the form: // For CidV1, the data buffer is in the form:
// //
// <version><codec-type><multihash> // <version><codec-type><multihash>
// //
// CidV0 are also supported. In particular, data buffers starting // CidV0 are also supported. In particular, data buffers starting
// with length 34 bytes, which starts with bytes [18,32...] are considered // with length 34 bytes, which starts with bytes [18,32...] are considered

View File

@@ -1,61 +0,0 @@
package cid_test
import (
"math/rand"
"testing"
"github.com/ipfs/go-cid"
"github.com/multiformats/go-multihash"
)
// BenchmarkIdentityCheck benchmarks two ways of checking whether a CIDv1 has multihash.IDENTITY
// code:
// 1. Cid.Prefix(), and
// 2. decoding the Cid.Hash().
//
// This benchmark illustrates that using Cid.Prefix is more efficient than multihash.Decode.
// Users wishing to perform such a check should use Cid.Prefix.
//
// Consider that `Cid.Prefix` is already efficient enough and introducing a dedicated API for
// performing this check will likely result in small gains.
func BenchmarkIdentityCheck(b *testing.B) {
rng := rand.New(rand.NewSource(1413))
data := make([]byte, rng.Intn(100)+1024)
if _, err := rng.Read(data); err != nil {
b.Fatal(err)
}
mh, err := multihash.Sum(data, multihash.IDENTITY, -1)
if err != nil {
b.Fatal(err)
}
cv1 := cid.NewCidV1(cid.Raw, mh)
b.SetBytes(int64(cv1.ByteLen()))
b.ReportAllocs()
b.ResetTimer()
b.Run("Prefix", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if cv1.Prefix().MhType != multihash.IDENTITY {
b.Fatal("expected IDENTITY CID")
}
}
})
})
b.Run("MultihashDecode", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
dmh, err := multihash.Decode(cv1.Hash())
if err != nil {
b.Fatal(err)
}
if dmh.Code != multihash.IDENTITY {
b.Fatal("expected IDENTITY CID")
}
}
})
})
}

227
cid.go
View File

@@ -10,7 +10,7 @@
// //
// A CIDv1 has four parts: // A CIDv1 has four parts:
// //
// <cidv1> ::= <multibase-prefix><cid-version><multicodec-packed-content-type><multihash-content-address> // <cidv1> ::= <multibase-prefix><cid-version><multicodec-packed-content-type><multihash-content-address>
// //
// As shown above, the CID implementation relies heavily on Multiformats, // As shown above, the CID implementation relies heavily on Multiformats,
// particularly Multibase // particularly Multibase
@@ -22,7 +22,6 @@ package cid
import ( import (
"bytes" "bytes"
"encoding" "encoding"
"encoding/binary"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@@ -37,53 +36,29 @@ import (
// UnsupportedVersionString just holds an error message // UnsupportedVersionString just holds an error message
const UnsupportedVersionString = "<unsupported cid version>" const UnsupportedVersionString = "<unsupported cid version>"
// ErrInvalidCid is an error that indicates that a CID is invalid.
type ErrInvalidCid struct {
Err error
}
func (e ErrInvalidCid) Error() string {
return fmt.Sprintf("invalid cid: %s", e.Err)
}
func (e ErrInvalidCid) Unwrap() error {
return e.Err
}
func (e ErrInvalidCid) Is(err error) bool {
switch err.(type) {
case ErrInvalidCid, *ErrInvalidCid:
return true
default:
return false
}
}
var ( var (
// ErrCidTooShort means that the cid passed to decode was not long // ErrCidTooShort means that the cid passed to decode was not long
// enough to be a valid Cid // enough to be a valid Cid
ErrCidTooShort = ErrInvalidCid{errors.New("cid too short")} ErrCidTooShort = errors.New("cid too short")
// ErrInvalidEncoding means that selected encoding is not supported // ErrInvalidEncoding means that selected encoding is not supported
// by this Cid version // by this Cid version
ErrInvalidEncoding = errors.New("invalid base encoding") ErrInvalidEncoding = errors.New("invalid base encoding")
) )
// Consts below are DEPRECATED and left only for legacy reasons: // These are multicodec-packed content types. The should match
// <https://github.com/ipfs/go-cid/pull/137> // the codes described in the authoritative document:
// Modern code should use consts from go-multicodec instead: // https://github.com/multiformats/multicodec/blob/master/table.csv
// <https://github.com/multiformats/go-multicodec>
const ( const (
// common ones Raw = 0x55
Raw = 0x55
DagProtobuf = 0x70 // https://ipld.io/docs/codecs/known/dag-pb/
DagCBOR = 0x71 // https://ipld.io/docs/codecs/known/dag-cbor/
DagJSON = 0x0129 // https://ipld.io/docs/codecs/known/dag-json/
Libp2pKey = 0x72 // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids
// other DagProtobuf = 0x70
GitRaw = 0x78 DagCBOR = 0x71
DagJOSE = 0x85 // https://ipld.io/specs/codecs/dag-jose/spec/ Libp2pKey = 0x72
GitRaw = 0x78
DagJOSE = 0x85
EthBlock = 0x90 EthBlock = 0x90
EthBlockList = 0x91 EthBlockList = 0x91
EthTxTrie = 0x92 EthTxTrie = 0x92
@@ -105,6 +80,64 @@ const (
FilCommitmentSealed = 0xf102 FilCommitmentSealed = 0xf102
) )
// Codecs maps the name of a codec to its type
var Codecs = map[string]uint64{
"v0": DagProtobuf,
"raw": Raw,
"protobuf": DagProtobuf,
"cbor": DagCBOR,
"libp2p-key": Libp2pKey,
"git-raw": GitRaw,
"eth-block": EthBlock,
"eth-block-list": EthBlockList,
"eth-tx-trie": EthTxTrie,
"eth-tx": EthTx,
"eth-tx-receipt-trie": EthTxReceiptTrie,
"eth-tx-receipt": EthTxReceipt,
"eth-state-trie": EthStateTrie,
"eth-account-snapshot": EthAccountSnapshot,
"eth-storage-trie": EthStorageTrie,
"bitcoin-block": BitcoinBlock,
"bitcoin-tx": BitcoinTx,
"zcash-block": ZcashBlock,
"zcash-tx": ZcashTx,
"decred-block": DecredBlock,
"decred-tx": DecredTx,
"dash-block": DashBlock,
"dash-tx": DashTx,
"fil-commitment-unsealed": FilCommitmentUnsealed,
"fil-commitment-sealed": FilCommitmentSealed,
"dag-jose": DagJOSE,
}
// CodecToStr maps the numeric codec to its name
var CodecToStr = map[uint64]string{
Raw: "raw",
DagProtobuf: "protobuf",
DagCBOR: "cbor",
GitRaw: "git-raw",
EthBlock: "eth-block",
EthBlockList: "eth-block-list",
EthTxTrie: "eth-tx-trie",
EthTx: "eth-tx",
EthTxReceiptTrie: "eth-tx-receipt-trie",
EthTxReceipt: "eth-tx-receipt",
EthStateTrie: "eth-state-trie",
EthAccountSnapshot: "eth-account-snapshot",
EthStorageTrie: "eth-storage-trie",
BitcoinBlock: "bitcoin-block",
BitcoinTx: "bitcoin-tx",
ZcashBlock: "zcash-block",
ZcashTx: "zcash-tx",
DecredBlock: "decred-block",
DecredTx: "decred-tx",
DashBlock: "dash-block",
DashTx: "dash-tx",
FilCommitmentUnsealed: "fil-commitment-unsealed",
FilCommitmentSealed: "fil-commitment-sealed",
DagJOSE: "dag-jose",
}
// tryNewCidV0 tries to convert a multihash into a CIDv0 CID and returns an // tryNewCidV0 tries to convert a multihash into a CIDv0 CID and returns an
// error on failure. // error on failure.
func tryNewCidV0(mhash mh.Multihash) (Cid, error) { func tryNewCidV0(mhash mh.Multihash) (Cid, error) {
@@ -112,10 +145,10 @@ func tryNewCidV0(mhash mh.Multihash) (Cid, error) {
// incorrectly detect it as CidV1 in the Version() method // incorrectly detect it as CidV1 in the Version() method
dec, err := mh.Decode(mhash) dec, err := mh.Decode(mhash)
if err != nil { if err != nil {
return Undef, ErrInvalidCid{err} return Undef, err
} }
if dec.Code != mh.SHA2_256 || dec.Length != 32 { if dec.Code != mh.SHA2_256 || dec.Length != 32 {
return Undef, ErrInvalidCid{fmt.Errorf("invalid hash for cidv0 %d-%d", dec.Code, dec.Length)} return Undef, fmt.Errorf("invalid hash for cidv0 %d-%d", dec.Code, dec.Length)
} }
return Cid{string(mhash)}, nil return Cid{string(mhash)}, nil
} }
@@ -140,24 +173,16 @@ func NewCidV0(mhash mh.Multihash) Cid {
// Panics if the multihash is invalid. // Panics if the multihash is invalid.
func NewCidV1(codecType uint64, mhash mh.Multihash) Cid { func NewCidV1(codecType uint64, mhash mh.Multihash) Cid {
hashlen := len(mhash) hashlen := len(mhash)
// two 8 bytes (max) numbers plus hash
// Two 8 bytes (max) numbers plus hash. buf := make([]byte, 1+varint.UvarintSize(codecType)+hashlen)
// We use strings.Builder to only allocate once. n := varint.PutUvarint(buf, 1)
var b strings.Builder n += varint.PutUvarint(buf[n:], codecType)
b.Grow(1 + varint.UvarintSize(codecType) + hashlen) cn := copy(buf[n:], mhash)
b.WriteByte(1)
var buf [binary.MaxVarintLen64]byte
n := varint.PutUvarint(buf[:], codecType)
b.Write(buf[:n])
cn, _ := b.Write(mhash)
if cn != hashlen { if cn != hashlen {
panic("copy hash length is inconsistent") panic("copy hash length is inconsistent")
} }
return Cid{b.String()} return Cid{string(buf[:n+hashlen])}
} }
var ( var (
@@ -199,23 +224,14 @@ func Parse(v interface{}) (Cid, error) {
case Cid: case Cid:
return v2, nil return v2, nil
default: default:
return Undef, ErrInvalidCid{fmt.Errorf("can't parse %+v as Cid", v2)} return Undef, fmt.Errorf("can't parse %+v as Cid", v2)
} }
} }
// MustParse calls Parse but will panic on error.
func MustParse(v interface{}) Cid {
c, err := Parse(v)
if err != nil {
panic(err)
}
return c
}
// Decode parses a Cid-encoded string and returns a Cid object. // Decode parses a Cid-encoded string and returns a Cid object.
// For CidV1, a Cid-encoded string is primarily a multibase string: // For CidV1, a Cid-encoded string is primarily a multibase string:
// //
// <multibase-type-code><base-encoded-string> // <multibase-type-code><base-encoded-string>
// //
// The base-encoded string represents a: // The base-encoded string represents a:
// //
@@ -232,7 +248,7 @@ func Decode(v string) (Cid, error) {
if len(v) == 46 && v[:2] == "Qm" { if len(v) == 46 && v[:2] == "Qm" {
hash, err := mh.FromB58String(v) hash, err := mh.FromB58String(v)
if err != nil { if err != nil {
return Undef, ErrInvalidCid{err} return Undef, err
} }
return tryNewCidV0(hash) return tryNewCidV0(hash)
@@ -240,7 +256,7 @@ func Decode(v string) (Cid, error) {
_, data, err := mbase.Decode(v) _, data, err := mbase.Decode(v)
if err != nil { if err != nil {
return Undef, ErrInvalidCid{err} return Undef, err
} }
return Cast(data) return Cast(data)
@@ -262,7 +278,7 @@ func ExtractEncoding(v string) (mbase.Encoding, error) {
// check encoding is valid // check encoding is valid
_, err := mbase.NewEncoder(encoding) _, err := mbase.NewEncoder(encoding)
if err != nil { if err != nil {
return -1, ErrInvalidCid{err} return -1, err
} }
return encoding, nil return encoding, nil
@@ -271,7 +287,7 @@ func ExtractEncoding(v string) (mbase.Encoding, error) {
// Cast takes a Cid data slice, parses it and returns a Cid. // Cast takes a Cid data slice, parses it and returns a Cid.
// For CidV1, the data buffer is in the form: // For CidV1, the data buffer is in the form:
// //
// <version><codec-type><multihash> // <version><codec-type><multihash>
// //
// CidV0 are also supported. In particular, data buffers starting // CidV0 are also supported. In particular, data buffers starting
// with length 34 bytes, which starts with bytes [18,32...] are considered // with length 34 bytes, which starts with bytes [18,32...] are considered
@@ -282,11 +298,11 @@ func ExtractEncoding(v string) (mbase.Encoding, error) {
func Cast(data []byte) (Cid, error) { func Cast(data []byte) (Cid, error) {
nr, c, err := CidFromBytes(data) nr, c, err := CidFromBytes(data)
if err != nil { if err != nil {
return Undef, ErrInvalidCid{err} return Undef, err
} }
if nr != len(data) { if nr != len(data) {
return Undef, ErrInvalidCid{fmt.Errorf("trailing bytes in data buffer passed to cid Cast")} return Undef, fmt.Errorf("trailing bytes in data buffer passed to cid Cast")
} }
return c, nil return c, nil
@@ -400,13 +416,7 @@ func (c Cid) Hash() mh.Multihash {
// Bytes returns the byte representation of a Cid. // Bytes returns the byte representation of a Cid.
// The output of bytes can be parsed back into a Cid // The output of bytes can be parsed back into a Cid
// with Cast(). // with Cast().
//
// If c.Defined() == false, it return a nil slice and may not
// be parsable with Cast().
func (c Cid) Bytes() []byte { func (c Cid) Bytes() []byte {
if !c.Defined() {
return nil
}
return []byte(c.str) return []byte(c.str)
} }
@@ -456,7 +466,7 @@ func (c Cid) Equals(o Cid) bool {
// UnmarshalJSON parses the JSON representation of a Cid. // UnmarshalJSON parses the JSON representation of a Cid.
func (c *Cid) UnmarshalJSON(b []byte) error { func (c *Cid) UnmarshalJSON(b []byte) error {
if len(b) < 2 { if len(b) < 2 {
return ErrInvalidCid{fmt.Errorf("invalid cid json blob")} return fmt.Errorf("invalid cid json blob")
} }
obj := struct { obj := struct {
CidTarget string `json:"/"` CidTarget string `json:"/"`
@@ -464,7 +474,7 @@ func (c *Cid) UnmarshalJSON(b []byte) error {
objptr := &obj objptr := &obj
err := json.Unmarshal(b, &objptr) err := json.Unmarshal(b, &objptr)
if err != nil { if err != nil {
return ErrInvalidCid{err} return err
} }
if objptr == nil { if objptr == nil {
*c = Cid{} *c = Cid{}
@@ -472,12 +482,12 @@ func (c *Cid) UnmarshalJSON(b []byte) error {
} }
if obj.CidTarget == "" { if obj.CidTarget == "" {
return ErrInvalidCid{fmt.Errorf("cid was incorrectly formatted")} return fmt.Errorf("cid was incorrectly formatted")
} }
out, err := Decode(obj.CidTarget) out, err := Decode(obj.CidTarget)
if err != nil { if err != nil {
return ErrInvalidCid{err} return err
} }
*c = out *c = out
@@ -487,7 +497,7 @@ func (c *Cid) UnmarshalJSON(b []byte) error {
// MarshalJSON procudes a JSON representation of a Cid, which looks as follows: // MarshalJSON procudes a JSON representation of a Cid, which looks as follows:
// //
// { "/": "<cid-string>" } // { "/": "<cid-string>" }
// //
// Note that this formatting comes from the IPLD specification // Note that this formatting comes from the IPLD specification
// (https://github.com/ipld/specs/tree/master/ipld) // (https://github.com/ipld/specs/tree/master/ipld)
@@ -544,8 +554,7 @@ func (c Cid) Prefix() Prefix {
// and the Multihash length. It does not contains // and the Multihash length. It does not contains
// any actual content information. // any actual content information.
// NOTE: The use -1 in MhLength to mean default length is deprecated, // NOTE: The use -1 in MhLength to mean default length is deprecated,
// // use the V0Builder or V1Builder structures instead
// use the V0Builder or V1Builder structures instead
type Prefix struct { type Prefix struct {
Version uint64 Version uint64
Codec uint64 Codec uint64
@@ -564,12 +573,12 @@ func (p Prefix) Sum(data []byte) (Cid, error) {
if p.Version == 0 && (p.MhType != mh.SHA2_256 || if p.Version == 0 && (p.MhType != mh.SHA2_256 ||
(p.MhLength != 32 && p.MhLength != -1)) { (p.MhLength != 32 && p.MhLength != -1)) {
return Undef, ErrInvalidCid{fmt.Errorf("invalid v0 prefix")} return Undef, fmt.Errorf("invalid v0 prefix")
} }
hash, err := mh.Sum(data, p.MhType, length) hash, err := mh.Sum(data, p.MhType, length)
if err != nil { if err != nil {
return Undef, ErrInvalidCid{err} return Undef, err
} }
switch p.Version { switch p.Version {
@@ -578,13 +587,13 @@ func (p Prefix) Sum(data []byte) (Cid, error) {
case 1: case 1:
return NewCidV1(p.Codec, hash), nil return NewCidV1(p.Codec, hash), nil
default: default:
return Undef, ErrInvalidCid{fmt.Errorf("invalid cid version")} return Undef, fmt.Errorf("invalid cid version")
} }
} }
// Bytes returns a byte representation of a Prefix. It looks like: // Bytes returns a byte representation of a Prefix. It looks like:
// //
// <version><codec><mh-type><mh-length> // <version><codec><mh-type><mh-length>
func (p Prefix) Bytes() []byte { func (p Prefix) Bytes() []byte {
size := varint.UvarintSize(p.Version) size := varint.UvarintSize(p.Version)
size += varint.UvarintSize(p.Codec) size += varint.UvarintSize(p.Codec)
@@ -608,22 +617,22 @@ func PrefixFromBytes(buf []byte) (Prefix, error) {
r := bytes.NewReader(buf) r := bytes.NewReader(buf)
vers, err := varint.ReadUvarint(r) vers, err := varint.ReadUvarint(r)
if err != nil { if err != nil {
return Prefix{}, ErrInvalidCid{err} return Prefix{}, err
} }
codec, err := varint.ReadUvarint(r) codec, err := varint.ReadUvarint(r)
if err != nil { if err != nil {
return Prefix{}, ErrInvalidCid{err} return Prefix{}, err
} }
mhtype, err := varint.ReadUvarint(r) mhtype, err := varint.ReadUvarint(r)
if err != nil { if err != nil {
return Prefix{}, ErrInvalidCid{err} return Prefix{}, err
} }
mhlen, err := varint.ReadUvarint(r) mhlen, err := varint.ReadUvarint(r)
if err != nil { if err != nil {
return Prefix{}, ErrInvalidCid{err} return Prefix{}, err
} }
return Prefix{ return Prefix{
@@ -637,12 +646,12 @@ func PrefixFromBytes(buf []byte) (Prefix, error) {
func CidFromBytes(data []byte) (int, Cid, error) { func CidFromBytes(data []byte) (int, Cid, error) {
if len(data) > 2 && data[0] == mh.SHA2_256 && data[1] == 32 { if len(data) > 2 && data[0] == mh.SHA2_256 && data[1] == 32 {
if len(data) < 34 { if len(data) < 34 {
return 0, Undef, ErrInvalidCid{fmt.Errorf("not enough bytes for cid v0")} return 0, Undef, fmt.Errorf("not enough bytes for cid v0")
} }
h, err := mh.Cast(data[:34]) h, err := mh.Cast(data[:34])
if err != nil { if err != nil {
return 0, Undef, ErrInvalidCid{err} return 0, Undef, err
} }
return 34, Cid{string(h)}, nil return 34, Cid{string(h)}, nil
@@ -650,21 +659,21 @@ func CidFromBytes(data []byte) (int, Cid, error) {
vers, n, err := varint.FromUvarint(data) vers, n, err := varint.FromUvarint(data)
if err != nil { if err != nil {
return 0, Undef, ErrInvalidCid{err} return 0, Undef, err
} }
if vers != 1 { if vers != 1 {
return 0, Undef, ErrInvalidCid{fmt.Errorf("expected 1 as the cid version number, got: %d", vers)} return 0, Undef, fmt.Errorf("expected 1 as the cid version number, got: %d", vers)
} }
_, cn, err := varint.FromUvarint(data[n:]) _, cn, err := varint.FromUvarint(data[n:])
if err != nil { if err != nil {
return 0, Undef, ErrInvalidCid{err} return 0, Undef, err
} }
mhnr, _, err := mh.MHFromBytes(data[n+cn:]) mhnr, _, err := mh.MHFromBytes(data[n+cn:])
if err != nil { if err != nil {
return 0, Undef, ErrInvalidCid{err} return 0, Undef, err
} }
l := n + cn + mhnr l := n + cn + mhnr
@@ -727,32 +736,32 @@ func CidFromReader(r io.Reader) (int, Cid, error) {
// The varint package wants a io.ByteReader, so we must wrap our io.Reader. // The varint package wants a io.ByteReader, so we must wrap our io.Reader.
vers, err := varint.ReadUvarint(br) vers, err := varint.ReadUvarint(br)
if err != nil { if err != nil {
return len(br.dst), Undef, ErrInvalidCid{err} return len(br.dst), Undef, err
} }
// If we have a CIDv0, read the rest of the bytes and cast the buffer. // If we have a CIDv0, read the rest of the bytes and cast the buffer.
if vers == mh.SHA2_256 { if vers == mh.SHA2_256 {
if n, err := io.ReadFull(r, br.dst[1:34]); err != nil { if n, err := io.ReadFull(r, br.dst[1:34]); err != nil {
return len(br.dst) + n, Undef, ErrInvalidCid{err} return len(br.dst) + n, Undef, err
} }
br.dst = br.dst[:34] br.dst = br.dst[:34]
h, err := mh.Cast(br.dst) h, err := mh.Cast(br.dst)
if err != nil { if err != nil {
return len(br.dst), Undef, ErrInvalidCid{err} return len(br.dst), Undef, err
} }
return len(br.dst), Cid{string(h)}, nil return len(br.dst), Cid{string(h)}, nil
} }
if vers != 1 { if vers != 1 {
return len(br.dst), Undef, ErrInvalidCid{fmt.Errorf("expected 1 as the cid version number, got: %d", vers)} return len(br.dst), Undef, fmt.Errorf("expected 1 as the cid version number, got: %d", vers)
} }
// CID block encoding multicodec. // CID block encoding multicodec.
_, err = varint.ReadUvarint(br) _, err = varint.ReadUvarint(br)
if err != nil { if err != nil {
return len(br.dst), Undef, ErrInvalidCid{err} return len(br.dst), Undef, err
} }
// We could replace most of the code below with go-multihash's ReadMultihash. // We could replace most of the code below with go-multihash's ReadMultihash.
@@ -763,19 +772,19 @@ func CidFromReader(r io.Reader) (int, Cid, error) {
// Multihash hash function code. // Multihash hash function code.
_, err = varint.ReadUvarint(br) _, err = varint.ReadUvarint(br)
if err != nil { if err != nil {
return len(br.dst), Undef, ErrInvalidCid{err} return len(br.dst), Undef, err
} }
// Multihash digest length. // Multihash digest length.
mhl, err := varint.ReadUvarint(br) mhl, err := varint.ReadUvarint(br)
if err != nil { if err != nil {
return len(br.dst), Undef, ErrInvalidCid{err} return len(br.dst), Undef, err
} }
// Refuse to make large allocations to prevent OOMs due to bugs. // Refuse to make large allocations to prevent OOMs due to bugs.
const maxDigestAlloc = 32 << 20 // 32MiB const maxDigestAlloc = 32 << 20 // 32MiB
if mhl > maxDigestAlloc { if mhl > maxDigestAlloc {
return len(br.dst), Undef, ErrInvalidCid{fmt.Errorf("refusing to allocate %d bytes for a digest", mhl)} return len(br.dst), Undef, fmt.Errorf("refusing to allocate %d bytes for a digest", mhl)
} }
// Fine to convert mhl to int, given maxDigestAlloc. // Fine to convert mhl to int, given maxDigestAlloc.
@@ -784,7 +793,7 @@ func CidFromReader(r io.Reader) (int, Cid, error) {
if cidLength > cap(br.dst) { if cidLength > cap(br.dst) {
// If the multihash digest doesn't fit in our initial 64 bytes, // If the multihash digest doesn't fit in our initial 64 bytes,
// efficiently extend the slice via append+make. // efficiently extend the slice via append+make.
br.dst = append(br.dst, make([]byte, cidLength-len(br.dst))...) br.dst = append(br.dst, make([]byte, cidLength-cap(br.dst))...)
} else { } else {
// The multihash digest fits inside our buffer, // The multihash digest fits inside our buffer,
// so just extend its capacity. // so just extend its capacity.
@@ -794,7 +803,7 @@ func CidFromReader(r io.Reader) (int, Cid, error) {
if n, err := io.ReadFull(r, br.dst[prefixLength:cidLength]); err != nil { if n, err := io.ReadFull(r, br.dst[prefixLength:cidLength]); err != nil {
// We can't use len(br.dst) here, // We can't use len(br.dst) here,
// as we've only read n bytes past prefixLength. // as we've only read n bytes past prefixLength.
return prefixLength + n, Undef, ErrInvalidCid{err} return prefixLength + n, Undef, err
} }
// This simply ensures the multihash is valid. // This simply ensures the multihash is valid.
@@ -802,7 +811,7 @@ func CidFromReader(r io.Reader) (int, Cid, error) {
// for now, it helps ensure consistency with CidFromBytes. // for now, it helps ensure consistency with CidFromBytes.
_, _, err = mh.MHFromBytes(br.dst[mhStart:]) _, _, err = mh.MHFromBytes(br.dst[mhStart:])
if err != nil { if err != nil {
return len(br.dst), Undef, ErrInvalidCid{err} return len(br.dst), Undef, err
} }
return len(br.dst), Cid{string(br.dst)}, nil return len(br.dst), Cid{string(br.dst)}, nil

View File

@@ -1,4 +1,4 @@
//go:build gofuzz // +build gofuzz
package cid package cid

View File

@@ -2,9 +2,7 @@ package cid
import ( import (
"bytes" "bytes"
crand "crypto/rand"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"math/rand" "math/rand"
@@ -17,6 +15,37 @@ import (
mh "github.com/multiformats/go-multihash" mh "github.com/multiformats/go-multihash"
) )
// Copying the "silly test" idea from
// https://github.com/multiformats/go-multihash/blob/7aa9f26a231c6f34f4e9fad52bf580fd36627285/multihash_test.go#L13
// Makes it so changing the table accidentally has to happen twice.
var tCodecs = map[uint64]string{
Raw: "raw",
DagProtobuf: "protobuf",
DagCBOR: "cbor",
Libp2pKey: "libp2p-key",
GitRaw: "git-raw",
EthBlock: "eth-block",
EthBlockList: "eth-block-list",
EthTxTrie: "eth-tx-trie",
EthTx: "eth-tx",
EthTxReceiptTrie: "eth-tx-receipt-trie",
EthTxReceipt: "eth-tx-receipt",
EthStateTrie: "eth-state-trie",
EthAccountSnapshot: "eth-account-snapshot",
EthStorageTrie: "eth-storage-trie",
BitcoinBlock: "bitcoin-block",
BitcoinTx: "bitcoin-tx",
ZcashBlock: "zcash-block",
ZcashTx: "zcash-tx",
DecredBlock: "decred-block",
DecredTx: "decred-tx",
DashBlock: "dash-block",
DashTx: "dash-tx",
FilCommitmentUnsealed: "fil-commitment-unsealed",
FilCommitmentSealed: "fil-commitment-sealed",
DagJOSE: "dag-jose",
}
func assertEqual(t *testing.T, a, b Cid) { func assertEqual(t *testing.T, a, b Cid) {
if a.Type() != b.Type() { if a.Type() != b.Type() {
t.Fatal("mismatch on type") t.Fatal("mismatch on type")
@@ -31,6 +60,26 @@ func assertEqual(t *testing.T, a, b Cid) {
} }
} }
func TestTable(t *testing.T) {
if len(tCodecs) != len(Codecs)-1 {
t.Errorf("Item count mismatch in the Table of Codec. Should be %d, got %d", len(tCodecs)+1, len(Codecs))
}
for k, v := range tCodecs {
if Codecs[v] != k {
t.Errorf("Table mismatch: 0x%x %s", k, v)
}
}
}
// The table returns cid.DagProtobuf for "v0"
// so we test it apart
func TestTableForV0(t *testing.T) {
if Codecs["v0"] != DagProtobuf {
t.Error("Table mismatch: Codecs[\"v0\"] should resolve to DagProtobuf (0x70)")
}
}
func TestPrefixSum(t *testing.T) { func TestPrefixSum(t *testing.T) {
// Test creating CIDs both manually and with Prefix. // Test creating CIDs both manually and with Prefix.
// Tests: https://github.com/ipfs/go-cid/issues/83 // Tests: https://github.com/ipfs/go-cid/issues/83
@@ -163,9 +212,6 @@ func TestBasesMarshaling(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("expected too-short error from ExtractEncoding") t.Fatal("expected too-short error from ExtractEncoding")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
if ee != -1 { if ee != -1 {
t.Fatal("expected -1 from too-short ExtractEncoding") t.Fatal("expected -1 from too-short ExtractEncoding")
} }
@@ -231,9 +277,6 @@ func TestEmptyString(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("shouldnt be able to parse an empty cid") t.Fatal("shouldnt be able to parse an empty cid")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("error must be ErrInvalidCid")
}
} }
func TestV0Handling(t *testing.T) { func TestV0Handling(t *testing.T) {
@@ -289,9 +332,6 @@ func TestV0ErrorCases(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("should have failed to decode that ref") t.Fatal("should have failed to decode that ref")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("error must be ErrInvalidCid")
}
} }
func TestNewPrefixV1(t *testing.T) { func TestNewPrefixV1(t *testing.T) {
@@ -382,9 +422,6 @@ func TestInvalidV0Prefix(t *testing.T) {
if err == nil { if err == nil {
t.Fatalf("should error (index %d)", i) t.Fatalf("should error (index %d)", i)
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
} }
} }
@@ -394,9 +431,6 @@ func TestBadPrefix(t *testing.T) {
if err == nil { if err == nil {
t.Fatalf("expected error on v3 prefix Sum") t.Fatalf("expected error on v3 prefix Sum")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
} }
func TestPrefixRoundtrip(t *testing.T) { func TestPrefixRoundtrip(t *testing.T) {
@@ -433,30 +467,18 @@ func TestBadPrefixFromBytes(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("expected error for bad byte 0") t.Fatal("expected error for bad byte 0")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
_, err = PrefixFromBytes([]byte{0x01, 0x80}) _, err = PrefixFromBytes([]byte{0x01, 0x80})
if err == nil { if err == nil {
t.Fatal("expected error for bad byte 1") t.Fatal("expected error for bad byte 1")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
_, err = PrefixFromBytes([]byte{0x01, 0x01, 0x80}) _, err = PrefixFromBytes([]byte{0x01, 0x01, 0x80})
if err == nil { if err == nil {
t.Fatal("expected error for bad byte 2") t.Fatal("expected error for bad byte 2")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
_, err = PrefixFromBytes([]byte{0x01, 0x01, 0x01, 0x80}) _, err = PrefixFromBytes([]byte{0x01, 0x01, 0x01, 0x80})
if err == nil { if err == nil {
t.Fatal("expected error for bad byte 3") t.Fatal("expected error for bad byte 3")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
} }
func Test16BytesVarint(t *testing.T) { func Test16BytesVarint(t *testing.T) {
@@ -470,7 +492,7 @@ func TestFuzzCid(t *testing.T) {
buf := make([]byte, 128) buf := make([]byte, 128)
for i := 0; i < 200; i++ { for i := 0; i < 200; i++ {
s := rand.Intn(128) s := rand.Intn(128)
crand.Read(buf[:s]) rand.Read(buf[:s])
_, _ = Cast(buf[:s]) _, _ = Cast(buf[:s])
} }
} }
@@ -483,9 +505,6 @@ func TestParse(t *testing.T) {
if !strings.Contains(err.Error(), "can't parse 123 as Cid") { if !strings.Contains(err.Error(), "can't parse 123 as Cid") {
t.Fatalf("expected int error, got %s", err.Error()) t.Fatalf("expected int error, got %s", err.Error())
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatalf("expected ErrInvalidCid, got %s", err.Error())
}
theHash := "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" theHash := "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n"
h, err := mh.FromB58String(theHash) h, err := mh.FromB58String(theHash)
@@ -603,29 +622,17 @@ func TestJsonRoundTrip(t *testing.T) {
t.Fatal("cids not equal for Cid") t.Fatal("cids not equal for Cid")
} }
err = actual2.UnmarshalJSON([]byte("1")) if err = actual2.UnmarshalJSON([]byte("1")); err == nil {
if err == nil {
t.Fatal("expected error for too-short JSON") t.Fatal("expected error for too-short JSON")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
err = actual2.UnmarshalJSON([]byte(`{"nope":"nope"}`)) if err = actual2.UnmarshalJSON([]byte(`{"nope":"nope"}`)); err == nil {
if err == nil {
t.Fatal("expected error for bad CID JSON") t.Fatal("expected error for bad CID JSON")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
err = actual2.UnmarshalJSON([]byte(`bad "" json!`)) if err = actual2.UnmarshalJSON([]byte(`bad "" json!`)); err == nil {
if err == nil {
t.Fatal("expected error for bad JSON") t.Fatal("expected error for bad JSON")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("expected error to be ErrInvalidCid")
}
var actual3 Cid var actual3 Cid
enc, err = actual3.MarshalJSON() enc, err = actual3.MarshalJSON()
@@ -660,7 +667,6 @@ func TestReadCidsFromBuffer(t *testing.T) {
"k2cwueckqkibutvhkr4p2ln2pjcaxaakpd9db0e7j7ax1lxhhxy3ekpv", "k2cwueckqkibutvhkr4p2ln2pjcaxaakpd9db0e7j7ax1lxhhxy3ekpv",
"Qmf5Qzp6nGBku7CEn2UQx4mgN8TW69YUok36DrGa6NN893", "Qmf5Qzp6nGBku7CEn2UQx4mgN8TW69YUok36DrGa6NN893",
"zb2rhZi1JR4eNc2jBGaRYJKYM8JEB4ovenym8L1CmFsRAytkz", "zb2rhZi1JR4eNc2jBGaRYJKYM8JEB4ovenym8L1CmFsRAytkz",
"bafkqarjpmzuwyzltorxxezjpkvcfgqkfjfbfcvslivje2vchkzdu6rckjjcfgtkolaze6mssjqzeyn2ekrcfatkjku2vowseky3fswkfkm2deqkrju3e2",
} }
var cids []Cid var cids []Cid
@@ -792,9 +798,6 @@ func TestBadParse(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("expected to fail to parse an invalid CIDv1 CID") t.Fatal("expected to fail to parse an invalid CIDv1 CID")
} }
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("error must be ErrInvalidCid")
}
} }
func TestLoggable(t *testing.T) { func TestLoggable(t *testing.T) {
@@ -809,80 +812,3 @@ func TestLoggable(t *testing.T) {
t.Fatalf("did not get expected loggable form (got %v)", actual) t.Fatalf("did not get expected loggable form (got %v)", actual)
} }
} }
func TestErrInvalidCidIs(t *testing.T) {
for i, test := range []struct {
err error
target error
}{
{&ErrInvalidCid{}, ErrInvalidCid{}},
{ErrInvalidCid{}, &ErrInvalidCid{}},
{ErrInvalidCid{}, ErrInvalidCid{}},
{&ErrInvalidCid{}, &ErrInvalidCid{}},
} {
if !errors.Is(test.err, test.target) {
t.Fatalf("expected error to be ErrInvalidCid, case %d", i)
}
}
}
func TestErrInvalidCid(t *testing.T) {
run := func(err error) {
if err == nil {
t.Fatal("expected error")
}
if !strings.HasPrefix(err.Error(), "invalid cid: ") {
t.Fatal(`expected error message to contain "invalid cid: "`)
}
is := errors.Is(err, ErrInvalidCid{})
if !is {
t.Fatal("expected error to be ErrInvalidCid")
}
if !errors.Is(err, &ErrInvalidCid{}) {
t.Fatal("expected error to be &ErrInvalidCid")
}
}
_, err := Decode("")
run(err)
_, err = Decode("not-a-cid")
run(err)
_, err = Decode("bafyInvalid")
run(err)
_, err = Decode("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zIII")
run(err)
_, err = Cast([]byte("invalid"))
run(err)
_, err = Parse("not-a-cid")
run(err)
_, err = Parse("bafyInvalid")
run(err)
_, err = Parse("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zIII")
run(err)
_, err = Parse(123)
run(err)
_, _, err = CidFromBytes([]byte("invalid"))
run(err)
_, err = Prefix{}.Sum([]byte("data"))
run(err)
_, err = PrefixFromBytes([]byte{0x80})
run(err)
_, err = ExtractEncoding("invalid ")
run(err)
}

14
go.mod
View File

@@ -4,17 +4,7 @@ require (
github.com/multiformats/go-multibase v0.0.3 github.com/multiformats/go-multibase v0.0.3
github.com/multiformats/go-multihash v0.0.15 github.com/multiformats/go-multihash v0.0.15
github.com/multiformats/go-varint v0.0.6 github.com/multiformats/go-varint v0.0.6
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect
) )
require ( go 1.15
github.com/klauspost/cpuid/v2 v2.0.4 // indirect
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.0.3 // indirect
github.com/multiformats/go-base36 v0.1.0 // indirect
golang.org/x/crypto v0.1.0 // indirect
golang.org/x/sys v0.1.0 // indirect
)
go 1.19

12
go.sum
View File

@@ -19,13 +19,17 @@ github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@@ -1,3 +0,0 @@
{
"version": "v0.4.0"
}