Compare commits

..

3 Commits

Author SHA1 Message Date
Steven Allen
02253f5120 ci: remove gx support 2019-08-20 08:03:01 -07:00
whyrusleeping
a5acc01164 use constants and enforce max length elsewhere 2019-08-19 18:19:07 -07:00
whyrusleeping
aa319eb8b5 Add fastpath cbor marshalers 2019-08-19 17:13:12 -07:00
13 changed files with 368 additions and 474 deletions

View File

@@ -1,8 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Getting Help on IPFS
url: https://ipfs.io/help
about: All information about how and where to get help on IPFS.
- name: IPFS Official Forum
url: https://discuss.ipfs.io
about: Please post general questions, support requests, and discussions here.

View File

@@ -1,19 +0,0 @@
---
name: Open an issue
about: Only for actionable issues relevant to this repository.
title: ''
labels: need/triage
assignees: ''
---
<!--
Hello! To ensure this issue is correctly addressed as soon as possible by the IPFS team, please try to make sure:
- This issue is relevant to this repository's topic or codebase.
- A clear description is provided. It should includes as much relevant information as possible and clear scope for the issue to be actionable.
FOR GENERAL DISCUSSION, HELP OR QUESTIONS, please see the options at https://ipfs.io/help or head directly to https://discuss.ipfs.io.
(you can delete this section after reading)
-->

68
.github/config.yml vendored
View File

@@ -1,68 +0,0 @@
# Configuration for welcome - https://github.com/behaviorbot/welcome
# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome
# Comment to be posted to on first time issues
newIssueWelcomeComment: >
Thank you for submitting your first issue to this repository! A maintainer
will be here shortly to triage and review.
In the meantime, please double-check that you have provided all the
necessary information to make this process easy! Any information that can
help save additional round trips is useful! We currently aim to give
initial feedback within **two business days**. If this does not happen, feel
free to leave a comment.
Please keep an eye on how this issue will be labeled, as labels give an
overview of priorities, assignments and additional actions requested by the
maintainers:
- "Priority" labels will show how urgent this is for the team.
- "Status" labels will show if this is ready to be worked on, blocked, or in progress.
- "Need" labels will indicate if additional input or analysis is required.
Finally, remember to use https://discuss.ipfs.io if you just need general
support.
# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
# Comment to be posted to on PRs from first time contributors in your repository
newPRWelcomeComment: >
Thank you for submitting this PR!
A maintainer will be here shortly to review it.
We are super grateful, but we are also overloaded! Help us by making sure
that:
* The context for this PR is clear, with relevant discussion, decisions
and stakeholders linked/mentioned.
* Your contribution itself is clear (code comments, self-review for the
rest) and in its best form. Follow the [code contribution
guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md#code-contribution-guidelines)
if they apply.
Getting other community members to do a review would be great help too on
complex PRs (you can ask in the chats/forums). If you are unsure about
something, just leave us a comment.
Next steps:
* A maintainer will triage and assign priority to this PR, commenting on
any missing things and potentially assigning a reviewer for high
priority items.
* The PR gets reviews, discussed and approvals as needed.
* The PR is merged by maintainers when it has been approved and comments addressed.
We currently aim to provide initial feedback/triaging within **two business
days**. Please keep an eye on any labelling actions, as these will indicate
priorities and status of your contribution.
We are very grateful for your contribution!
# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge
# Comment to be posted to on pull requests merged by a first time user
# Currently disabled
#firstPRMergeComment: ""

1
.gx/lastpubver Normal file
View File

@@ -0,0 +1 @@
0.9.3: QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN

View File

@@ -1,5 +0,0 @@
all: deps
deps:
go get github.com/mattn/goveralls
go get golang.org/x/tools/cmd/cover

View File

@@ -14,9 +14,6 @@ go-cid
This is an implementation in Go of the [CID spec](https://github.com/ipld/cid).
It is used in `go-ipfs` and related packages to refer to a typed hunk of data.
## Lead Maintainer
[Eric Myhre](https://github.com/warpfork)
## Table of Contents
@@ -34,16 +31,20 @@ It is used in `go-ipfs` and related packages to refer to a typed hunk of data.
go get github.com/ipfs/go-cid
```
Note that `go-cid` is packaged with Gx, so it is recommended to use Gx to install and use it (see Usage section).
## Usage
### Running tests
Run tests with `go test` from the directory root
Before running tests, please run:
```sh
go test
make deps
```
This will make sure that dependencies are rewritten to known working versions.
### Examples
#### Parsing string input from users

View File

@@ -38,7 +38,7 @@ func (p V0Builder) Sum(data []byte) (Cid, error) {
if err != nil {
return Undef, err
}
return Cid{string(hash)}, nil
return NewCidV0(hash), nil
}
func (p V0Builder) GetCodec() uint64 {

436
cid.go
View File

@@ -22,6 +22,7 @@ package cid
import (
"bytes"
"encoding"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
@@ -30,17 +31,33 @@ import (
mbase "github.com/multiformats/go-multibase"
mh "github.com/multiformats/go-multihash"
varint "github.com/multiformats/go-varint"
cbg "github.com/whyrusleeping/cbor-gen"
)
// UnsupportedVersionString just holds an error message
const UnsupportedVersionString = "<unsupported cid version>"
const CidMaxLen = 256
var (
// ErrVarintBuffSmall means that a buffer passed to the cid parser was not
// long enough, or did not contain an invalid cid
ErrVarintBuffSmall = errors.New("reading varint: buffer too small")
// ErrVarintTooBig means that the varint in the given cid was above the
// limit of 2^64
ErrVarintTooBig = errors.New("reading varint: varint bigger than 64bits" +
" and not supported")
// ErrCidTooShort means that the cid passed to decode was not long
// enough to be a valid Cid
ErrCidTooShort = errors.New("cid too short")
// ErrCidTooLong means that the CIDs payload was greater than CidMaxLen
// bytes
ErrCidTooLong = errors.New("cid too long")
// ErrInvalidEncoding means that selected encoding is not supported
// by this Cid version
ErrInvalidEncoding = errors.New("invalid base encoding")
@@ -58,122 +75,102 @@ const (
GitRaw = 0x78
EthBlock = 0x90
EthBlockList = 0x91
EthTxTrie = 0x92
EthTx = 0x93
EthTxReceiptTrie = 0x94
EthTxReceipt = 0x95
EthStateTrie = 0x96
EthAccountSnapshot = 0x97
EthStorageTrie = 0x98
BitcoinBlock = 0xb0
BitcoinTx = 0xb1
ZcashBlock = 0xc0
ZcashTx = 0xc1
DecredBlock = 0xe0
DecredTx = 0xe1
DashBlock = 0xf0
DashTx = 0xf1
FilCommitmentUnsealed = 0xf101
FilCommitmentSealed = 0xf102
EthBlock = 0x90
EthBlockList = 0x91
EthTxTrie = 0x92
EthTx = 0x93
EthTxReceiptTrie = 0x94
EthTxReceipt = 0x95
EthStateTrie = 0x96
EthAccountSnapshot = 0x97
EthStorageTrie = 0x98
BitcoinBlock = 0xb0
BitcoinTx = 0xb1
ZcashBlock = 0xc0
ZcashTx = 0xc1
DecredBlock = 0xe0
DecredTx = 0xe1
DashBlock = 0xf0
DashTx = 0xf1
)
// 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,
"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,
}
// 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",
}
// tryNewCidV0 tries to convert a multihash into a CIDv0 CID and returns an
// error on failure.
func tryNewCidV0(mhash mh.Multihash) (Cid, error) {
// Need to make sure hash is valid for CidV0 otherwise we will
// incorrectly detect it as CidV1 in the Version() method
dec, err := mh.Decode(mhash)
if err != nil {
return Undef, err
}
if dec.Code != mh.SHA2_256 || dec.Length != 32 {
return Undef, fmt.Errorf("invalid hash for cidv0 %d-%d", dec.Code, dec.Length)
}
return Cid{string(mhash)}, nil
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",
}
// NewCidV0 returns a Cid-wrapped multihash.
// They exist to allow IPFS to work with Cids while keeping
// compatibility with the plain-multihash format used used in IPFS.
// NewCidV1 should be used preferentially.
//
// Panics if the multihash isn't sha2-256.
func NewCidV0(mhash mh.Multihash) Cid {
c, err := tryNewCidV0(mhash)
// Need to make sure hash is valid for CidV0 otherwise we will
// incorrectly detect it as CidV1 in the Version() method
dec, err := mh.Decode(mhash)
if err != nil {
panic(err)
}
return c
if dec.Code != mh.SHA2_256 || dec.Length != 32 {
panic("invalid hash for cidv0")
}
return Cid{string(mhash)}
}
// NewCidV1 returns a new Cid using the given multicodec-packed
// content type.
//
// Panics if the multihash is invalid.
func NewCidV1(codecType uint64, mhash mh.Multihash) Cid {
hashlen := len(mhash)
// two 8 bytes (max) numbers plus hash
buf := make([]byte, 1+varint.UvarintSize(codecType)+hashlen)
n := varint.PutUvarint(buf, 1)
n += varint.PutUvarint(buf[n:], codecType)
buf := make([]byte, 2*binary.MaxVarintLen64+hashlen)
n := binary.PutUvarint(buf, 1)
n += binary.PutUvarint(buf[n:], codecType)
cn := copy(buf[n:], mhash)
if cn != hashlen {
panic("copy hash length is inconsistent")
@@ -215,7 +212,7 @@ func Parse(v interface{}) (Cid, error) {
case []byte:
return Cast(v2)
case mh.Multihash:
return tryNewCidV0(v2)
return NewCidV0(v2), nil
case Cid:
return v2, nil
default:
@@ -246,7 +243,7 @@ func Decode(v string) (Cid, error) {
return Undef, err
}
return tryNewCidV0(hash)
return NewCidV0(hash), nil
}
_, data, err := mbase.Decode(v)
@@ -279,6 +276,17 @@ func ExtractEncoding(v string) (mbase.Encoding, error) {
return encoding, nil
}
func uvError(read int) error {
switch {
case read == 0:
return ErrVarintBuffSmall
case read < 0:
return ErrVarintTooBig
default:
return nil
}
}
// Cast takes a Cid data slice, parses it and returns a Cid.
// For CidV1, the data buffer is in the form:
//
@@ -291,16 +299,40 @@ func ExtractEncoding(v string) (mbase.Encoding, error) {
// Please use decode when parsing a regular Cid string, as Cast does not
// expect multibase-encoded data. Cast accepts the output of Cid.Bytes().
func Cast(data []byte) (Cid, error) {
nr, c, err := CidFromBytes(data)
if len(data) > CidMaxLen {
return Undef, ErrCidTooLong
}
if len(data) == 34 && data[0] == 18 && data[1] == 32 {
h, err := mh.Cast(data)
if err != nil {
return Undef, err
}
return NewCidV0(h), nil
}
vers, n := binary.Uvarint(data)
if err := uvError(n); err != nil {
return Undef, err
}
if vers != 1 {
return Undef, fmt.Errorf("expected 1 as the cid version number, got: %d", vers)
}
_, cn := binary.Uvarint(data[n:])
if err := uvError(cn); err != nil {
return Undef, err
}
rest := data[n+cn:]
h, err := mh.Cast(rest)
if err != nil {
return Undef, err
}
if nr != len(data) {
return Undef, fmt.Errorf("trailing bytes in data buffer passed to cid Cast")
}
return c, nil
return Cid{string(data[0 : n+cn+len(h)])}, nil
}
// UnmarshalBinary is equivalent to Cast(). It implements the
@@ -338,14 +370,14 @@ func (c Cid) Type() uint64 {
if c.Version() == 0 {
return DagProtobuf
}
_, n, _ := uvarint(c.str)
codec, _, _ := uvarint(c.str[n:])
_, n := uvarint(c.str)
codec, _ := uvarint(c.str[n:])
return codec
}
// String returns the default string representation of a
// Cid. Currently, Base32 is used for CIDV1 as the encoding for the
// multibase string, Base58 is used for CIDV0.
// Cid. Currently, Base58 is used as the encoding for the
// multibase string.
func (c Cid) String() string {
switch c.Version() {
case 0:
@@ -401,9 +433,9 @@ func (c Cid) Hash() mh.Multihash {
}
// skip version length
_, n1, _ := varint.FromUvarint(bytes)
_, n1 := binary.Uvarint(bytes)
// skip codec length
_, n2, _ := varint.FromUvarint(bytes[n1:])
_, n2 := binary.Uvarint(bytes[n1:])
return mh.Multihash(bytes[n1+n2:])
}
@@ -415,30 +447,6 @@ func (c Cid) Bytes() []byte {
return []byte(c.str)
}
// ByteLen returns the length of the CID in bytes.
// It's equivalent to `len(c.Bytes())`, but works without an allocation,
// and should therefore be preferred.
//
// (See also the WriteTo method for other important operations that work without allocation.)
func (c Cid) ByteLen() int {
return len(c.str)
}
// WriteBytes writes the CID bytes to the given writer.
// This method works without incurring any allocation.
//
// (See also the ByteLen method for other important operations that work without allocation.)
func (c Cid) WriteBytes(w io.Writer) (int, error) {
n, err := io.WriteString(w, c.str)
if err != nil {
return n, err
}
if n != len(c.str) {
return n, fmt.Errorf("failed to write entire cid string")
}
return n, nil
}
// MarshalBinary is equivalent to Bytes(). It implements the
// encoding.BinaryMarshaler interface.
func (c Cid) MarshalBinary() ([]byte, error) {
@@ -518,32 +526,84 @@ func (c Cid) Loggable() map[string]interface{} {
// Prefix builds and returns a Prefix out of a Cid.
func (c Cid) Prefix() Prefix {
if c.Version() == 0 {
return Prefix{
MhType: mh.SHA2_256,
MhLength: 32,
Version: 0,
Codec: DagProtobuf,
}
}
offset := 0
version, n, _ := uvarint(c.str[offset:])
offset += n
codec, n, _ := uvarint(c.str[offset:])
offset += n
mhtype, n, _ := uvarint(c.str[offset:])
offset += n
mhlen, _, _ := uvarint(c.str[offset:])
dec, _ := mh.Decode(c.Hash()) // assuming we got a valid multiaddr, this will not error
return Prefix{
MhType: mhtype,
MhLength: int(mhlen),
Version: version,
Codec: codec,
MhType: dec.Code,
MhLength: dec.Length,
Version: c.Version(),
Codec: c.Type(),
}
}
func (c Cid) MarshalCBOR(w io.Writer) error {
tag := cbg.CborEncodeMajorType(cbg.MajTag, 42)
if _, err := w.Write(tag); err != nil {
return err
}
typ := cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(c.Bytes())+1))
if _, err := w.Write(typ); err != nil {
return err
}
// that binary multibase prefix...
if _, err := w.Write([]byte{0}); err != nil {
return err
}
if _, err := w.Write(c.Bytes()); err != nil {
return err
}
return nil
}
func (c *Cid) UnmarshalCBOR(br cbg.ByteReader) error {
maj, extra, err := cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajTag || extra != 42 {
return fmt.Errorf("CBOR serialized CIDs must have the tag 42")
}
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != 2 {
return fmt.Errorf("CBOR serialized CIDs must be tagged byte arrays")
}
if extra < 2 {
return fmt.Errorf("encoded CID body must be at least two bytes long")
}
if extra > CidMaxLen+1 {
return fmt.Errorf("CIDs cannot be longer than 256 bytes")
}
buf := make([]byte, extra)
if _, err := io.ReadFull(br, buf); err != nil {
return fmt.Errorf("failed to read CID body: %s", err)
}
if buf[0] != 0 {
return fmt.Errorf("encoded CID did not have binary multibase")
}
out, err := Cast(buf[1:])
if err != nil {
return err
}
*c = out
return nil
}
// Prefix represents all the metadata of a Cid,
// that is, the Version, the Codec, the Multihash type
// and the Multihash length. It does not contains
@@ -565,12 +625,6 @@ func (p Prefix) Sum(data []byte) (Cid, error) {
length = -1
}
if p.Version == 0 && (p.MhType != mh.SHA2_256 ||
(p.MhLength != 32 && p.MhLength != -1)) {
return Undef, fmt.Errorf("invalid v0 prefix")
}
hash, err := mh.Sum(data, p.MhType, length)
if err != nil {
return Undef, err
@@ -590,42 +644,34 @@ func (p Prefix) Sum(data []byte) (Cid, error) {
//
// <version><codec><mh-type><mh-length>
func (p Prefix) Bytes() []byte {
size := varint.UvarintSize(p.Version)
size += varint.UvarintSize(p.Codec)
size += varint.UvarintSize(p.MhType)
size += varint.UvarintSize(uint64(p.MhLength))
buf := make([]byte, size)
n := varint.PutUvarint(buf, p.Version)
n += varint.PutUvarint(buf[n:], p.Codec)
n += varint.PutUvarint(buf[n:], p.MhType)
n += varint.PutUvarint(buf[n:], uint64(p.MhLength))
if n != size {
panic("size mismatch")
}
return buf
buf := make([]byte, 4*binary.MaxVarintLen64)
n := binary.PutUvarint(buf, p.Version)
n += binary.PutUvarint(buf[n:], p.Codec)
n += binary.PutUvarint(buf[n:], uint64(p.MhType))
n += binary.PutUvarint(buf[n:], uint64(p.MhLength))
return buf[:n]
}
// PrefixFromBytes parses a Prefix-byte representation onto a
// Prefix.
func PrefixFromBytes(buf []byte) (Prefix, error) {
r := bytes.NewReader(buf)
vers, err := varint.ReadUvarint(r)
vers, err := binary.ReadUvarint(r)
if err != nil {
return Prefix{}, err
}
codec, err := varint.ReadUvarint(r)
codec, err := binary.ReadUvarint(r)
if err != nil {
return Prefix{}, err
}
mhtype, err := varint.ReadUvarint(r)
mhtype, err := binary.ReadUvarint(r)
if err != nil {
return Prefix{}, err
}
mhlen, err := varint.ReadUvarint(r)
mhlen, err := binary.ReadUvarint(r)
if err != nil {
return Prefix{}, err
}
@@ -637,41 +683,3 @@ func PrefixFromBytes(buf []byte) (Prefix, error) {
MhLength: int(mhlen),
}, nil
}
func CidFromBytes(data []byte) (int, Cid, error) {
if len(data) > 2 && data[0] == mh.SHA2_256 && data[1] == 32 {
if len(data) < 34 {
return 0, Undef, fmt.Errorf("not enough bytes for cid v0")
}
h, err := mh.Cast(data[:34])
if err != nil {
return 0, Undef, err
}
return 34, Cid{string(h)}, nil
}
vers, n, err := varint.FromUvarint(data)
if err != nil {
return 0, Undef, err
}
if vers != 1 {
return 0, Undef, fmt.Errorf("expected 1 as the cid version number, got: %d", vers)
}
_, cn, err := varint.FromUvarint(data[n:])
if err != nil {
return 0, Undef, err
}
mhnr, _, err := mh.MHFromBytes(data[n+cn:])
if err != nil {
return 0, Undef, err
}
l := n + cn + mhnr
return l, Cid{string(data[0:l])}, nil
}

View File

@@ -2,6 +2,7 @@ package cid
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"math/rand"
@@ -16,30 +17,28 @@ import (
// 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",
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",
}
func assertEqual(t *testing.T, a, b Cid) {
@@ -335,41 +334,6 @@ func TestNewPrefixV0(t *testing.T) {
if c1.Prefix() != c2.Prefix() {
t.Fatal("prefixes mismatch")
}
}
func TestInvalidV0Prefix(t *testing.T) {
tests := []Prefix{
{
MhType: mh.SHA2_256,
MhLength: 31,
},
{
MhType: mh.SHA2_256,
MhLength: 33,
},
{
MhType: mh.SHA2_256,
MhLength: -2,
},
{
MhType: mh.SHA2_512,
MhLength: 32,
},
{
MhType: mh.SHA2_512,
MhLength: -1,
},
}
for i, p := range tests {
t.Log(i)
_, err := p.Sum([]byte("testdata"))
if err == nil {
t.Fatalf("should error (index %d)", i)
}
}
}
func TestPrefixRoundtrip(t *testing.T) {
@@ -536,6 +500,77 @@ func TestJsonRoundTrip(t *testing.T) {
}
}
func TestCBORSerialization(t *testing.T) {
cases := map[string]string{
"bafybeibpzhw63c3vh7vushzvfzvkc2nbhr6nm3ui2o7kj33cx3xoovnzp4": "d82a582500017012202fc9eded8b753feb491f352e6aa169a13c7cd66e88d3bea4ef62beeee755b97f",
"QmRZCTZPygAnfagZKzjy48b4LMmqPNpJnNBEg5LtCCzHCA": "d82a58230012202fc9eded8b753feb491f352e6aa169a13c7cd66e88d3bea4ef62beeee755b97f",
}
for cs, encs := range cases {
c, err := Decode(cs)
if err != nil {
t.Fatal(err)
}
buf := new(bytes.Buffer)
if err := c.MarshalCBOR(buf); err != nil {
t.Fatal(err)
}
exp, err := hex.DecodeString(encs)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(exp, buf.Bytes()) {
t.Fatalf("serialization incorrect: %x != %x", buf.Bytes(), exp)
}
var out Cid
if err := out.UnmarshalCBOR(bytes.NewReader(exp)); err != nil {
t.Fatalf("unmarshal case %s failed: %s", cs, err)
}
if out != c {
t.Fatal("unmarshal CBOR failed")
}
}
}
func BenchmarkCBORMarshal(b *testing.B) {
c, err := Decode("bafybeibpzhw63c3vh7vushzvfzvkc2nbhr6nm3ui2o7kj33cx3xoovnzp4")
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
buf := new(bytes.Buffer)
for i := 0; i < b.N; i++ {
buf.Reset()
if err := c.MarshalCBOR(buf); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkCBORUnmarshal(b *testing.B) {
enc, err := hex.DecodeString("d82a582500017012202fc9eded8b753feb491f352e6aa169a13c7cd66e88d3bea4ef62beeee755b97f")
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
var c Cid
for i := 0; i < b.N; i++ {
br := bytes.NewReader(enc)
if err := c.UnmarshalCBOR(br); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkStringV1(b *testing.B) {
data := []byte("this is some test content")
hash, _ := mh.Sum(data, mh.SHA2_256, -1)
@@ -552,49 +587,3 @@ func BenchmarkStringV1(b *testing.B) {
b.FailNow()
}
}
func TestReadCidsFromBuffer(t *testing.T) {
cidstr := []string{
"bafkreie5qrjvaw64n4tjm6hbnm7fnqvcssfed4whsjqxzslbd3jwhsk3mm",
"k2cwueckqkibutvhkr4p2ln2pjcaxaakpd9db0e7j7ax1lxhhxy3ekpv",
"Qmf5Qzp6nGBku7CEn2UQx4mgN8TW69YUok36DrGa6NN893",
"zb2rhZi1JR4eNc2jBGaRYJKYM8JEB4ovenym8L1CmFsRAytkz",
}
var cids []Cid
var buf []byte
for _, cs := range cidstr {
c, err := Decode(cs)
if err != nil {
t.Fatal(err)
}
cids = append(cids, c)
buf = append(buf, c.Bytes()...)
}
var cur int
for _, expc := range cids {
n, c, err := CidFromBytes(buf[cur:])
if err != nil {
t.Fatal(err)
}
if c != expc {
t.Fatal("cids mismatched")
}
cur += n
}
if cur != len(buf) {
t.Fatal("had trailing bytes")
}
}
func TestBadParse(t *testing.T) {
hash, err := mh.Sum([]byte("foobar"), mh.SHA3_256, -1)
if err != nil {
t.Fatal(err)
}
_, err = Parse(hash)
if err == nil {
t.Fatal("expected to fail to parse an invalid CIDv1 CID")
}
}

8
go.mod
View File

@@ -1,9 +1,7 @@
module github.com/ipfs/go-cid
require (
github.com/multiformats/go-multibase v0.0.3
github.com/multiformats/go-multihash v0.0.13
github.com/multiformats/go-varint v0.0.5
github.com/multiformats/go-multibase v0.0.1
github.com/multiformats/go-multihash v0.0.1
github.com/whyrusleeping/cbor-gen v0.0.0-20190820011425-d5123893e85a
)
go 1.13

55
go.sum
View File

@@ -1,28 +1,39 @@
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gxed/hashland/keccakpg v0.0.1 h1:wrk3uMNaMxbXiHibbPO4S0ymqJMm41WiudyFSs7UnsU=
github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
github.com/gxed/hashland/murmur3 v0.0.1 h1:SheiaIt0sda5K+8FLz952/1iWS9zrnKsEJaOJu4ZbSc=
github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY=
github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc=
github.com/ipfs/go-ipld-cbor v0.0.3/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc=
github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771 h1:MHkK1uRtFbVqvAgvWxafZe54+5uBxLluGylDiKgdhwo=
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16 h1:5W7KhL8HVF3XCFOweFD3BNESdnO8ewyYTFT2R+/b8FQ=
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
github.com/mr-tron/base58 v1.1.3/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/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
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-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk=
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
github.com/multiformats/go-multihash v0.0.13 h1:06x+mk/zj1FoMsgNejLpy6QTvJqlSt/BhLEy87zidlc=
github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
github.com/multiformats/go-varint v0.0.5 h1:XVZwSo04Cs3j/jS0uAEPpT3JY6DzMcVLLoWOSnCxOjg=
github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA=
github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
github.com/multiformats/go-multihash v0.0.1 h1:HHwN1K12I+XllBCrqKnhX949Orn4oawPkegHMu2vDqQ=
github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU=
github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
github.com/whyrusleeping/cbor-gen v0.0.0-20190818214716-81392149d1dd h1:Nj8P2Fg3mRVnBfkNnxZK+QhjwCxI3kLng2nfTTLIudk=
github.com/whyrusleeping/cbor-gen v0.0.0-20190818214716-81392149d1dd/go.mod h1:ga0xb76iSIPvNNiuR+g3+c3Rnwy0oVuBGvqDwsEV6x4=
github.com/whyrusleeping/cbor-gen v0.0.0-20190819235733-17c06ddc16cb h1:49o2mhJ/B2nsmSjV4fUsrFEoKGM9NLWQSnk+YrAKFLg=
github.com/whyrusleeping/cbor-gen v0.0.0-20190819235733-17c06ddc16cb/go.mod h1:ga0xb76iSIPvNNiuR+g3+c3Rnwy0oVuBGvqDwsEV6x4=
github.com/whyrusleeping/cbor-gen v0.0.0-20190820011425-d5123893e85a h1:MYglUpczqDD6LXsnSXk6NWzwf64fheAuA3eBjyYGD/8=
github.com/whyrusleeping/cbor-gen v0.0.0-20190820011425-d5123893e85a/go.mod h1:38LwjsrZy8ga8AbyOueCsLUAtdc1BDj/I929R+LnJfI=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/sys v0.0.0-20190219092855-153ac476189d h1:Z0Ahzd7HltpJtjAHHxX8QFP3j1yYgiuvjbjRzDj/KH0=
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@@ -1,9 +1,5 @@
package cid
import (
"github.com/multiformats/go-varint"
)
// Version of varint function that work with a string rather than
// []byte to avoid unnecessary allocation
@@ -19,7 +15,7 @@ import (
// n < 0: value larger than 64 bits (overflow)
// and -n is the number of bytes read
//
func uvarint(buf string) (uint64, int, error) {
func uvarint(buf string) (uint64, int) {
var x uint64
var s uint
// we have a binary string so we can't use a range loope
@@ -27,14 +23,12 @@ func uvarint(buf string) (uint64, int, error) {
b := buf[i]
if b < 0x80 {
if i > 9 || i == 9 && b > 1 {
return 0, 0, varint.ErrOverflow
} else if b == 0 && i > 0 {
return 0, 0, varint.ErrNotMinimal
return 0, -(i + 1) // overflow
}
return x | uint64(b)<<s, i + 1, nil
return x | uint64(b)<<s, i + 1
}
x |= uint64(b&0x7f) << s
s += 7
}
return 0, 0, varint.ErrUnderflow
return 0, 0
}

View File

@@ -1,25 +1,17 @@
package cid
import (
"encoding/binary"
"testing"
"github.com/multiformats/go-varint"
)
func TestUvarintRoundTrip(t *testing.T) {
testCases := []uint64{0, 1, 2, 127, 128, 129, 255, 256, 257, 1<<63 - 1}
for _, tc := range testCases {
t.Log("testing", tc)
buf := make([]byte, 16)
varint.PutUvarint(buf, tc)
v, l1, err := uvarint(string(buf))
if err != nil {
t.Fatalf("%v: %s", buf, err)
}
_, l2, err := varint.FromUvarint(buf)
if err != nil {
t.Fatal(err)
}
binary.PutUvarint(buf, tc)
v, l1 := uvarint(string(buf))
_, l2 := binary.Uvarint(buf)
if tc != v {
t.Errorf("roundtrip failed expected %d but got %d", tc, v)
}