Skip to content
This repository has been archived by the owner on Sep 3, 2021. It is now read-only.

Commit

Permalink
feat: preserve base when constructed from a string
Browse files Browse the repository at this point in the history
BREAKING CHANGE: previously base was not preserved and all CIDs would
be normalised to base58btc when asking for their string representation.

The default will change to base32 in https://github.com/multiformats/js-cid/pull/73/files

The idea behind this change is that we shouldnt lose information when
the user passes us a base encoded string, but keep it and use it as
the default base so toString returns the same string they provided.

I'd like this as a fix for ipld explorer, which currently forces all
CIDs into base58btc, seee: ipfs/ipfs-webui#999

License: MIT
Signed-off-by: Oli Evans <oli@tableflip.io>
  • Loading branch information
olizilla authored and vmx committed Apr 4, 2019
1 parent 2c06315 commit 56f3a69
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 48 deletions.
92 changes: 57 additions & 35 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const withIs = require('class-is')
* @param {string} codec
* @param {number} version
* @param {Buffer} multihash
*
*/

/**
Expand All @@ -35,81 +34,106 @@ class CID {
*
* The algorithm for argument input is roughly:
* ```
* if (str)
* if (cid)
* -> create a copy
* else if (str)
* if (1st char is on multibase table) -> CID String
* else -> bs58 encoded multihash
* else if (Buffer)
* if (0 or 1) -> CID
* if (1st byte is 0 or 1) -> CID
* else -> multihash
* else if (Number)
* -> construct CID by parts
*
* ..if only JS had traits..
* ```
*
* @param {string|Buffer} version
* @param {string} [codec]
* @param {Buffer} [multihash]
* @param {string} [multibaseName]
*
* @example
*
* new CID(<version>, <codec>, <multihash>)
* new CID(<version>, <codec>, <multihash>, <multibaseName>)
* new CID(<cidStr>)
* new CID(<cid.buffer>)
* new CID(<multihash>)
* new CID(<bs58 encoded multihash>)
* new CID(<cid>)
*
*/
constructor (version, codec, multihash) {
constructor (version, codec, multihash, multibaseName = 'base58btc') {
if (module.exports.isCID(version)) {
let cid = version
// version is an exising CID instance
const cid = version
this.version = cid.version
this.codec = cid.codec
this.multihash = Buffer.from(cid.multihash)
this.multibaseName = cid.multibaseName
return
}

if (typeof version === 'string') {
if (multibase.isEncoded(version)) { // CID String (encoded with multibase)
// e.g. 'base32' or false
const baseName = multibase.isEncoded(version)
if (baseName) {
// version is a CID String encoded with multibase, so v1
const cid = multibase.decode(version)
version = parseInt(cid.slice(0, 1).toString('hex'), 16)
codec = multicodec.getCodec(cid.slice(1))
multihash = multicodec.rmPrefix(cid.slice(1))
} else { // bs58 string encoded multihash
codec = 'dag-pb'
multihash = mh.fromB58String(version)
version = 0
this.version = parseInt(cid.slice(0, 1).toString('hex'), 16)
this.codec = multicodec.getCodec(cid.slice(1))
this.multihash = multicodec.rmPrefix(cid.slice(1))
this.multibaseName = baseName
} else {
// version is a base58btc string multihash, so v0
this.version = 0
this.codec = 'dag-pb'
this.multihash = mh.fromB58String(version)
this.multibaseName = 'base58btc'
}
} else if (Buffer.isBuffer(version)) {
CID.validateCID(this)
return
}

if (Buffer.isBuffer(version)) {
const firstByte = version.slice(0, 1)
const v = parseInt(firstByte.toString('hex'), 16)
if (v === 0 || v === 1) { // CID
if (v === 0 || v === 1) {
// version is a CID buffer
const cid = version
version = v
codec = multicodec.getCodec(cid.slice(1))
multihash = multicodec.rmPrefix(cid.slice(1))
} else { // multihash
codec = 'dag-pb'
multihash = version
version = 0
this.version = v
this.codec = multicodec.getCodec(cid.slice(1))
this.multihash = multicodec.rmPrefix(cid.slice(1))
this.multibaseName = (v === 0) ? 'base58btc' : multibaseName
} else {
// version is a raw multihash buffer, so v0
this.version = 0
this.codec = 'dag-pb'
this.multihash = version
this.multibaseName = 'base58btc'
}
CID.validateCID(this)
return
}

/**
* @type {string}
*/
this.codec = codec
// otherwise, assemble the CID from the parameters

/**
* @type {number}
*/
this.version = version

/**
* @type {string}
*/
this.codec = codec

/**
* @type {Buffer}
*/
this.multihash = multihash

/**
* @type {string}
*/
this.multibaseName = multibaseName

CID.validateCID(this)
}

Expand Down Expand Up @@ -193,12 +217,10 @@ class CID {
/**
* Encode the CID into a string.
*
* @param {string} [base='base58btc'] - Base encoding to use.
* @param {string} [base=this.multibaseName] - Base encoding to use.
* @returns {string}
*/
toBaseEncodedString (base) {
base = base || 'base58btc'

toBaseEncodedString (base = this.multibaseName) {
switch (this.version) {
case 0: {
if (base !== 'base58btc') {
Expand Down
39 changes: 26 additions & 13 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,32 @@ describe('CID', () => {
})
})

describe('.toString', () => {
it('returns a CID string', () => {
const cid = new CID(hash)
expect(cid.toString()).to.equal('QmatYkNGZnELf8cAGdyJpUca2PyY4szai3RHyyWofNY1pY')
})

it('returns a string in the same base as the string passed to the constructor - base64 flavour', () => {
const base64Str = 'mAXASIOnrbGCADfkPyOI37VMkbzluh1eaukBqqnl2oFaFnuIt'
const cid = new CID(base64Str)
expect(cid.toString()).to.equal(base64Str)
})

it('returns a string in the same base as the string passed to the constructor - base16 flavour', () => {
const base16Str = 'f01701220e9eb6c60800df90fc8e237ed53246f396e87579aba406aaa7976a056859ee22d'
const cid = new CID(base16Str)
expect(cid.toString()).to.equal(base16Str)
})

it('returns a string in the base provided', () => {
const b58v1Str = 'zdj7Wd8AMwqnhJGQCbFxBVodGSBG84TM7Hs1rcJuQMwTyfEDS'
const b32v1Str = 'bafybeidskjjd4zmr7oh6ku6wp72vvbxyibcli2r6if3ocdcy7jjjusvl2u'
const cid = new CID(b58v1Str)
expect(cid.toString('base32')).to.equal(b32v1Str)
})
})

describe('utilities', () => {
const h1 = 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n'
const h2 = 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1o'
Expand Down Expand Up @@ -207,19 +233,6 @@ describe('CID', () => {
CID.isCID(new CID(h1).toV1())
).to.equal(true)
})

it('.toString() outputs default base encoded CID', () => {
const mhStr = 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n'
const cid = new CID(mhStr)
expect(`${cid}`).to.equal(mhStr)
})

it('.toString(base) outputs base encoded CID', () => {
const b58v1Str = 'zdj7Wd8AMwqnhJGQCbFxBVodGSBG84TM7Hs1rcJuQMwTyfEDS'
const b32v1Str = 'bafybeidskjjd4zmr7oh6ku6wp72vvbxyibcli2r6if3ocdcy7jjjusvl2u'
const cid = new CID(b58v1Str)
expect(cid.toString('base32')).to.equal(b32v1Str)
})
})

describe('throws on invalid inputs', () => {
Expand Down

0 comments on commit 56f3a69

Please sign in to comment.