cid.go 17.4 KB
Newer Older
1
// Package cid implements the Content-IDentifiers specification
tavit ohanian's avatar
tavit ohanian committed
2
// (https://gitlab.dms3.io/dms3/public/dms3ld/cid) in Go. CIDs are
3
// self-describing content-addressed identifiers useful for
tavit ohanian's avatar
tavit ohanian committed
4 5
// distributed information systems. CIDs are used in the DMS3
// (https://dms3.io) project ecosystem.
6 7 8 9 10 11 12 13 14 15 16 17 18 19
//
// 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).
Jeromy's avatar
Jeromy committed
20 21 22
package cid

import (
23
	"bytes"
24
	"encoding"
Jeromy's avatar
Jeromy committed
25
	"encoding/json"
Jakub Sztandera's avatar
Jakub Sztandera committed
26
	"errors"
Jeromy's avatar
Jeromy committed
27
	"fmt"
Jeromy's avatar
Jeromy committed
28
	"io"
29
	"strings"
Jeromy's avatar
Jeromy committed
30

Jeromy's avatar
Jeromy committed
31
	mbase "github.com/multiformats/go-multibase"
Jeromy's avatar
Jeromy committed
32
	mh "github.com/multiformats/go-multihash"
Steven Allen's avatar
Steven Allen committed
33
	varint "github.com/multiformats/go-varint"
Jeromy's avatar
Jeromy committed
34 35
)

36
// UnsupportedVersionString just holds an error message
Jeromy's avatar
Jeromy committed
37 38
const UnsupportedVersionString = "<unsupported cid version>"

39 40 41 42
var (
	// ErrCidTooShort means that the cid passed to decode was not long
	// enough to be a valid Cid
	ErrCidTooShort = errors.New("cid too short")
Łukasz Magiera's avatar
Łukasz Magiera committed
43 44 45 46

	// ErrInvalidEncoding means that selected encoding is not supported
	// by this Cid version
	ErrInvalidEncoding = errors.New("invalid base encoding")
47 48
)

49 50 51
// 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
52
const (
Jeromy's avatar
Jeromy committed
53 54 55 56
	Raw = 0x55

	DagProtobuf = 0x70
	DagCBOR     = 0x71
tavit ohanian's avatar
tavit ohanian committed
57
	P2pKey      = 0x72
Jeromy's avatar
Jeromy committed
58

Łukasz Magiera's avatar
Łukasz Magiera committed
59 60
	GitRaw = 0x78

Alex Good's avatar
Alex Good committed
61
	DagJOSE               = 0x85
Rod Vagg's avatar
Rod Vagg committed
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
	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
81 82
)

Herman Junge's avatar
Herman Junge committed
83
// Codecs maps the name of a codec to its type
Herman Junge's avatar
Herman Junge committed
84
var Codecs = map[string]uint64{
Rod Vagg's avatar
Rod Vagg committed
85 86 87 88
	"v0":                      DagProtobuf,
	"raw":                     Raw,
	"protobuf":                DagProtobuf,
	"cbor":                    DagCBOR,
tavit ohanian's avatar
tavit ohanian committed
89
	"p2p-key":                 P2pKey,
Rod Vagg's avatar
Rod Vagg committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
	"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,
Alex Good's avatar
Alex Good committed
110
	"dag-jose":                DagJOSE,
Herman Junge's avatar
Herman Junge committed
111 112
}

113 114
// CodecToStr maps the numeric codec to its name
var CodecToStr = map[uint64]string{
Rod Vagg's avatar
Rod Vagg committed
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
	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",
Alex Good's avatar
Alex Good committed
138
	DagJOSE:               "dag-jose",
139 140
}

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
// 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
}

156
// NewCidV0 returns a Cid-wrapped multihash.
tavit ohanian's avatar
tavit ohanian committed
157 158
// They exist to allow DMS3 to work with Cids while keeping
// compatibility with the plain-multihash format used used in DMS3.
159
// NewCidV1 should be used preferentially.
160 161
//
// Panics if the multihash isn't sha2-256.
162
func NewCidV0(mhash mh.Multihash) Cid {
163
	c, err := tryNewCidV0(mhash)
164 165 166
	if err != nil {
		panic(err)
	}
167
	return c
168 169
}

170 171
// NewCidV1 returns a new Cid using the given multicodec-packed
// content type.
172 173
//
// Panics if the multihash is invalid.
174 175 176
func NewCidV1(codecType uint64, mhash mh.Multihash) Cid {
	hashlen := len(mhash)
	// two 8 bytes (max) numbers plus hash
Steven Allen's avatar
Steven Allen committed
177 178 179
	buf := make([]byte, 1+varint.UvarintSize(codecType)+hashlen)
	n := varint.PutUvarint(buf, 1)
	n += varint.PutUvarint(buf[n:], codecType)
180 181 182
	cn := copy(buf[n:], mhash)
	if cn != hashlen {
		panic("copy hash length is inconsistent")
183
	}
184

185
	return Cid{string(buf[:n+hashlen])}
186 187
}

188 189 190 191 192
var _ encoding.BinaryMarshaler = Cid{}
var _ encoding.BinaryUnmarshaler = (*Cid)(nil)
var _ encoding.TextMarshaler = Cid{}
var _ encoding.TextUnmarshaler = (*Cid)(nil)

Madper Xie's avatar
Madper Xie committed
193
// Cid represents a self-describing content addressed
194 195
// identifier. It is formed by a Version, a Codec (which indicates
// a multicodec-packed content type) and a Multihash.
196
type Cid struct{ str string }
197

198 199 200
// Undef can be used to represent a nil or undefined Cid, using Cid{}
// directly is also acceptable.
var Undef = Cid{}
Jeromy's avatar
Jeromy committed
201

202 203
// Defined returns true if a Cid is defined
// Calling any other methods on an undefined Cid will result in
Kevin Atkinson's avatar
Kevin Atkinson committed
204
// undefined behavior.
205 206
func (c Cid) Defined() bool {
	return c.str != ""
Kevin Atkinson's avatar
Kevin Atkinson committed
207 208
}

209 210
// Parse is a short-hand function to perform Decode, Cast etc... on
// a generic interface{} type.
211
func Parse(v interface{}) (Cid, error) {
212 213
	switch v2 := v.(type) {
	case string:
tavit ohanian's avatar
tavit ohanian committed
214 215
		if strings.Contains(v2, "/dms3/") {
			return Decode(strings.Split(v2, "/dms3/")[1])
216 217 218 219 220
		}
		return Decode(v2)
	case []byte:
		return Cast(v2)
	case mh.Multihash:
221
		return tryNewCidV0(v2)
222
	case Cid:
223 224
		return v2, nil
	default:
225
		return Undef, fmt.Errorf("can't parse %+v as Cid", v2)
226 227 228
	}
}

229 230 231 232 233 234 235 236 237 238 239 240
// 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.
241
func Decode(v string) (Cid, error) {
Jeromy's avatar
Jeromy committed
242
	if len(v) < 2 {
243
		return Undef, ErrCidTooShort
Jeromy's avatar
Jeromy committed
244 245
	}

Jeromy's avatar
Jeromy committed
246 247 248
	if len(v) == 46 && v[:2] == "Qm" {
		hash, err := mh.FromB58String(v)
		if err != nil {
249
			return Undef, err
Jeromy's avatar
Jeromy committed
250 251
		}

252
		return tryNewCidV0(hash)
Jeromy's avatar
Jeromy committed
253 254
	}

Jeromy's avatar
Jeromy committed
255 256
	_, data, err := mbase.Decode(v)
	if err != nil {
257
		return Undef, err
Jeromy's avatar
Jeromy committed
258 259 260 261 262
	}

	return Cast(data)
}

263 264 265 266 267
// 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
Jeromy's avatar
Jeromy committed
268 269
	}

270 271 272 273 274 275 276 277 278 279 280
	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
	}
281

282
	return encoding, nil
Jeromy's avatar
Jeromy committed
283 284
}

285 286 287 288 289 290 291 292 293 294 295
// 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().
296
func Cast(data []byte) (Cid, error) {
297 298
	nr, c, err := CidFromBytes(data)
	if err != nil {
299
		return Undef, err
Jakub Sztandera's avatar
Jakub Sztandera committed
300
	}
Jeromy's avatar
Jeromy committed
301

302 303
	if nr != len(data) {
		return Undef, fmt.Errorf("trailing bytes in data buffer passed to cid Cast")
Jeromy's avatar
Jeromy committed
304 305
	}

306
	return c, nil
Jeromy's avatar
Jeromy committed
307 308
}

309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
// UnmarshalBinary is equivalent to Cast(). It implements the
// encoding.BinaryUnmarshaler interface.
func (c *Cid) UnmarshalBinary(data []byte) error {
	casted, err := Cast(data)
	if err != nil {
		return err
	}
	c.str = casted.str
	return nil
}

// UnmarshalText is equivalent to Decode(). It implements the
// encoding.TextUnmarshaler interface.
func (c *Cid) UnmarshalText(text []byte) error {
	decodedCid, err := Decode(string(text))
	if err != nil {
		return err
	}
	c.str = decodedCid.str
	return nil
}

331 332
// Version returns the Cid version.
func (c Cid) Version() uint64 {
333
	if len(c.str) == 34 && c.str[0] == 18 && c.str[1] == 32 {
334 335 336
		return 0
	}
	return 1
337 338
}

339
// Type returns the multicodec-packed content type of a Cid.
340
func (c Cid) Type() uint64 {
341 342 343
	if c.Version() == 0 {
		return DagProtobuf
	}
Steven Allen's avatar
Steven Allen committed
344 345
	_, n, _ := uvarint(c.str)
	codec, _, _ := uvarint(c.str[n:])
346
	return codec
347 348
}

349
// String returns the default string representation of a
350 351
// Cid. Currently, Base32 is used for CIDV1 as the encoding for the
// multibase string, Base58 is used for CIDV0.
352
func (c Cid) String() string {
353
	switch c.Version() {
Jeromy's avatar
Jeromy committed
354
	case 0:
355
		return c.Hash().B58String()
Jeromy's avatar
Jeromy committed
356
	case 1:
Steven Allen's avatar
Steven Allen committed
357
		mbstr, err := mbase.Encode(mbase.Base32, c.Bytes())
Jeromy's avatar
Jeromy committed
358 359 360 361 362 363
		if err != nil {
			panic("should not error with hardcoded mbase: " + err.Error())
		}

		return mbstr
	default:
364
		panic("not possible to reach this point")
Jeromy's avatar
Jeromy committed
365 366 367
	}
}

Łukasz Magiera's avatar
Łukasz Magiera committed
368 369
// String returns the string representation of a Cid
// encoded is selected base
370
func (c Cid) StringOfBase(base mbase.Encoding) (string, error) {
371
	switch c.Version() {
Łukasz Magiera's avatar
Łukasz Magiera committed
372 373 374 375
	case 0:
		if base != mbase.Base58BTC {
			return "", ErrInvalidEncoding
		}
376
		return c.Hash().B58String(), nil
Łukasz Magiera's avatar
Łukasz Magiera committed
377
	case 1:
378
		return mbase.Encode(base, c.Bytes())
Łukasz Magiera's avatar
Łukasz Magiera committed
379 380 381 382 383
	default:
		panic("not possible to reach this point")
	}
}

384
// Encode return the string representation of a Cid in a given base
385 386
// when applicable.  Version 0 Cid's are always in Base58 as they do
// not take a multibase prefix.
387
func (c Cid) Encode(base mbase.Encoder) string {
388
	switch c.Version() {
389
	case 0:
390
		return c.Hash().B58String()
391
	case 1:
392
		return base.Encode(c.Bytes())
393 394 395 396 397
	default:
		panic("not possible to reach this point")
	}
}

398
// Hash returns the multihash contained by a Cid.
399
func (c Cid) Hash() mh.Multihash {
400 401
	bytes := c.Bytes()

402
	if c.Version() == 0 {
403
		return mh.Multihash(bytes)
404 405
	}

406
	// skip version length
Steven Allen's avatar
Steven Allen committed
407
	_, n1, _ := varint.FromUvarint(bytes)
408
	// skip codec length
Steven Allen's avatar
Steven Allen committed
409
	_, n2, _ := varint.FromUvarint(bytes[n1:])
410

411
	return mh.Multihash(bytes[n1+n2:])
412 413
}

414 415 416
// Bytes returns the byte representation of a Cid.
// The output of bytes can be parsed back into a Cid
// with Cast().
417
func (c Cid) Bytes() []byte {
418
	return []byte(c.str)
419
}
Jeromy's avatar
Jeromy committed
420

421 422 423 424 425
// 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.)
Jeromy's avatar
Jeromy committed
426 427 428 429
func (c Cid) ByteLen() int {
	return len(c.str)
}

430
// WriteBytes writes the CID bytes to the given writer.
431 432 433
// This method works without incurring any allocation.
//
// (See also the ByteLen method for other important operations that work without allocation.)
434 435 436 437 438 439 440 441 442
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
Jeromy's avatar
Jeromy committed
443 444
}

445 446 447 448 449 450 451 452 453 454 455 456
// MarshalBinary is equivalent to Bytes(). It implements the
// encoding.BinaryMarshaler interface.
func (c Cid) MarshalBinary() ([]byte, error) {
	return c.Bytes(), nil
}

// MarshalText is equivalent to String(). It implements the
// encoding.TextMarshaler interface.
func (c Cid) MarshalText() ([]byte, error) {
	return []byte(c.String()), nil
}

457 458 459
// 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.
460
func (c Cid) Equals(o Cid) bool {
461
	return c == o
Jeromy's avatar
Jeromy committed
462
}
463

464
// UnmarshalJSON parses the JSON representation of a Cid.
465 466 467 468
func (c *Cid) UnmarshalJSON(b []byte) error {
	if len(b) < 2 {
		return fmt.Errorf("invalid cid json blob")
	}
Jeromy's avatar
Jeromy committed
469 470 471
	obj := struct {
		CidTarget string `json:"/"`
	}{}
472 473
	objptr := &obj
	err := json.Unmarshal(b, &objptr)
Jeromy's avatar
Jeromy committed
474 475 476
	if err != nil {
		return err
	}
477 478 479 480
	if objptr == nil {
		*c = Cid{}
		return nil
	}
Jeromy's avatar
Jeromy committed
481 482 483 484 485 486

	if obj.CidTarget == "" {
		return fmt.Errorf("cid was incorrectly formatted")
	}

	out, err := Decode(obj.CidTarget)
487 488 489 490
	if err != nil {
		return err
	}

491
	*c = out
492

493 494 495
	return nil
}

496 497 498 499
// MarshalJSON procudes a JSON representation of a Cid, which looks as follows:
//
//    { "/": "<cid-string>" }
//
tavit ohanian's avatar
tavit ohanian committed
500 501
// Note that this formatting comes from the DMS3LD specification
// (https://github.com/dms3ld/specs/tree/master/dms3ld)
Fritz Schneider's avatar
Fritz Schneider committed
502
func (c Cid) MarshalJSON() ([]byte, error) {
503 504 505
	if !c.Defined() {
		return []byte("null"), nil
	}
Jeromy's avatar
Jeromy committed
506
	return []byte(fmt.Sprintf("{\"/\":\"%s\"}", c.String())), nil
507
}
Jeromy's avatar
Jeromy committed
508

509
// KeyString returns the binary representation of the Cid as a string
510
func (c Cid) KeyString() string {
511
	return c.str
Jeromy's avatar
Jeromy committed
512
}
Jeromy's avatar
Jeromy committed
513

514
// Loggable returns a Loggable (as defined by
tavit ohanian's avatar
tavit ohanian committed
515
// https://godoc.org/gitlab.dms3.io/dms3/public/go-log).
516
func (c Cid) Loggable() map[string]interface{} {
Jeromy's avatar
Jeromy committed
517 518 519 520
	return map[string]interface{}{
		"cid": c,
	}
}
521

522
// Prefix builds and returns a Prefix out of a Cid.
523
func (c Cid) Prefix() Prefix {
Steven Allen's avatar
Steven Allen committed
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
	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:])

542
	return Prefix{
Steven Allen's avatar
Steven Allen committed
543 544 545 546
		MhType:   mhtype,
		MhLength: int(mhlen),
		Version:  version,
		Codec:    codec,
547 548 549
	}
}

550 551 552 553
// 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.
554
// NOTE: The use -1 in MhLength to mean default length is deprecated,
Kevin Atkinson's avatar
Kevin Atkinson committed
555
//   use the V0Builder or V1Builder structures instead
556 557 558
type Prefix struct {
	Version  uint64
	Codec    uint64
Jeromy's avatar
Jeromy committed
559
	MhType   uint64
560 561 562
	MhLength int
}

563 564
// Sum uses the information in a prefix to perform a multihash.Sum()
// and return a newly constructed Cid with the resulting multihash.
565
func (p Prefix) Sum(data []byte) (Cid, error) {
566 567 568 569 570
	length := p.MhLength
	if p.MhType == mh.ID {
		length = -1
	}

571 572 573 574 575 576
	if p.Version == 0 && (p.MhType != mh.SHA2_256 ||
		(p.MhLength != 32 && p.MhLength != -1)) {

		return Undef, fmt.Errorf("invalid v0 prefix")
	}

577
	hash, err := mh.Sum(data, p.MhType, length)
578
	if err != nil {
579
		return Undef, err
580 581 582 583 584 585 586 587
	}

	switch p.Version {
	case 0:
		return NewCidV0(hash), nil
	case 1:
		return NewCidV1(p.Codec, hash), nil
	default:
588
		return Undef, fmt.Errorf("invalid cid version")
589 590 591
	}
}

592 593 594
// Bytes returns a byte representation of a Prefix. It looks like:
//
//     <version><codec><mh-type><mh-length>
595
func (p Prefix) Bytes() []byte {
Steven Allen's avatar
Steven Allen committed
596 597 598 599 600 601 602 603 604 605 606 607 608 609
	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
610 611
}

612 613
// PrefixFromBytes parses a Prefix-byte representation onto a
// Prefix.
614 615
func PrefixFromBytes(buf []byte) (Prefix, error) {
	r := bytes.NewReader(buf)
Steven Allen's avatar
Steven Allen committed
616
	vers, err := varint.ReadUvarint(r)
617 618 619 620
	if err != nil {
		return Prefix{}, err
	}

Steven Allen's avatar
Steven Allen committed
621
	codec, err := varint.ReadUvarint(r)
622 623 624 625
	if err != nil {
		return Prefix{}, err
	}

Steven Allen's avatar
Steven Allen committed
626
	mhtype, err := varint.ReadUvarint(r)
627 628 629 630
	if err != nil {
		return Prefix{}, err
	}

Steven Allen's avatar
Steven Allen committed
631
	mhlen, err := varint.ReadUvarint(r)
632 633 634 635 636 637 638
	if err != nil {
		return Prefix{}, err
	}

	return Prefix{
		Version:  vers,
		Codec:    codec,
Jeromy's avatar
Jeromy committed
639
		MhType:   mhtype,
640 641 642
		MhLength: int(mhlen),
	}, nil
}
643 644

func CidFromBytes(data []byte) (int, Cid, error) {
Steven Allen's avatar
Steven Allen committed
645
	if len(data) > 2 && data[0] == mh.SHA2_256 && data[1] == 32 {
646 647 648 649 650 651 652 653 654
		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
		}

655
		return 34, Cid{string(h)}, nil
656 657
	}

Steven Allen's avatar
Steven Allen committed
658 659
	vers, n, err := varint.FromUvarint(data)
	if err != nil {
660 661 662 663 664 665 666
		return 0, Undef, err
	}

	if vers != 1 {
		return 0, Undef, fmt.Errorf("expected 1 as the cid version number, got: %d", vers)
	}

Steven Allen's avatar
Steven Allen committed
667 668
	_, cn, err := varint.FromUvarint(data[n:])
	if err != nil {
669 670 671
		return 0, Undef, err
	}

672
	mhnr, _, err := mh.MHFromBytes(data[n+cn:])
673 674 675 676
	if err != nil {
		return 0, Undef, err
	}

677
	l := n + cn + mhnr
678 679 680

	return l, Cid{string(data[0:l])}, nil
}