Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Store multibase prefix with CID #66

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 3 additions & 19 deletions cid-fmt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,27 +133,11 @@ func errorMsg(fmtStr string, a ...interface{}) {
}

func decode(v string) (mb.Encoding, *c.Cid, error) {
if len(v) < 2 {
return 0, nil, c.ErrCidTooShort
}

if len(v) == 46 && v[:2] == "Qm" {
hash, err := mh.FromB58String(v)
if err != nil {
return 0, nil, err
}

return mb.Base58BTC, c.NewCidV0(hash), nil
}

base, data, err := mb.Decode(v)
cid, err := c.Decode(v)
if err != nil {
return 0, nil, err
return -1, cid, err
}

cid, err := c.Cast(data)

return base, cid, err
return cid.Base().Encoding(), cid, err
}

const ERR_STR = "!ERROR!"
Expand Down
76 changes: 58 additions & 18 deletions cid.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import (
mh "github.com/multiformats/go-multihash"
)

// DefaultBase is the default base to use when encoding CidV1
var DefaultBase mbase.Encoding = mbase.Base58BTC

// UnsupportedVersionString just holds an error message
const UnsupportedVersionString = "<unsupported cid version>"

Expand Down Expand Up @@ -134,6 +137,7 @@ var CodecToStr = map[uint64]string{
// NewCidV1 should be used preferentially.
func NewCidV0(mhash mh.Multihash) *Cid {
return &Cid{
base: -1,
version: 0,
codec: DagProtobuf,
hash: mhash,
Expand All @@ -144,6 +148,7 @@ func NewCidV0(mhash mh.Multihash) *Cid {
// content type.
func NewCidV1(codecType uint64, mhash mh.Multihash) *Cid {
return &Cid{
base: -1,
version: 1,
codec: codecType,
hash: mhash,
Expand Down Expand Up @@ -175,6 +180,7 @@ func NewPrefixV1(codecType uint64, mhType uint64) Prefix {
// identifier. It is formed by a Version, a Codec (which indicates
// a multicodec-packed content type) and a Multihash.
type Cid struct {
base mbase.Encoding // -1 if not defined
version uint64
codec uint64
hash mh.Multihash
Expand Down Expand Up @@ -226,12 +232,17 @@ func Decode(v string) (*Cid, error) {
return NewCidV0(hash), nil
}

_, data, err := mbase.Decode(v)
base, data, err := mbase.Decode(v)
if err != nil {
return nil, err
}

return Cast(data)
c, err := Cast(data)
if err != nil {
return nil, err
}
c.base = base
return c, nil
}

func uvError(read int) error {
Expand Down Expand Up @@ -263,11 +274,7 @@ func Cast(data []byte) (*Cid, error) {
return nil, err
}

return &Cid{
codec: DagProtobuf,
version: 0,
hash: h,
}, nil
return NewCidV0(h), nil
}

vers, n := binary.Uvarint(data)
Expand All @@ -291,6 +298,7 @@ func Cast(data []byte) (*Cid, error) {
}

return &Cid{
base: -1,
version: vers,
codec: codec,
hash: h,
Expand All @@ -302,27 +310,56 @@ 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) HaveBase() bool {
return c.base != -1
}

func (c *Cid) Base() mbase.Encoder {
base := c.base
if c.base == -1 {
base = DefaultBase
if c.version == 0 {
base = mbase.Base58BTC
}
}
encoder, err := mbase.NewEncoder(base)
if err != nil {
panic(err) // should not happen
}
return encoder
}

// WithBase changes the Multibase that associated with the Cid. If
// the Cid is version 0 then the multibase is ignored when conversting
// to a string, but the value is still associated with the Cid. This
// is useful when, for example, converting a CidV0 to CidV1.
func (c *Cid) WithBase(b mbase.Encoder) *Cid {
c2 := *c
c2.base = b.Encoding()
return &c2
}

// ResetBase resets the base to the default value
func (c *Cid) ResetBase() *Cid {
c2 := *c
c2.base = -1
return &c2
}

// String returns the string representation of a Cid.
func (c *Cid) String() string {
switch c.version {
case 0:
return c.hash.B58String()
case 1:
mbstr, err := mbase.Encode(mbase.Base58BTC, c.bytesV1())
if err != nil {
panic("should not error with hardcoded mbase: " + err.Error())
}

return mbstr
return c.Base().Encode(c.bytesV1())
default:
panic("not possible to reach this point")
}
}

// String returns the string representation of a Cid
// encoded is selected base
// StringOfBase returns the string representation of a Cid encoded is
// selected base. Deprecated use: WithBase(...).String()
func (c *Cid) StringOfBase(base mbase.Encoding) (string, error) {
switch c.version {
case 0:
Expand Down Expand Up @@ -376,6 +413,8 @@ func (c *Cid) bytesV1() []byte {
// 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.
// Two Cids can be equal even if they have different
// multibases associated with them.
func (c *Cid) Equals(o *Cid) bool {
return c.codec == o.codec &&
c.version == o.version &&
Expand Down Expand Up @@ -404,6 +443,7 @@ func (c *Cid) UnmarshalJSON(b []byte) error {
return err
}

c.base = out.base
c.version = out.version
c.hash = out.hash
c.codec = out.codec
Expand Down
48 changes: 39 additions & 9 deletions cid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,23 +163,53 @@ func TestEmptyString(t *testing.T) {
}

func TestV0Handling(t *testing.T) {
origDef := DefaultBase
old := "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n"
DefaultBase = mbase.Base58BTC
t.Run("DefaultBase=base58btc", func (t *testing.T) {testHandling(t, 0, old, old)})
DefaultBase = mbase.Base32
t.Run("DefaultBase=base32", func (t *testing.T) {testHandling(t, 0, old, old)})
DefaultBase = origDef
}

func TestV1Handling(t *testing.T) {
origDef := DefaultBase
new := "zdj7Wkkhxcu2rsiN6GUyHCLsSLL47kdUNfjbFqBUUhMFTZKBi"
base32 := "bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku"
DefaultBase = mbase.Base58BTC
t.Run("DefaultBase=base58btc", func (t *testing.T) {testHandling(t, 1, new, new)})
DefaultBase = mbase.Base32
t.Run("DefaultBase=base32", func (t *testing.T) {testHandling(t, 1, new, base32)})
DefaultBase = origDef
}

cid, err := Decode(old)
func testHandling(t *testing.T, v uint64, cidStr, cidStr2 string) {
cid, err := Decode(cidStr)
if err != nil {
t.Fatal(err)
}

if cid.version != 0 {
t.Fatal("should have gotten version 0 cid")
if cid.version != v {
t.Fatalf("should have gotten version %d cid", v)
}

if v == 0 {
if cid.hash.B58String() != cidStr {
t.Fatal("marshaling roundtrip failed: B58String()")
}
}

if cid.String() != cidStr {
t.Fatal("marshaling roundtrip failed: String()")
}

if cid.hash.B58String() != old {
t.Fatal("marshaling roundtrip failed")
if cid.Base().Encoding() != mbase.Base58BTC {
t.Fatal("base wrong")
}

if cid.String() != old {
t.Fatal("marshaling roundtrip failed")
cid = cid.ResetBase()
if cid.String() != cidStr2 {
t.Fatal("marshaling roundtrip failed after ResetBase()")
}
}

Expand Down Expand Up @@ -355,7 +385,7 @@ func TestHexDecode(t *testing.T) {
t.Fatal(err)
}

if c.String() != "zb2rhhFAEMepUBbGyP1k8tGfz7BSciKXP6GHuUeUsJBaK6cqG" {
if c.String() != hexcid {
t.Fatal("hash value failed to round trip decoding from hex")
}
}
Expand All @@ -382,7 +412,7 @@ func TestFromJson(t *testing.T) {
}

if c.String() != cval {
t.Fatal("json parsing failed")
t.Fatalf("json parsing failed: %s != %s", c.String(), cval)
}
}

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
},
{
"author": "whyrusleeping",
"hash": "QmexBtiTTEwwn42Yi6ouKt6VqzpA6wjJgiW1oh9VfaRrup",
"hash": "QmSbvata2WqNkqGtZNg8MR3SKwnB8iQ7vTPJgWqB8bC5kR",
"name": "go-multibase",
"version": "0.2.6"
"version": "0.2.7"
}
],
"gxVersion": "0.8.0",
Expand Down