Compare commits

...

41 Commits

Author SHA1 Message Date
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
Lars Gierth
ca0a568b21 gx publish 0.7.7 2016-11-25 18:47:39 +01:00
Jeromy Johnson
896454b8a7 Merge pull request #12 from ipfs/fix/build-failure
fix build failures i introduced
2016-11-23 10:14:26 -08:00
Jeromy
f623f824db fix build failures i introduced 2016-11-23 10:04:56 -08:00
Jeromy
15876e12dc update readme 2016-11-22 11:56:12 -08:00
Jeromy
18f61fe7b9 add readme and default files 2016-11-21 17:16:16 -08:00
Jeromy Johnson
f68586ac44 Merge pull request #9 from ipfs/fix/raw-id
fix raw codec number to be 0x55
2016-11-21 08:47:31 -08:00
Jeromy
1260e85a2d fix raw codec number to be 0x55 2016-11-21 08:39:15 -08:00
Jakub Sztandera
a0eff8c9d1 Merge pull request #8 from ipfs/feat/v0.7.6
MIT license, and go-cid v0.7.6
2016-11-18 01:07:25 +01:00
Lars Gierth
9c8abab049 go-cid v0.7.6 2016-11-18 00:56:28 +01:00
Lars Gierth
ab63d19c4e Add MIT license 2016-11-18 00:50:33 +01:00
Jeromy Johnson
5ccc98a754 Merge pull request #7 from ipfs/feat/fuzz
Add fuzz tests, fix panic
2016-11-17 10:25:44 -08:00
Jakub Sztandera
ba97b640bd Check length of copy to be extra sure we are copying whole thing 2016-11-17 19:24:12 +01:00
Jakub Sztandera
9116bf8025 Fix lengths in prefix too 2016-11-17 19:16:05 +01:00
Jakub Sztandera
c67fe910f2 Test prefix.Bytes() in fuzz tests 2016-11-17 19:03:01 +01:00
Jakub Sztandera
9c3e314588 Use defined MaxVarintLen64 from stdlib 2016-11-17 19:01:33 +01:00
Jakub Sztandera
5da6d87c58 Add test for max lenght varint 2016-11-17 18:53:33 +01:00
Jakub Sztandera
6ce8a80816 Fix panic when length of varints is greater than 8
It can be 16
2016-11-17 18:53:33 +01:00
Jakub Sztandera
5ec5bbcb48 Improve cid_fuzz.go tests 2016-11-17 18:53:33 +01:00
Jakub Sztandera
8aeb1a44a8 Handle UVarit overflows 2016-11-17 18:53:33 +01:00
Jakub Sztandera
832b6a0170 Add basic fuzz test and basic corpus 2016-11-17 18:53:33 +01:00
Jeromy Johnson
eae3431cc9 Merge pull request #4 from ipfs/feat/parse
Add Parse func accepting various types
2016-11-17 09:35:58 -08:00
Lars Gierth
d0e0822854 Add Parse func accepting various types 2016-11-17 17:25:57 +01:00
Jeromy
bffa0300df gx publish 0.7.5 2016-11-10 10:26:33 -08:00
Jeromy
691f8625be add zcash multicodecs to table 2016-11-10 10:26:18 -08:00
Jeromy
177ab398cc gx publish 0.7.3 2016-10-24 17:31:04 -07:00
Jeromy
02ce4e9b23 fix decoding of empty strings 2016-10-24 17:30:53 -07:00
Jeromy
460554fb6e gx publish 0.7.2 2016-10-24 09:16:05 -07:00
Jeromy
5d17ab8c70 add bitcoin tx value 2016-10-24 09:10:06 -07:00
Jeromy
ed8d621d9a gx publish 0.7.0 2016-10-19 17:54:06 -07:00
Jeromy
9a43493ca7 add codes for bitcoin and ethereum 2016-10-19 17:53:39 -07:00
Jeromy
8677934d48 add small test to fuzz cid creation routines 2016-10-09 12:19:14 -07:00
Jeromy
5f11b062c3 gx publish 0.6.0 2016-10-08 21:18:24 -07:00
Jeromy
961f0fe7a8 add 'Prefix' object and some more helper routines 2016-10-08 21:17:59 -07:00
Jeromy
6c7d9e3de2 gx publish 0.5.2 2016-10-05 12:08:42 -07:00
Jeromy
e5a96152bd gx publish 0.5.1 2016-10-05 11:37:05 -07:00
13 changed files with 477 additions and 18 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
cid-fuzz.zip

View File

@@ -1 +1 @@
0.5.0: QmcW7CcRA5kMdqNBRpif7e8y9yvVRmJG1uurMvea8TY2SM
0.7.10: QmTau856czj6wc5UyKQX2MfBQZ9iCZPsuUsVW2b2pRtLVp

24
.travis.yml Normal file
View File

@@ -0,0 +1,24 @@
os:
- linux
- osx
language: go
go:
- 1.7
install: true
before_install:
- make deps
script:
- go vet
- $GOPATH/bin/goveralls -service="travis-ci"
cache:
directories:
- $GOPATH/src/gx
notifications:
email: false

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Protocol Labs, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

16
Makefile Normal file
View File

@@ -0,0 +1,16 @@
all: deps
gx:
go get github.com/whyrusleeping/gx
go get github.com/whyrusleeping/gx-go
covertools:
go get github.com/mattn/goveralls
go get golang.org/x/tools/cmd/cover
deps: gx covertools
gx --verbose install --global
gx-go rewrite
publish:
gx-go rewrite --undo

94
README.md Normal file
View File

@@ -0,0 +1,94 @@
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)
[![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.
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
- [Install](#install)
- [Usage](#usage)
- [API](#api)
- [Contribute](#contribute)
- [License](#license)
## Install
```sh
make deps
```
## Examples
To use CIDs, import it in your code like:
```go
import "github.com/ipfs/go-cid"
```
Then, depending on how you want to use it, something like one of the following examples should work:
### Parsing string input from users
```go
// Create a cid from a marshaled string
c, err := cid.Decode("zdvgqEMYmNeH5fKciougvQcfzMcNjF3Z1tPouJ8C7pc3pe63k")
if err != nil {...}
fmt.Println("Got CID: ", c)
```
### Creating a CID from scratch
```go
// Create a cid manually by specifying the 'prefix' parameters
pref := cid.Prefix{
Version: 1,
Codec: cid.Raw,
MhType: mh.SHA2_256,
MhLength: -1, // default length
}
// And then feed it some data
c, err := pref.Sum([]byte("Hello World!"))
if err != nil {...}
fmt.Println("Created CID: ", c)
```
### 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) {
fmt.Println("These two refer to the same exact data!")
}
```
### 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:
other, err := c.Prefix().Sum(mydata)
if err != nil {...}
if !c.Equals(other) {
fmt.Println("This data is different.")
}
```
## Contribute
PRs are welcome!
Small note: If editing the Readme, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
## License
MIT © Jeromy Johnson

150
cid.go
View File

@@ -3,25 +3,34 @@ package cid
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"strings"
mh "github.com/jbenet/go-multihash"
mbase "github.com/multiformats/go-multibase"
mh "github.com/multiformats/go-multihash"
)
const UnsupportedVersionString = "<unsupported cid version>"
const (
Protobuf = iota
Raw
JSON
CBOR
Raw = 0x55
DagProtobuf = 0x70
DagCBOR = 0x71
EthereumBlock = 0x90
EthereumTx = 0x91
BitcoinBlock = 0xb0
BitcoinTx = 0xb1
ZcashBlock = 0xc0
ZcashTx = 0xc1
)
func NewCidV0(h mh.Multihash) *Cid {
return &Cid{
version: 0,
codec: Protobuf,
codec: DagProtobuf,
hash: h,
}
}
@@ -40,7 +49,29 @@ type Cid struct {
hash mh.Multihash
}
func Parse(v interface{}) (*Cid, error) {
switch v2 := v.(type) {
case string:
if strings.Contains(v2, "/ipfs/") {
return Decode(strings.Split(v2, "/ipfs/")[1])
}
return Decode(v2)
case []byte:
return Cast(v2)
case mh.Multihash:
return NewCidV0(v2), nil
case *Cid:
return v2, nil
default:
return nil, fmt.Errorf("can't parse %+v as Cid", v2)
}
}
func Decode(v string) (*Cid, error) {
if len(v) < 2 {
return nil, fmt.Errorf("cid too short")
}
if len(v) == 46 && v[:2] == "Qm" {
hash, err := mh.FromB58String(v)
if err != nil {
@@ -58,6 +89,23 @@ 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")
)
func uvError(read int) error {
switch {
case read == 0:
return ErrVarintBuffSmall
case read < 0:
return ErrVarintTooBig
default:
return nil
}
}
func Cast(data []byte) (*Cid, error) {
if len(data) == 34 && data[0] == 18 && data[1] == 32 {
h, err := mh.Cast(data)
@@ -66,18 +114,25 @@ func Cast(data []byte) (*Cid, error) {
}
return &Cid{
codec: Protobuf,
codec: DagProtobuf,
version: 0,
hash: h,
}, nil
}
vers, n := binary.Uvarint(data)
if err := uvError(n); err != nil {
return nil, err
}
if vers != 0 && vers != 1 {
return nil, fmt.Errorf("invalid cid version number: %d", vers)
}
codec, cn := binary.Uvarint(data[n:])
if err := uvError(cn); err != nil {
return nil, err
}
rest := data[n+cn:]
h, err := mh.Cast(rest)
@@ -132,10 +187,14 @@ func (c *Cid) bytesV0() []byte {
}
func (c *Cid) bytesV1() []byte {
buf := make([]byte, 8+len(c.hash))
// two 8 bytes (max) numbers plus hash
buf := make([]byte, 2*binary.MaxVarintLen64+len(c.hash))
n := binary.PutUvarint(buf, c.version)
n += binary.PutUvarint(buf[n:], c.codec)
copy(buf[n:], c.hash)
cn := copy(buf[n:], c.hash)
if cn != len(c.hash) {
panic("copy hash length is inconsistent")
}
return buf[:n+len(c.hash)]
}
@@ -174,3 +233,76 @@ func (c *Cid) Loggable() map[string]interface{} {
"cid": c,
}
}
func (c *Cid) Prefix() Prefix {
dec, _ := mh.Decode(c.hash) // assuming we got a valid multiaddr, this will not error
return Prefix{
MhType: dec.Code,
MhLength: dec.Length,
Version: c.version,
Codec: c.codec,
}
}
// Prefix represents all the metadata of a cid, minus any actual content information
type Prefix struct {
Version uint64
Codec uint64
MhType uint64
MhLength int
}
func (p Prefix) Sum(data []byte) (*Cid, error) {
hash, err := mh.Sum(data, p.MhType, p.MhLength)
if err != nil {
return nil, err
}
switch p.Version {
case 0:
return NewCidV0(hash), nil
case 1:
return NewCidV1(p.Codec, hash), nil
default:
return nil, fmt.Errorf("invalid cid version")
}
}
func (p Prefix) Bytes() []byte {
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]
}
func PrefixFromBytes(buf []byte) (Prefix, error) {
r := bytes.NewReader(buf)
vers, err := binary.ReadUvarint(r)
if err != nil {
return Prefix{}, err
}
codec, err := binary.ReadUvarint(r)
if err != nil {
return Prefix{}, err
}
mhtype, err := binary.ReadUvarint(r)
if err != nil {
return Prefix{}, err
}
mhlen, err := binary.ReadUvarint(r)
if err != nil {
return Prefix{}, err
}
return Prefix{
Version: vers,
Codec: codec,
MhType: mhtype,
MhLength: int(mhlen),
}, nil
}

37
cid_fuzz.go Normal file
View File

@@ -0,0 +1,37 @@
// +build gofuzz
package cid
func Fuzz(data []byte) int {
cid, err := Cast(data)
if err != nil {
return 0
}
_ = cid.Bytes()
_ = cid.String()
p := cid.Prefix()
_ = p.Bytes()
if !cid.Equals(cid) {
panic("inequality")
}
// json loop
json, err := cid.MarshalJSON()
if err != nil {
panic(err.Error())
}
cid2 := &Cid{}
err = cid2.UnmarshalJSON(json)
if err != nil {
panic(err.Error())
}
if !cid.Equals(cid2) {
panic("json loop not equal")
}
return 1
}

View File

@@ -2,9 +2,12 @@ package cid
import (
"bytes"
"fmt"
"math/rand"
"strings"
"testing"
mh "github.com/jbenet/go-multihash"
mh "github.com/multiformats/go-multihash"
)
func assertEqual(t *testing.T, a, b *Cid) {
@@ -51,6 +54,13 @@ func TestBasicMarshaling(t *testing.T) {
assertEqual(t, cid, out2)
}
func TestEmptyString(t *testing.T) {
_, err := Decode("")
if err == nil {
t.Fatal("shouldnt be able to parse an empty cid")
}
}
func TestV0Handling(t *testing.T) {
old := "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n"
@@ -79,3 +89,114 @@ func TestV0ErrorCases(t *testing.T) {
t.Fatal("should have failed to decode that ref")
}
}
func TestPrefixRoundtrip(t *testing.T) {
data := []byte("this is some test content")
hash, _ := mh.Sum(data, mh.SHA2_256, -1)
c := NewCidV1(DagCBOR, hash)
pref := c.Prefix()
c2, err := pref.Sum(data)
if err != nil {
t.Fatal(err)
}
if !c.Equals(c2) {
t.Fatal("output didnt match original")
}
pb := pref.Bytes()
pref2, err := PrefixFromBytes(pb)
if err != nil {
t.Fatal(err)
}
if pref.Version != pref2.Version || pref.Codec != pref2.Codec ||
pref.MhType != pref2.MhType || pref.MhLength != pref2.MhLength {
t.Fatal("input prefix didnt match output")
}
}
func Test16BytesVarint(t *testing.T) {
data := []byte("this is some test content")
hash, _ := mh.Sum(data, mh.SHA2_256, -1)
c := NewCidV1(DagCBOR, hash)
c.codec = 1 << 63
_ = c.Bytes()
}
func TestFuzzCid(t *testing.T) {
buf := make([]byte, 128)
for i := 0; i < 200; i++ {
s := rand.Intn(128)
rand.Read(buf[:s])
_, _ = Cast(buf[:s])
}
}
func TestParse(t *testing.T) {
cid, err := Parse(123)
if err == nil {
t.Fatalf("expected error from Parse()")
}
if !strings.Contains(err.Error(), "can't parse 123 as Cid") {
t.Fatalf("expected int error, got %s", err.Error())
}
theHash := "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n"
h, err := mh.FromB58String(theHash)
if err != nil {
t.Fatal(err)
}
assertions := [][]interface{}{
[]interface{}{NewCidV0(h), theHash},
[]interface{}{NewCidV0(h).Bytes(), theHash},
[]interface{}{h, theHash},
[]interface{}{theHash, theHash},
[]interface{}{"/ipfs/" + theHash, theHash},
[]interface{}{"https://ipfs.io/ipfs/" + theHash, theHash},
[]interface{}{"http://localhost:8080/ipfs/" + theHash, theHash},
}
assert := func(arg interface{}, expected string) error {
cid, err = Parse(arg)
if err != nil {
return err
}
if cid.version != 0 {
return fmt.Errorf("expected version 0, got %s", string(cid.version))
}
actual := cid.Hash().B58String()
if actual != expected {
return fmt.Errorf("expected hash %s, got %s", expected, actual)
}
actual = cid.String()
if actual != expected {
return fmt.Errorf("expected string %s, got %s", expected, actual)
}
return nil
}
for _, args := range assertions {
err := assert(args[0], args[1].(string))
if err != nil {
t.Fatal(err)
}
}
}
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")
}
}

1
fuzz-data/corpus/cid0 Normal file
View File

@@ -0,0 +1 @@
 ëgáD1üüÊe<C38A>-D˜/¹q3ø~å(Ä7`8<38>‡n

1
fuzz-data/corpus/cid1 Normal file
View File

@@ -0,0 +1 @@
q -[<5B>ï<EFBFBD>h<EFBFBD>[<5B><10><>

View File

@@ -9,22 +9,22 @@
"gxDependencies": [
{
"author": "whyrusleeping",
"hash": "QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku",
"hash": "QmbZ6Cee2uHjG7hf19qLHppgKDRtaG4CVtMzdmK9VCVqLu",
"name": "go-multihash",
"version": "0.0.0"
"version": "1.0.2"
},
{
"author": "whyrusleeping",
"hash": "QmYiTi9mKBMjfiup7na7PhJK7QEZPdMTJenLdgFYVQ2NUv",
"hash": "QmcxkxTVuURV2Ptse8TvkqH5BQDwV62X1x19JqqvbBzwUM",
"name": "go-multibase",
"version": "0.2.0"
"version": "0.2.3"
}
],
"gxVersion": "0.8.0",
"language": "go",
"license": "",
"license": "MIT",
"name": "go-cid",
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
"version": "0.5.0"
"version": "0.7.10"
}

13
set.go
View File

@@ -26,7 +26,7 @@ func (s *Set) Len() int {
}
func (s *Set) Keys() []*Cid {
var out []*Cid
out := make([]*Cid, 0, len(s.set))
for k, _ := range s.set {
c, _ := Cast([]byte(k))
out = append(out, c)
@@ -42,3 +42,14 @@ func (s *Set) Visit(c *Cid) bool {
return false
}
func (s *Set) ForEach(f func(c *Cid) error) error {
for cs, _ := range s.set {
c, _ := Cast([]byte(cs))
err := f(c)
if err != nil {
return err
}
}
return nil
}