Compare commits

...

11 Commits

Author SHA1 Message Date
Steven Allen
c0ab85119f feat: add non-dag codec variants (json/cbor)
We now CBOR, at least, in Filecoin when we want structured data that
can't have links.
2023-04-17 11:25:22 -07:00
dependabot[bot]
829c826f6b chore(deps): bump github.com/multiformats/go-varint from 0.0.6 to 0.0.7
Bumps [github.com/multiformats/go-varint](https://github.com/multiformats/go-varint) from 0.0.6 to 0.0.7.
- [Release notes](https://github.com/multiformats/go-varint/releases)
- [Commits](https://github.com/multiformats/go-varint/compare/v0.0.6...v0.0.7)

---
updated-dependencies:
- dependency-name: github.com/multiformats/go-varint
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-05 13:13:25 +10:00
dependabot[bot]
8bb6c55e54 chore(deps): bump github.com/multiformats/go-multihash
Bumps [github.com/multiformats/go-multihash](https://github.com/multiformats/go-multihash) from 0.0.15 to 0.2.1.
- [Release notes](https://github.com/multiformats/go-multihash/releases)
- [Commits](https://github.com/multiformats/go-multihash/compare/v0.0.15...v0.2.1)

---
updated-dependencies:
- dependency-name: github.com/multiformats/go-multihash
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-05 12:53:59 +10:00
dependabot[bot]
72c7f21dcd chore(deps): bump github.com/multiformats/go-multibase
Bumps [github.com/multiformats/go-multibase](https://github.com/multiformats/go-multibase) from 0.0.3 to 0.2.0.
- [Release notes](https://github.com/multiformats/go-multibase/releases)
- [Commits](https://github.com/multiformats/go-multibase/compare/v0.0.3...v0.2.0)

---
updated-dependencies:
- dependency-name: github.com/multiformats/go-multibase
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-05 12:50:34 +10:00
dependabot-preview[bot]
93a8da769f Upgrade to GitHub-native Dependabot 2023-04-04 14:13:57 +10:00
Rod Vagg
d46e7f2866 v0.4.1 2023-04-04 14:08:15 +10:00
gammazero
83a0e939a4 Add unit test for unexpected eof 2023-04-04 14:08:15 +10:00
Andrew Gillis
0981f8566c Update cid.go
Co-authored-by: Rod Vagg <rod@vagg.org>
2023-04-04 14:08:15 +10:00
gammazero
166a3a6880 CidFromReader should not wrap valid EOF return.
When reading from an io.Reader that has no data, the io.EOF error should not be wrapped in ErrInvalidCid. This is not an invalid CID, and is not the same as a partial read which is indicated by io.ErrUnexpectedEOF.

This fix is needed because existing code that uses CidFromReader may check for the end of an input stream by `if err == io.EOF` instead of the preferred `if errors.Is(err, io.EOF)`, and that code break at runtime after upgrading to go-cid v0.4.0.
2023-04-04 14:08:15 +10:00
Henrique Dias
8098d66787 chore: version 0.4.0 2023-03-20 09:29:34 +01:00
Henrique Dias
b98e249130 feat: wrap parsing errors into ErrInvalidCid 2023-03-20 09:29:34 +01:00
6 changed files with 249 additions and 65 deletions

8
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: gomod
directory: "/"
schedule:
interval: weekly
time: "11:00"
open-pull-requests-limit: 10

106
cid.go
View File

@@ -37,10 +37,32 @@ 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 = errors.New("cid too short") ErrCidTooShort = ErrInvalidCid{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
@@ -59,6 +81,10 @@ const (
DagJSON = 0x0129 // https://ipld.io/docs/codecs/known/dag-json/ 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 Libp2pKey = 0x72 // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids
// Non "dag" variants of common codecs
CBOR = 0x51 // Regular (arbitrary) CBOR.
JSON = 0x0200 // Regular (arbitrary) JSON.
// other // other
GitRaw = 0x78 GitRaw = 0x78
DagJOSE = 0x85 // https://ipld.io/specs/codecs/dag-jose/spec/ DagJOSE = 0x85 // https://ipld.io/specs/codecs/dag-jose/spec/
@@ -90,10 +116,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, err return Undef, ErrInvalidCid{err}
} }
if dec.Code != mh.SHA2_256 || dec.Length != 32 { if dec.Code != mh.SHA2_256 || dec.Length != 32 {
return Undef, fmt.Errorf("invalid hash for cidv0 %d-%d", dec.Code, dec.Length) return Undef, ErrInvalidCid{fmt.Errorf("invalid hash for cidv0 %d-%d", dec.Code, dec.Length)}
} }
return Cid{string(mhash)}, nil return Cid{string(mhash)}, nil
} }
@@ -177,7 +203,7 @@ func Parse(v interface{}) (Cid, error) {
case Cid: case Cid:
return v2, nil return v2, nil
default: default:
return Undef, fmt.Errorf("can't parse %+v as Cid", v2) return Undef, ErrInvalidCid{fmt.Errorf("can't parse %+v as Cid", v2)}
} }
} }
@@ -210,7 +236,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, err return Undef, ErrInvalidCid{err}
} }
return tryNewCidV0(hash) return tryNewCidV0(hash)
@@ -218,7 +244,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, err return Undef, ErrInvalidCid{err}
} }
return Cast(data) return Cast(data)
@@ -240,7 +266,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, err return -1, ErrInvalidCid{err}
} }
return encoding, nil return encoding, nil
@@ -260,11 +286,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, err return Undef, ErrInvalidCid{err}
} }
if nr != len(data) { if nr != len(data) {
return Undef, fmt.Errorf("trailing bytes in data buffer passed to cid Cast") return Undef, ErrInvalidCid{fmt.Errorf("trailing bytes in data buffer passed to cid Cast")}
} }
return c, nil return c, nil
@@ -434,7 +460,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 fmt.Errorf("invalid cid json blob") return ErrInvalidCid{fmt.Errorf("invalid cid json blob")}
} }
obj := struct { obj := struct {
CidTarget string `json:"/"` CidTarget string `json:"/"`
@@ -442,7 +468,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 err return ErrInvalidCid{err}
} }
if objptr == nil { if objptr == nil {
*c = Cid{} *c = Cid{}
@@ -450,12 +476,12 @@ func (c *Cid) UnmarshalJSON(b []byte) error {
} }
if obj.CidTarget == "" { if obj.CidTarget == "" {
return fmt.Errorf("cid was incorrectly formatted") return ErrInvalidCid{fmt.Errorf("cid was incorrectly formatted")}
} }
out, err := Decode(obj.CidTarget) out, err := Decode(obj.CidTarget)
if err != nil { if err != nil {
return err return ErrInvalidCid{err}
} }
*c = out *c = out
@@ -542,12 +568,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, fmt.Errorf("invalid v0 prefix") return Undef, ErrInvalidCid{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, err return Undef, ErrInvalidCid{err}
} }
switch p.Version { switch p.Version {
@@ -556,7 +582,7 @@ 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, fmt.Errorf("invalid cid version") return Undef, ErrInvalidCid{fmt.Errorf("invalid cid version")}
} }
} }
@@ -586,22 +612,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{}, err return Prefix{}, ErrInvalidCid{err}
} }
codec, err := varint.ReadUvarint(r) codec, err := varint.ReadUvarint(r)
if err != nil { if err != nil {
return Prefix{}, err return Prefix{}, ErrInvalidCid{err}
} }
mhtype, err := varint.ReadUvarint(r) mhtype, err := varint.ReadUvarint(r)
if err != nil { if err != nil {
return Prefix{}, err return Prefix{}, ErrInvalidCid{err}
} }
mhlen, err := varint.ReadUvarint(r) mhlen, err := varint.ReadUvarint(r)
if err != nil { if err != nil {
return Prefix{}, err return Prefix{}, ErrInvalidCid{err}
} }
return Prefix{ return Prefix{
@@ -615,12 +641,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, fmt.Errorf("not enough bytes for cid v0") return 0, Undef, ErrInvalidCid{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, err return 0, Undef, ErrInvalidCid{err}
} }
return 34, Cid{string(h)}, nil return 34, Cid{string(h)}, nil
@@ -628,21 +654,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, err return 0, Undef, ErrInvalidCid{err}
} }
if vers != 1 { if vers != 1 {
return 0, Undef, fmt.Errorf("expected 1 as the cid version number, got: %d", vers) return 0, Undef, ErrInvalidCid{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, err return 0, Undef, ErrInvalidCid{err}
} }
mhnr, _, err := mh.MHFromBytes(data[n+cn:]) mhnr, _, err := mh.MHFromBytes(data[n+cn:])
if err != nil { if err != nil {
return 0, Undef, err return 0, Undef, ErrInvalidCid{err}
} }
l := n + cn + mhnr l := n + cn + mhnr
@@ -695,6 +721,9 @@ func (r *bufByteReader) ReadByte() (byte, error) {
// It's recommended to supply a reader that buffers and implements io.ByteReader, // It's recommended to supply a reader that buffers and implements io.ByteReader,
// as CidFromReader has to do many single-byte reads to decode varints. // as CidFromReader has to do many single-byte reads to decode varints.
// If the argument only implements io.Reader, single-byte Read calls are used instead. // If the argument only implements io.Reader, single-byte Read calls are used instead.
//
// If the Reader is found to yield zero bytes, an io.EOF error is returned directly, in all
// other error cases, an ErrInvalidCid, wrapping the original error, is returned.
func CidFromReader(r io.Reader) (int, Cid, error) { func CidFromReader(r io.Reader) (int, Cid, error) {
// 64 bytes is enough for any CIDv0, // 64 bytes is enough for any CIDv0,
// and it's enough for most CIDv1s in practice. // and it's enough for most CIDv1s in practice.
@@ -705,32 +734,37 @@ 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, err if err == io.EOF {
// First-byte read in ReadUvarint errors with io.EOF, so reader has no data.
// Subsequent reads with an EOF will return io.ErrUnexpectedEOF and be wrapped here.
return 0, Undef, err
}
return len(br.dst), Undef, ErrInvalidCid{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, err return len(br.dst) + n, Undef, ErrInvalidCid{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, err return len(br.dst), Undef, ErrInvalidCid{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, fmt.Errorf("expected 1 as the cid version number, got: %d", vers) return len(br.dst), Undef, ErrInvalidCid{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, err return len(br.dst), Undef, ErrInvalidCid{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.
@@ -741,19 +775,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, err return len(br.dst), Undef, ErrInvalidCid{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, err return len(br.dst), Undef, ErrInvalidCid{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, fmt.Errorf("refusing to allocate %d bytes for a digest", mhl) return len(br.dst), Undef, ErrInvalidCid{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.
@@ -772,7 +806,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, err return prefixLength + n, Undef, ErrInvalidCid{err}
} }
// This simply ensures the multihash is valid. // This simply ensures the multihash is valid.
@@ -780,7 +814,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, err return len(br.dst), Undef, ErrInvalidCid{err}
} }
return len(br.dst), Cid{string(br.dst)}, nil return len(br.dst), Cid{string(br.dst)}, nil

View File

@@ -4,6 +4,7 @@ import (
"bytes" "bytes"
crand "crypto/rand" crand "crypto/rand"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"math/rand" "math/rand"
@@ -162,6 +163,9 @@ 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")
} }
@@ -227,6 +231,9 @@ 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) {
@@ -282,6 +289,9 @@ 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) {
@@ -372,6 +382,9 @@ 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")
}
} }
} }
@@ -381,6 +394,9 @@ 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) {
@@ -417,18 +433,30 @@ 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) {
@@ -455,6 +483,9 @@ 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)
@@ -572,17 +603,29 @@ func TestJsonRoundTrip(t *testing.T) {
t.Fatal("cids not equal for Cid") t.Fatal("cids not equal for Cid")
} }
if err = actual2.UnmarshalJSON([]byte("1")); err == nil { err = actual2.UnmarshalJSON([]byte("1"))
if err == nil {
t.Fatal("expected error for too-short JSON") t.Fatal("expected error for too-short JSON")
} }
if !errors.Is(err, ErrInvalidCid{}) {
if err = actual2.UnmarshalJSON([]byte(`{"nope":"nope"}`)); err == nil { t.Fatal("expected error to be ErrInvalidCid")
t.Fatal("expected error for bad CID JSON")
} }
if err = actual2.UnmarshalJSON([]byte(`bad "" json!`)); err == nil { err = actual2.UnmarshalJSON([]byte(`{"nope":"nope"}`))
if err == nil {
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 == 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()
@@ -740,6 +783,30 @@ func TestBadCidInput(t *testing.T) {
} }
} }
func TestFromReaderNoData(t *testing.T) {
// Reading no data from io.Reader should return io.EOF, not ErrInvalidCid.
n, cid, err := CidFromReader(bytes.NewReader(nil))
if err != io.EOF {
t.Fatal("Expected io.EOF error")
}
if cid != Undef {
t.Fatal("Expected Undef CID")
}
if n != 0 {
t.Fatal("Expected 0 data")
}
// Read byte indicatiing more data to and check error is ErrInvalidCid.
_, _, err = CidFromReader(bytes.NewReader([]byte{0x80}))
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("Expected ErrInvalidCid error")
}
// Check for expected wrapped error.
if !errors.Is(err, io.ErrUnexpectedEOF) {
t.Fatal("Expected error", io.ErrUnexpectedEOF)
}
}
func TestBadParse(t *testing.T) { func TestBadParse(t *testing.T) {
hash, err := mh.Sum([]byte("foobar"), mh.SHA3_256, -1) hash, err := mh.Sum([]byte("foobar"), mh.SHA3_256, -1)
if err != nil { if err != nil {
@@ -749,6 +816,9 @@ 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) {
@@ -763,3 +833,80 @@ 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)
}

11
go.mod
View File

@@ -1,20 +1,21 @@
module github.com/ipfs/go-cid module github.com/ipfs/go-cid
require ( require (
github.com/multiformats/go-multibase v0.0.3 github.com/multiformats/go-multibase v0.2.0
github.com/multiformats/go-multihash v0.0.15 github.com/multiformats/go-multihash v0.2.1
github.com/multiformats/go-varint v0.0.6 github.com/multiformats/go-varint v0.0.7
) )
require ( require (
github.com/klauspost/cpuid/v2 v2.0.4 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect github.com/minio/sha256-simd v1.0.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.0.3 // indirect github.com/multiformats/go-base32 v0.0.3 // indirect
github.com/multiformats/go-base36 v0.1.0 // indirect github.com/multiformats/go-base36 v0.1.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
golang.org/x/crypto v0.1.0 // indirect golang.org/x/crypto v0.1.0 // indirect
golang.org/x/sys v0.1.0 // indirect golang.org/x/sys v0.1.0 // indirect
lukechampine.com/blake3 v1.1.6 // indirect
) )
go 1.19 go 1.19

30
go.sum
View File

@@ -1,31 +1,25 @@
github.com/klauspost/cpuid/v2 v2.0.4 h1:g0I61F2K2DjRHz1cnxlkNSBIaePVoJIjjnHui8QHbiw=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4=
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
github.com/multiformats/go-multihash v0.0.15 h1:hWOPdrNqDjwHDx82vsYGSDZNyktOJJ2dzZJzFkOV1jM= github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108=
github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc=
github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= 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/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= lukechampine.com/blake3 v1.1.6 h1:H3cROdztr7RCfoaTpGZFQsrqvweFLrqS73j7L7cmR5c=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=

View File

@@ -1,3 +1,3 @@
{ {
"version": "v0.3.2" "version": "v0.4.1"
} }