Compare commits

...

84 Commits

Author SHA1 Message Date
Kevin Atkinson
afcde25c66 gx publish 0.8.0 2018-08-21 15:15:34 -04:00
Kevin Atkinson
fb85ebd768 Merge pull request #69 from ipfs/kevina/extract
Extract non-core functionality from go-cid into go-cidutil
2018-08-21 15:11:14 -04:00
Kevin Atkinson
870aa9e7de Extract non-core functionality from go-cid into go-cidutil. 2018-08-16 21:51:31 -04:00
Steven Allen
73e5246a65 gx publish 0.7.25 2018-08-15 08:24:56 -07:00
Steven Allen
83a7594d41 Merge pull request #67 from ipfs/feat/streaming-set
add a streaming CID set
2018-08-11 01:06:53 +00:00
Łukasz Magiera
3655c1cdd4 add a streaming CID set
used in https://github.com/ipfs/go-ipfs/pull/4804
2018-08-10 17:32:43 -07:00
Steven Allen
1543f4a136 Merge pull request #44 from ipfs/feat/bench
add String benchmark
2018-08-10 23:55:30 +00:00
Steven Allen
d6e0b4e5a7 add String benchmark
We call String all over the place so we should make sure it remains fast.
2018-08-10 16:23:25 -07:00
Kevin Atkinson
5eff744da0 gx publish 0.7.24 2018-08-10 17:40:00 -04:00
Kevin Atkinson
a8ae38caae Merge pull request #61 from ipfs/kevina/cid-fmt-enhan
cid-fmt Enhancments
2018-08-10 17:35:25 -04:00
Kevin Atkinson
23f03cb301 Merge pull request #53 from ipfs/kevina/format
Create new Builder interface for creating CIDs.
2018-08-10 17:34:56 -04:00
Kevin Atkinson
1c907dba61 Allocate bytes.Buffer directly on the stack. 2018-08-10 17:27:58 -04:00
Kevin Atkinson
86805e711c Change field names in V1Builder to match Prefix. 2018-08-10 00:13:49 -04:00
Kevin Atkinson
f868375825 Enhance Tests. 2018-08-09 23:55:45 -04:00
Kevin Atkinson
8f7ba15bfb Documentation Cleanups. 2018-08-09 23:55:33 -04:00
Kevin Atkinson
ae25e25d1a Remove PrefixToBuilder as it is needed right now. 2018-08-09 23:14:37 -04:00
Kevin Atkinson
0f09109d9f Replace DecodeV2 with ExtractEncoding 2018-08-09 02:37:09 -04:00
Kevin Atkinson
67951e2c09 Move deprecated function to there own file. 2018-08-09 00:15:04 -04:00
Kevin Atkinson
ad88cb11c5 Rename Format to Builder. 2018-08-09 00:15:04 -04:00
Steven Allen
10944c9d86 Merge pull request #63 from ipfs/tests/set
Add tests for Set type
2018-08-02 19:25:20 +00:00
Hector Sanjuan
b340dd202e Add tests for Set type
License: MIT
Signed-off-by: Hector Sanjuan <hector@protocol.ai>
2018-08-02 11:51:05 +02:00
Kevin Atkinson
c4bfcd0671 Extract most of cid-fmt logic so it can be used as a library. 2018-08-01 04:21:32 -04:00
Kevin Atkinson
36bab4873c Clean up cid-fmt util code. 2018-07-31 22:10:47 -04:00
Kevin Atkinson
056eac16ae Fix fmtRef string. 2018-07-31 17:12:16 -04:00
Kevin Atkinson
038b7f7cc9 Use new Encoder in multibase package.
This allows multibase codes to be specified by the name rather than just
the prefix char.
2018-07-31 17:07:49 -04:00
Kevin Atkinson
019d945bf5 Use lookup table in go-multibase now that it is supported. 2018-07-31 16:33:41 -04:00
Kevin Atkinson
799731b9e5 gx update go-multibase to 0.2.7 2018-07-31 16:33:41 -04:00
Hector Sanjuan
06f861b665 Merge pull request #59 from ipfs/0.7.23
gx publish 0.7.23
2018-07-28 00:44:12 +02:00
Kevin Atkinson
88cd5dcebf Add WithCodec and GetCodec methods to format interface. 2018-07-24 22:28:19 -04:00
Hector Sanjuan
9949dd29e5 gx publish 0.7.23
License: MIT
Signed-off-by: Hector Sanjuan <hector@protocol.ai>
2018-07-24 11:09:24 +02:00
Steven Allen
75d3ffe549 Merge pull request #58 from ipfs/feat/decred-codec
Add Decred codecs
2018-07-23 18:57:13 +00:00
Hector Sanjuan
8028fee095 Add Decred codecs
0xe0 and 0xe1 have been assigned to Decred block and tx in the
multicodecs table. https://github.com/multiformats/multicodec/pull/78

License: MIT
Signed-off-by: Hector Sanjuan <hector@protocol.ai>
2018-07-23 19:28:53 +02:00
Steven Allen
bd441bb43e gx publish 0.7.22 2018-07-12 10:21:27 +02:00
Steven Allen
10a4d040b4 Merge pull request #55 from ipfs/bug/marshal
Use value receiver for MarshalJSON so we can marshal values
2018-07-12 08:19:51 +00:00
Fritz Schneider
d204c18f7a make test a little more clear 2018-07-11 11:24:59 -10:00
Fritz Schneider
b41162260a fix typo 2018-07-11 11:21:16 -10:00
Fritz Schneider
9cb0b7bcae use value receiver 2018-07-11 11:20:53 -10:00
Kevin Atkinson
6f951560f5 Update deprecated note to reflect code review. 2018-06-28 23:08:17 -04:00
Kevin Atkinson
5d8ad3eb9c Create new Format interface for creating CIDs. 2018-06-28 22:48:56 -04:00
Steven Allen
5b04f30433 gx publish 0.7.21 2018-06-08 20:25:20 -07:00
Steven Allen
078355866b gx publish 0.7.20 2018-01-19 20:51:37 -08:00
Steven Allen
1805dd530f gx publish 0.7.19 2017-12-05 00:06:03 +00:00
Steven Allen
2055d2e652 Merge pull request #36 from ipfs/feat/cid-prefix-constructors
Add CID Prefix constructors.
2017-09-07 11:35:56 -07:00
Steven Allen
63d4b33fcf Add CID Prefix constructors.
One can now generate a CID by calling:

```go
cid.NewPrefixV1(cid.DagCBOR, mh.SHA_256).Sum(data)
```

Lots of code was already doing this by manually constructing CID Prefixes but
that tends to be a bit verbose and error prone.
2017-08-30 12:42:46 -07:00
Steven Allen
19c1c0e32e gx publish 0.7.18 2017-08-28 20:25:09 -07:00
Steven Allen
088f141a17 Merge pull request #33 from ipfs/gx/update-multibase
gx: update multibase
2017-08-28 20:22:53 -07:00
Steven Allen
758714796c gx: update multibase
And fix test case.
2017-08-28 20:17:55 -07:00
Steven Allen
ed3563b69e Merge pull request #31 from ipfs/kevina/cid-fmt
Implement 'cid-fmt' utility.
2017-08-28 16:56:05 -07:00
Kevin Atkinson
db11d7248a Use an exit code of 1 on non-fatal errors and 2 on fatal errors. 2017-08-15 17:23:44 -04:00
Kevin Atkinson
68abb41a9b Add ability to change CID version. 2017-08-15 15:34:15 -04:00
Kevin Atkinson
7333c60a00 Implement 'cid-fmt' utility. 2017-08-15 14:26:24 -04:00
Kevin Atkinson
f62e35b87a Implement basic 'cid-fmt' utility.
Currently only implements printing the Cid prefix.
2017-08-15 03:46:48 -04:00
Jeromy Johnson
5652e6f751 Merge pull request #29 from Stebalien/gx/publish-0.7.17
gx publish 0.7.17
2017-07-10 12:45:42 -07:00
Steven Allen
fa428a337b gx publish 0.7.17 2017-07-07 19:34:27 -07:00
Jeromy Johnson
e0f0e24c2d Merge pull request #28 from ipfs/gx/publish-0.7.16
gx publish 0.7.16
2017-07-04 09:56:40 -07:00
Łukasz Magiera
1aa1093d28 gx publish 0.7.16 2017-07-04 18:45:18 +02:00
Jeromy Johnson
f4f9847100 Merge pull request #27 from ipfs/feat/codecov
Use Codecov instead of Coveralls
2017-07-03 16:33:39 -07:00
Jakub Sztandera
83b0d39470 Disable comments 2017-07-01 11:19:22 +02:00
Jakub Sztandera
b7772ebfe3 Use Codecov instead of Coveralls
As we do in most other repos.
2017-07-01 11:11:48 +02:00
Jeromy Johnson
e449699cc1 Merge pull request #26 from hermanjunge/feat/codecs-map
Added map of string to codecs
2017-06-30 11:52:50 -07:00
Herman Junge
7d345d4817 Switch from byte to uint64 2017-06-30 10:00:21 -04:00
Herman Junge
3c03b9e581 Improve tests 2017-06-30 03:01:26 -04:00
Herman Junge
f6990413f7 Added map of string to codecs 2017-06-29 21:00:23 -04:00
Jeromy Johnson
3f7f6c6a1d Merge pull request #21 from ipfs/gx/0.7.15
gx publish 0.7.15
2017-06-19 12:12:57 -07:00
Łukasz Magiera
05eac5f7ca gx publish 0.7.15 2017-06-19 18:31:26 +02:00
Jakub Sztandera
1f385a8224 Merge pull request #20 from ipfs/feat/strofbase
Implement StringOfBase function
2017-06-19 17:58:20 +02:00
Łukasz Magiera
b42583c8bf Add StringOfBase function 2017-06-19 17:25:51 +02:00
Jeromy
45ce89d41a gx publish 0.7.14 2017-06-13 11:33:50 -07:00
Jeromy Johnson
a20203df14 Merge pull request #18 from magik6k/patch-1
Add git-raw code
2017-06-09 11:55:35 -07:00
Łukasz Magiera
d4ebfff511 Add git-raw code 2017-06-07 20:08:56 +02:00
Jeromy
e310d23e36 gx publish 0.7.13 2017-05-01 17:37:29 -07:00
Jeromy
7e7f6811b4 gx publish 0.7.12 2017-03-24 18:53:57 -07:00
Jakub Sztandera
daa83089b3 Merge pull request #17 from ipfs/doc-improvements
Readme/golint: improve readme with gx instructions. Make golint happy.
2017-03-24 17:10:15 +01:00
Hector Sanjuan
7ef01bd895 Readme/golint: improve readme with gx instructions. Make golint happy.
License: MIT
Signed-off-by: Hector Sanjuan <hector@protocol.ai>
2017-03-24 15:12:07 +01:00
Jeromy
05ed406b31 add correct eth codes 2017-03-20 18:00:06 -07:00
Jeromy
92cac2f002 add an example to the tests to play around with the feature 2017-03-18 19:17:44 -07:00
Jeromy
a22bf1e2bf gx publish 0.7.11 2017-02-05 23:52:16 -08:00
Jeromy
7bcaf9264a do cid json handling in this package 2017-02-05 23:52:06 -08:00
Jeromy
c1db98f9ae gx publish 0.7.10 2017-02-02 19:05:37 -08:00
Jeromy
40c5ff1bfe gx publish 0.7.9 2017-02-02 18:53:37 -08:00
Jeromy
ebda3c73ae actually update multihash package 2017-02-02 18:53:32 -08:00
Jeromy
03f8d08574 gx publish 0.7.8 2017-02-02 18:39:03 -08:00
Jeromy
4ff5ee2b99 Update go-multibase, multihash. add test for hex 2017-02-02 18:39:00 -08:00
Jeromy Johnson
2b1b4a8339 Merge pull request #13 from ipfs/feat/gx-update-tree-hnwcen
gx update-tree go-cid go-libp2p-interface-pnet
2016-11-25 10:30:18 -08:00
12 changed files with 846 additions and 60 deletions

View File

@@ -1 +1 @@
0.7.7: QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD
0.8.0: QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb

View File

@@ -1,20 +1,19 @@
os:
- linux
- osx
sudo: false
language: go
go:
- 1.7
install: true
before_install:
- make deps
- 'tip'
install:
- go get github.com/whyrusleeping/gx
- go get github.com/whyrusleeping/gx-go
- gx install --global
script:
- go vet
- $GOPATH/bin/goveralls -service="travis-ci"
- gx test -v -race -coverprofile=coverage.txt -covermode=atomic .
after_success:
- bash <(curl -s https://codecov.io/bash)
cache:
directories:
@@ -22,3 +21,4 @@ cache:
notifications:
email: false

View File

@@ -4,13 +4,15 @@ go-cid
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
[![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
[![GoDoc](https://godoc.org/github.com/ipfs/go-cid?status.svg)](https://godoc.org/github.com/ipfs/go-cid)
[![Coverage Status](https://coveralls.io/repos/github/ipfs/go-cid/badge.svg?branch=master)](https://coveralls.io/github/ipfs/go-cid?branch=master)
[![Travis CI](https://travis-ci.org/ipfs/go-cid.svg?branch=master)](https://travis-ci.org/ipfs/go-cid)
> A package to handle content IDs in go.
> A package to handle content IDs in Go.
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.
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.
## Table of Contents
@@ -23,20 +25,46 @@ It is used in go-ipfs and related packages to refer to a typed hunk of data.
## Install
`go-cid` is a standard Go module which can be installed with:
```sh
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
### Using Gx and Gx-go
This module is packaged with [Gx](https://github.com/whyrusleeping/gx). In order to use it in your own project it is recommended that you:
```sh
go get -u github.com/whyrusleeping/gx
go get -u github.com/whyrusleeping/gx-go
cd <your-project-repository>
gx init
gx import github.com/ipfs/go-cid
gx install --global
gx-go --rewrite
```
Please check [Gx](https://github.com/whyrusleeping/gx) and [Gx-go](https://github.com/whyrusleeping/gx-go) documentation for more information.
### Running tests
Before running tests, please run:
```sh
make deps
```
## Examples
This will make sure that dependencies are rewritten to known working versions.
To use CIDs, import it in your code like:
```go
import "github.com/ipfs/go-cid"
```
### Examples
Then, depending on how you want to use it, something like one of the following examples should work:
#### Parsing string input from users
### Parsing string input from users
```go
// Create a cid from a marshaled string
c, err := cid.Decode("zdvgqEMYmNeH5fKciougvQcfzMcNjF3Z1tPouJ8C7pc3pe63k")
@@ -45,7 +73,8 @@ if err != nil {...}
fmt.Println("Got CID: ", c)
```
### Creating a CID from scratch
#### Creating a CID from scratch
```go
// Create a cid manually by specifying the 'prefix' parameters
pref := cid.Prefix{
@@ -62,7 +91,8 @@ if err != nil {...}
fmt.Println("Created CID: ", c)
```
### Check if two CIDs match
#### Check if two CIDs match
```go
// To test if two cid's are equivalent, be sure to use the 'Equals' method:
if c1.Equals(c2) {
@@ -70,7 +100,8 @@ if c1.Equals(c2) {
}
```
### Check if some data matches a given CID
#### Check if some data matches a given CID
```go
// To check if some data matches a given cid,
// Get your CIDs prefix, and use that to sum the data in question:

74
builder.go Normal file
View File

@@ -0,0 +1,74 @@
package cid
import (
mh "github.com/multiformats/go-multihash"
)
type Builder interface {
Sum(data []byte) (*Cid, error)
GetCodec() uint64
WithCodec(uint64) Builder
}
type V0Builder struct{}
type V1Builder struct {
Codec uint64
MhType uint64
MhLength int // MhLength <= 0 means the default length
}
func (p Prefix) GetCodec() uint64 {
return p.Codec
}
func (p Prefix) WithCodec(c uint64) Builder {
if c == p.Codec {
return p
}
p.Codec = c
if c != DagProtobuf {
p.Version = 1
}
return p
}
func (p V0Builder) Sum(data []byte) (*Cid, error) {
hash, err := mh.Sum(data, mh.SHA2_256, -1)
if err != nil {
return nil, err
}
return NewCidV0(hash), nil
}
func (p V0Builder) GetCodec() uint64 {
return DagProtobuf
}
func (p V0Builder) WithCodec(c uint64) Builder {
if c == DagProtobuf {
return p
}
return V1Builder{Codec: c, MhType: mh.SHA2_256}
}
func (p V1Builder) Sum(data []byte) (*Cid, error) {
mhLen := p.MhLength
if mhLen <= 0 {
mhLen = -1
}
hash, err := mh.Sum(data, p.MhType, mhLen)
if err != nil {
return nil, err
}
return NewCidV1(p.Codec, hash), nil
}
func (p V1Builder) GetCodec() uint64 {
return p.Codec
}
func (p V1Builder) WithCodec(c uint64) Builder {
p.Codec = c
return p
}

92
builder_test.go Normal file
View File

@@ -0,0 +1,92 @@
package cid
import (
"testing"
mh "github.com/multiformats/go-multihash"
)
func TestV0Builder(t *testing.T) {
data := []byte("this is some test content")
// Construct c1
format := V0Builder{}
c1, err := format.Sum(data)
if err != nil {
t.Fatal(err)
}
// Construct c2
hash, err := mh.Sum(data, mh.SHA2_256, -1)
if err != nil {
t.Fatal(err)
}
c2 := NewCidV0(hash)
if !c1.Equals(c2) {
t.Fatal("cids mismatch")
}
if c1.Prefix() != c2.Prefix() {
t.Fatal("prefixes mismatch")
}
}
func TestV1Builder(t *testing.T) {
data := []byte("this is some test content")
// Construct c1
format := V1Builder{Codec: DagCBOR, MhType: mh.SHA2_256}
c1, err := format.Sum(data)
if err != nil {
t.Fatal(err)
}
// Construct c2
hash, err := mh.Sum(data, mh.SHA2_256, -1)
if err != nil {
t.Fatal(err)
}
c2 := NewCidV1(DagCBOR, hash)
if !c1.Equals(c2) {
t.Fatal("cids mismatch")
}
if c1.Prefix() != c2.Prefix() {
t.Fatal("prefixes mismatch")
}
}
func TestCodecChange(t *testing.T) {
t.Run("Prefix-CidV0", func(t *testing.T) {
p := Prefix{Version: 0, Codec: DagProtobuf, MhType: mh.SHA2_256, MhLength: mh.DefaultLengths[mh.SHA2_256]}
testCodecChange(t, p)
})
t.Run("Prefix-CidV1", func(t *testing.T) {
p := Prefix{Version: 1, Codec: DagProtobuf, MhType: mh.SHA2_256, MhLength: mh.DefaultLengths[mh.SHA2_256]}
testCodecChange(t, p)
})
t.Run("V0Builder", func(t *testing.T) {
testCodecChange(t, V0Builder{})
})
t.Run("V1Builder", func(t *testing.T) {
testCodecChange(t, V1Builder{Codec: DagProtobuf, MhType: mh.SHA2_256})
})
}
func testCodecChange(t *testing.T, b Builder) {
data := []byte("this is some test content")
if b.GetCodec() != DagProtobuf {
t.Fatal("original builder not using Protobuf codec")
}
b = b.WithCodec(Raw)
c, err := b.Sum(data)
if err != nil {
t.Fatal(err)
}
if c.Type() != Raw {
t.Fatal("new cid codec did not change to Raw")
}
}

251
cid.go
View File

@@ -1,8 +1,28 @@
// Package cid implements the Content-IDentifiers specification
// (https://github.com/ipld/cid) in Go. CIDs are
// self-describing content-addressed identifiers useful for
// distributed information systems. CIDs are used in the IPFS
// (https://ipfs.io) project ecosystem.
//
// CIDs have two major versions. A CIDv0 corresponds to a multihash of type
// DagProtobuf, is deprecated and exists for compatibility reasons. Usually,
// CIDv1 should be used.
//
// A CIDv1 has four parts:
//
// <cidv1> ::= <multibase-prefix><cid-version><multicodec-packed-content-type><multihash-content-address>
//
// As shown above, the CID implementation relies heavily on Multiformats,
// particularly Multibase
// (https://github.com/multiformats/go-multibase), Multicodec
// (https://github.com/multiformats/multicodec) and Multihash
// implementations (https://github.com/multiformats/go-multihash).
package cid
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"strings"
@@ -11,44 +31,136 @@ import (
mh "github.com/multiformats/go-multihash"
)
// UnsupportedVersionString just holds an error message
const UnsupportedVersionString = "<unsupported cid version>"
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")
// ErrInvalidEncoding means that selected encoding is not supported
// by this Cid version
ErrInvalidEncoding = errors.New("invalid base encoding")
)
// These are multicodec-packed content types. The should match
// the codes described in the authoritative document:
// https://github.com/multiformats/multicodec/blob/master/table.csv
const (
Raw = 0x55
DagProtobuf = 0x70
DagCBOR = 0x71
EthereumBlock = 0x90
EthereumTx = 0x91
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
)
func NewCidV0(h mh.Multihash) *Cid {
// Codecs maps the name of a codec to its type
var Codecs = map[string]uint64{
"v0": DagProtobuf,
"raw": Raw,
"protobuf": DagProtobuf,
"cbor": DagCBOR,
"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,
}
// 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",
}
// 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.
func NewCidV0(mhash mh.Multihash) *Cid {
return &Cid{
version: 0,
codec: DagProtobuf,
hash: h,
hash: mhash,
}
}
func NewCidV1(c uint64, h mh.Multihash) *Cid {
// NewCidV1 returns a new Cid using the given multicodec-packed
// content type.
func NewCidV1(codecType uint64, mhash mh.Multihash) *Cid {
return &Cid{
version: 1,
codec: c,
hash: h,
codec: codecType,
hash: mhash,
}
}
// Cid represents a self-describing content adressed
// identifier. It is formed by a Version, a Codec (which indicates
// a multicodec-packed content type) and a Multihash.
type Cid struct {
version uint64
codec uint64
hash mh.Multihash
}
// Parse is a short-hand function to perform Decode, Cast etc... on
// a generic interface{} type.
func Parse(v interface{}) (*Cid, error) {
switch v2 := v.(type) {
case string:
@@ -67,9 +179,21 @@ func Parse(v interface{}) (*Cid, error) {
}
}
// Decode parses a Cid-encoded string and returns a Cid object.
// For CidV1, a Cid-encoded string is primarily a multibase string:
//
// <multibase-type-code><base-encoded-string>
//
// The base-encoded string represents a:
//
// <version><codec-type><multihash>
//
// Decode will also detect and parse CidV0 strings. Strings
// starting with "Qm" are considered CidV0 and treated directly
// as B58-encoded multihashes.
func Decode(v string) (*Cid, error) {
if len(v) < 2 {
return nil, fmt.Errorf("cid too short")
return nil, ErrCidTooShort
}
if len(v) == 46 && v[:2] == "Qm" {
@@ -89,11 +213,27 @@ func Decode(v string) (*Cid, error) {
return Cast(data)
}
var (
ErrVarintBuffSmall = errors.New("reading varint: buffer to small")
ErrVarintTooBig = errors.New("reading varint: varint bigger than 64bits" +
" and not supported")
)
// Extract the encoding from a Cid. If Decode on the same string did
// not return an error neither will this function.
func ExtractEncoding(v string) (mbase.Encoding, error) {
if len(v) < 2 {
return -1, ErrCidTooShort
}
if len(v) == 46 && v[:2] == "Qm" {
return mbase.Base58BTC, nil
}
encoding := mbase.Encoding(v[0])
// check encoding is valid
_, err := mbase.NewEncoder(encoding)
if err != nil {
return -1, err
}
return encoding, nil
}
func uvError(read int) error {
switch {
@@ -106,6 +246,17 @@ func uvError(read int) error {
}
}
// Cast takes a Cid data slice, parses it and returns a Cid.
// For CidV1, the data buffer is in the form:
//
// <version><codec-type><multihash>
//
// CidV0 are also supported. In particular, data buffers starting
// with length 34 bytes, which starts with bytes [18,32...] are considered
// binary multihashes.
//
// 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) {
if len(data) == 34 && data[0] == 18 && data[1] == 32 {
h, err := mh.Cast(data)
@@ -147,10 +298,14 @@ func Cast(data []byte) (*Cid, error) {
}, nil
}
// Type returns the multicodec-packed content type of a Cid.
func (c *Cid) Type() uint64 {
return c.codec
}
// String returns the default string representation of a
// Cid. Currently, Base58 is used as the encoding for the
// multibase string.
func (c *Cid) String() string {
switch c.version {
case 0:
@@ -167,10 +322,30 @@ func (c *Cid) String() string {
}
}
// String returns the string representation of a Cid
// encoded is selected base
func (c *Cid) StringOfBase(base mbase.Encoding) (string, error) {
switch c.version {
case 0:
if base != mbase.Base58BTC {
return "", ErrInvalidEncoding
}
return c.hash.B58String(), nil
case 1:
return mbase.Encode(base, c.bytesV1())
default:
panic("not possible to reach this point")
}
}
// Hash returns the multihash contained by a Cid.
func (c *Cid) Hash() mh.Multihash {
return c.hash
}
// Bytes returns the byte representation of a Cid.
// The output of bytes can be parsed back into a Cid
// with Cast().
func (c *Cid) Bytes() []byte {
switch c.version {
case 0:
@@ -199,17 +374,33 @@ func (c *Cid) bytesV1() []byte {
return buf[:n+len(c.hash)]
}
// Equals checks that two Cids are the same.
// In order for two Cids to be considered equal, the
// Version, the Codec and the Multihash must match.
func (c *Cid) Equals(o *Cid) bool {
return c.codec == o.codec &&
c.version == o.version &&
bytes.Equal(c.hash, o.hash)
}
// UnmarshalJSON parses the JSON representation of a Cid.
func (c *Cid) UnmarshalJSON(b []byte) error {
if len(b) < 2 {
return fmt.Errorf("invalid cid json blob")
}
out, err := Decode(string(b[1 : len(b)-1]))
obj := struct {
CidTarget string `json:"/"`
}{}
err := json.Unmarshal(b, &obj)
if err != nil {
return err
}
if obj.CidTarget == "" {
return fmt.Errorf("cid was incorrectly formatted")
}
out, err := Decode(obj.CidTarget)
if err != nil {
return err
}
@@ -220,20 +411,30 @@ func (c *Cid) UnmarshalJSON(b []byte) error {
return nil
}
func (c *Cid) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("\"%s\"", c.String())), nil
// MarshalJSON procudes a JSON representation of a Cid, which looks as follows:
//
// { "/": "<cid-string>" }
//
// Note that this formatting comes from the IPLD specification
// (https://github.com/ipld/specs/tree/master/ipld)
func (c Cid) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("{\"/\":\"%s\"}", c.String())), nil
}
// KeyString casts the result of cid.Bytes() as a string, and returns it.
func (c *Cid) KeyString() string {
return string(c.Bytes())
}
// Loggable returns a Loggable (as defined by
// https://godoc.org/github.com/ipfs/go-log).
func (c *Cid) Loggable() map[string]interface{} {
return map[string]interface{}{
"cid": c,
}
}
// Prefix builds and returns a Prefix out of a Cid.
func (c *Cid) Prefix() Prefix {
dec, _ := mh.Decode(c.hash) // assuming we got a valid multiaddr, this will not error
return Prefix{
@@ -244,14 +445,21 @@ func (c *Cid) Prefix() Prefix {
}
}
// Prefix represents all the metadata of a cid, minus any actual content information
// 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
// any actual content information.
// NOTE: The use -1 in MhLength to mean default length is deprecated,
// use the V0Builder or V1Builder structures instead
type Prefix struct {
Version uint64
Codec uint64
MhType int
MhType uint64
MhLength int
}
// Sum uses the information in a prefix to perform a multihash.Sum()
// and return a newly constructed Cid with the resulting multihash.
func (p Prefix) Sum(data []byte) (*Cid, error) {
hash, err := mh.Sum(data, p.MhType, p.MhLength)
if err != nil {
@@ -268,6 +476,9 @@ func (p Prefix) Sum(data []byte) (*Cid, error) {
}
}
// Bytes returns a byte representation of a Prefix. It looks like:
//
// <version><codec><mh-type><mh-length>
func (p Prefix) Bytes() []byte {
buf := make([]byte, 4*binary.MaxVarintLen64)
n := binary.PutUvarint(buf, p.Version)
@@ -277,6 +488,8 @@ func (p Prefix) Bytes() []byte {
return buf[:n]
}
// PrefixFromBytes parses a Prefix-byte representation onto a
// Prefix.
func PrefixFromBytes(buf []byte) (Prefix, error) {
r := bytes.NewReader(buf)
vers, err := binary.ReadUvarint(r)
@@ -302,7 +515,7 @@ func PrefixFromBytes(buf []byte) (Prefix, error) {
return Prefix{
Version: vers,
Codec: codec,
MhType: int(mhtype),
MhType: mhtype,
MhLength: int(mhlen),
}, nil
}

View File

@@ -2,14 +2,41 @@ package cid
import (
"bytes"
"encoding/json"
"fmt"
"math/rand"
"strings"
"testing"
mbase "github.com/multiformats/go-multibase"
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",
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",
}
func assertEqual(t *testing.T, a, b *Cid) {
if a.codec != b.codec {
t.Fatal("mismatch on type")
@@ -24,6 +51,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 TestBasicMarshaling(t *testing.T) {
h, err := mh.Sum([]byte("TEST"), mh.SHA3, 4)
if err != nil {
@@ -54,6 +101,60 @@ func TestBasicMarshaling(t *testing.T) {
assertEqual(t, cid, out2)
}
func TestBasesMarshaling(t *testing.T) {
h, err := mh.Sum([]byte("TEST"), mh.SHA3, 4)
if err != nil {
t.Fatal(err)
}
cid := &Cid{
codec: 7,
version: 1,
hash: h,
}
data := cid.Bytes()
out, err := Cast(data)
if err != nil {
t.Fatal(err)
}
assertEqual(t, cid, out)
testBases := []mbase.Encoding{
mbase.Base16,
mbase.Base32,
mbase.Base32hex,
mbase.Base32pad,
mbase.Base32hexPad,
mbase.Base58BTC,
mbase.Base58Flickr,
mbase.Base64pad,
mbase.Base64urlPad,
mbase.Base64url,
mbase.Base64,
}
for _, b := range testBases {
s, err := cid.StringOfBase(b)
if err != nil {
t.Fatal(err)
}
if s[0] != byte(b) {
t.Fatal("Invalid multibase header")
}
out2, err := Decode(s)
if err != nil {
t.Fatal(err)
}
assertEqual(t, cid, out2)
}
}
func TestEmptyString(t *testing.T) {
_, err := Decode("")
if err == nil {
@@ -90,6 +191,64 @@ func TestV0ErrorCases(t *testing.T) {
}
}
func TestNewPrefixV1(t *testing.T) {
data := []byte("this is some test content")
// Construct c1
prefix := NewPrefixV1(DagCBOR, mh.SHA2_256)
c1, err := prefix.Sum(data)
if err != nil {
t.Fatal(err)
}
if c1.Prefix() != prefix {
t.Fatal("prefix not preserved")
}
// Construct c2
hash, err := mh.Sum(data, mh.SHA2_256, -1)
if err != nil {
t.Fatal(err)
}
c2 := NewCidV1(DagCBOR, hash)
if !c1.Equals(c2) {
t.Fatal("cids mismatch")
}
if c1.Prefix() != c2.Prefix() {
t.Fatal("prefixes mismatch")
}
}
func TestNewPrefixV0(t *testing.T) {
data := []byte("this is some test content")
// Construct c1
prefix := NewPrefixV0(mh.SHA2_256)
c1, err := prefix.Sum(data)
if err != nil {
t.Fatal(err)
}
if c1.Prefix() != prefix {
t.Fatal("prefix not preserved")
}
// Construct c2
hash, err := mh.Sum(data, mh.SHA2_256, -1)
if err != nil {
t.Fatal(err)
}
c2 := NewCidV0(hash)
if !c1.Equals(c2) {
t.Fatal("cids mismatch")
}
if c1.Prefix() != c2.Prefix() {
t.Fatal("prefixes mismatch")
}
}
func TestPrefixRoundtrip(t *testing.T) {
data := []byte("this is some test content")
hash, _ := mh.Sum(data, mh.SHA2_256, -1)
@@ -188,3 +347,84 @@ func TestParse(t *testing.T) {
}
}
}
func TestHexDecode(t *testing.T) {
hexcid := "f015512209d8453505bdc6f269678e16b3e56c2a2948a41f2c792617cc9611ed363c95b63"
c, err := Decode(hexcid)
if err != nil {
t.Fatal(err)
}
if c.String() != "zb2rhhFAEMepUBbGyP1k8tGfz7BSciKXP6GHuUeUsJBaK6cqG" {
t.Fatal("hash value failed to round trip decoding from hex")
}
}
func ExampleDecode() {
encoded := "zb2rhhFAEMepUBbGyP1k8tGfz7BSciKXP6GHuUeUsJBaK6cqG"
c, err := Decode(encoded)
if err != nil {
fmt.Printf("Error: %s", err)
return
}
fmt.Println(c)
// Output: zb2rhhFAEMepUBbGyP1k8tGfz7BSciKXP6GHuUeUsJBaK6cqG
}
func TestFromJson(t *testing.T) {
cval := "zb2rhhFAEMepUBbGyP1k8tGfz7BSciKXP6GHuUeUsJBaK6cqG"
jsoncid := []byte(`{"/":"` + cval + `"}`)
var c Cid
err := json.Unmarshal(jsoncid, &c)
if err != nil {
t.Fatal(err)
}
if c.String() != cval {
t.Fatal("json parsing failed")
}
}
func TestJsonRoundTrip(t *testing.T) {
exp, err := Decode("zb2rhhFAEMepUBbGyP1k8tGfz7BSciKXP6GHuUeUsJBaK6cqG")
if err != nil {
t.Fatal(err)
}
// Verify it works for a *Cid.
enc, err := json.Marshal(exp)
if err != nil {
t.Fatal(err)
}
var actual Cid
err = json.Unmarshal(enc, &actual)
if !exp.Equals(&actual) {
t.Fatal("cids not equal for *Cid")
}
// Verify it works for a Cid.
enc, err = json.Marshal(*exp)
if err != nil {
t.Fatal(err)
}
var actual2 Cid
err = json.Unmarshal(enc, &actual2)
if !exp.Equals(&actual2) {
t.Fatal("cids not equal for Cid")
}
}
func BenchmarkStringV1(b *testing.B) {
data := []byte("this is some test content")
hash, _ := mh.Sum(data, mh.SHA2_256, -1)
cid := NewCidV1(Raw, hash)
b.ResetTimer()
count := 0
for i := 0; i < b.N; i++ {
count += len(cid.String())
}
if count != 49*b.N {
b.FailNow()
}
}

3
codecov.yml Normal file
View File

@@ -0,0 +1,3 @@
coverage:
range: "50...100"
comment: off

28
deprecated.go Normal file
View File

@@ -0,0 +1,28 @@
package cid
import (
mh "github.com/multiformats/go-multihash"
)
// NewPrefixV0 returns a CIDv0 prefix with the specified multihash type.
// DEPRECATED: Use V0Builder
func NewPrefixV0(mhType uint64) Prefix {
return Prefix{
MhType: mhType,
MhLength: mh.DefaultLengths[mhType],
Version: 0,
Codec: DagProtobuf,
}
}
// NewPrefixV1 returns a CIDv1 prefix with the specified codec and multihash
// type.
// DEPRECATED: Use V1Builder
func NewPrefixV1(codecType uint64, mhType uint64) Prefix {
return Prefix{
MhType: mhType,
MhLength: mh.DefaultLengths[mhType],
Version: 1,
Codec: codecType,
}
}

View File

@@ -9,15 +9,15 @@
"gxDependencies": [
{
"author": "whyrusleeping",
"hash": "QmYDds3421prZgqKbLpEK7T9Aa2eVdQ7o3YarX1LVLdP2J",
"hash": "QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8",
"name": "go-multihash",
"version": "1.0.0"
"version": "1.0.8"
},
{
"author": "whyrusleeping",
"hash": "QmUq3H9YpcPphbRj6ct6rBgBE377A8wANP8zPMRqe1WYbf",
"hash": "QmSbvata2WqNkqGtZNg8MR3SKwnB8iQ7vTPJgWqB8bC5kR",
"name": "go-multibase",
"version": "0.2.1"
"version": "0.2.7"
}
],
"gxVersion": "0.8.0",
@@ -25,6 +25,6 @@
"license": "MIT",
"name": "go-cid",
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
"version": "0.7.7"
"version": "0.8.0"
}

17
set.go
View File

@@ -1,39 +1,49 @@
package cid
// Set is a implementation of a set of Cids, that is, a structure
// to which holds a single copy of every Cids that is added to it.
type Set struct {
set map[string]struct{}
}
// NewSet initializes and returns a new Set.
func NewSet() *Set {
return &Set{set: make(map[string]struct{})}
}
// Add puts a Cid in the Set.
func (s *Set) Add(c *Cid) {
s.set[string(c.Bytes())] = struct{}{}
}
// Has returns if the Set contains a given Cid.
func (s *Set) Has(c *Cid) bool {
_, ok := s.set[string(c.Bytes())]
return ok
}
// Remove deletes a Cid from the Set.
func (s *Set) Remove(c *Cid) {
delete(s.set, string(c.Bytes()))
}
// Len returns how many elements the Set has.
func (s *Set) Len() int {
return len(s.set)
}
// Keys returns the Cids in the set.
func (s *Set) Keys() []*Cid {
out := make([]*Cid, 0, len(s.set))
for k, _ := range s.set {
for k := range s.set {
c, _ := Cast([]byte(k))
out = append(out, c)
}
return out
}
// Visit adds a Cid to the set only if it is
// not in it already.
func (s *Set) Visit(c *Cid) bool {
if !s.Has(c) {
s.Add(c)
@@ -43,8 +53,10 @@ func (s *Set) Visit(c *Cid) bool {
return false
}
// ForEach allows to run a custom function on each
// Cid in the set.
func (s *Set) ForEach(f func(c *Cid) error) error {
for cs, _ := range s.set {
for cs := range s.set {
c, _ := Cast([]byte(cs))
err := f(c)
if err != nil {
@@ -53,3 +65,4 @@ func (s *Set) ForEach(f func(c *Cid) error) error {
}
return nil
}

92
set_test.go Normal file
View File

@@ -0,0 +1,92 @@
package cid
import (
"crypto/rand"
"errors"
"testing"
mh "github.com/multiformats/go-multihash"
)
func makeRandomCid(t *testing.T) *Cid {
p := make([]byte, 256)
_, err := rand.Read(p)
if err != nil {
t.Fatal(err)
}
h, err := mh.Sum(p, mh.SHA3, 4)
if err != nil {
t.Fatal(err)
}
cid := &Cid{
codec: 7,
version: 1,
hash: h,
}
return cid
}
func TestSet(t *testing.T) {
cid := makeRandomCid(t)
cid2 := makeRandomCid(t)
s := NewSet()
s.Add(cid)
if !s.Has(cid) {
t.Error("should have the CID")
}
if s.Len() != 1 {
t.Error("should report 1 element")
}
keys := s.Keys()
if len(keys) != 1 || !keys[0].Equals(cid) {
t.Error("key should correspond to Cid")
}
if s.Visit(cid) {
t.Error("visit should return false")
}
foreach := []*Cid{}
foreachF := func(c *Cid) error {
foreach = append(foreach, c)
return nil
}
if err := s.ForEach(foreachF); err != nil {
t.Error(err)
}
if len(foreach) != 1 {
t.Error("ForEach should have visited 1 element")
}
foreachErr := func(c *Cid) error {
return errors.New("test")
}
if err := s.ForEach(foreachErr); err == nil {
t.Error("Should have returned an error")
}
if !s.Visit(cid2) {
t.Error("should have visited a new Cid")
}
if s.Len() != 2 {
t.Error("len should be 2 now")
}
s.Remove(cid2)
if s.Len() != 1 {
t.Error("len should be 1 now")
}
}