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

Commit 56f3a69

Browse files
olizillavmx
authored andcommitted
feat: preserve base when constructed from a string
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 <[email protected]>
1 parent 2c06315 commit 56f3a69

File tree

2 files changed

+83
-48
lines changed

2 files changed

+83
-48
lines changed

src/index.js

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ const withIs = require('class-is')
1212
* @param {string} codec
1313
* @param {number} version
1414
* @param {Buffer} multihash
15-
*
1615
*/
1716

1817
/**
@@ -35,81 +34,106 @@ class CID {
3534
*
3635
* The algorithm for argument input is roughly:
3736
* ```
38-
* if (str)
37+
* if (cid)
38+
* -> create a copy
39+
* else if (str)
3940
* if (1st char is on multibase table) -> CID String
4041
* else -> bs58 encoded multihash
4142
* else if (Buffer)
42-
* if (0 or 1) -> CID
43+
* if (1st byte is 0 or 1) -> CID
4344
* else -> multihash
4445
* else if (Number)
4546
* -> construct CID by parts
46-
*
47-
* ..if only JS had traits..
4847
* ```
4948
*
5049
* @param {string|Buffer} version
5150
* @param {string} [codec]
5251
* @param {Buffer} [multihash]
52+
* @param {string} [multibaseName]
5353
*
5454
* @example
55-
*
56-
* new CID(<version>, <codec>, <multihash>)
55+
* new CID(<version>, <codec>, <multihash>, <multibaseName>)
5756
* new CID(<cidStr>)
5857
* new CID(<cid.buffer>)
5958
* new CID(<multihash>)
6059
* new CID(<bs58 encoded multihash>)
6160
* new CID(<cid>)
62-
*
6361
*/
64-
constructor (version, codec, multihash) {
62+
constructor (version, codec, multihash, multibaseName = 'base58btc') {
6563
if (module.exports.isCID(version)) {
66-
let cid = version
64+
// version is an exising CID instance
65+
const cid = version
6766
this.version = cid.version
6867
this.codec = cid.codec
6968
this.multihash = Buffer.from(cid.multihash)
69+
this.multibaseName = cid.multibaseName
7070
return
7171
}
72+
7273
if (typeof version === 'string') {
73-
if (multibase.isEncoded(version)) { // CID String (encoded with multibase)
74+
// e.g. 'base32' or false
75+
const baseName = multibase.isEncoded(version)
76+
if (baseName) {
77+
// version is a CID String encoded with multibase, so v1
7478
const cid = multibase.decode(version)
75-
version = parseInt(cid.slice(0, 1).toString('hex'), 16)
76-
codec = multicodec.getCodec(cid.slice(1))
77-
multihash = multicodec.rmPrefix(cid.slice(1))
78-
} else { // bs58 string encoded multihash
79-
codec = 'dag-pb'
80-
multihash = mh.fromB58String(version)
81-
version = 0
79+
this.version = parseInt(cid.slice(0, 1).toString('hex'), 16)
80+
this.codec = multicodec.getCodec(cid.slice(1))
81+
this.multihash = multicodec.rmPrefix(cid.slice(1))
82+
this.multibaseName = baseName
83+
} else {
84+
// version is a base58btc string multihash, so v0
85+
this.version = 0
86+
this.codec = 'dag-pb'
87+
this.multihash = mh.fromB58String(version)
88+
this.multibaseName = 'base58btc'
8289
}
83-
} else if (Buffer.isBuffer(version)) {
90+
CID.validateCID(this)
91+
return
92+
}
93+
94+
if (Buffer.isBuffer(version)) {
8495
const firstByte = version.slice(0, 1)
8596
const v = parseInt(firstByte.toString('hex'), 16)
86-
if (v === 0 || v === 1) { // CID
97+
if (v === 0 || v === 1) {
98+
// version is a CID buffer
8799
const cid = version
88-
version = v
89-
codec = multicodec.getCodec(cid.slice(1))
90-
multihash = multicodec.rmPrefix(cid.slice(1))
91-
} else { // multihash
92-
codec = 'dag-pb'
93-
multihash = version
94-
version = 0
100+
this.version = v
101+
this.codec = multicodec.getCodec(cid.slice(1))
102+
this.multihash = multicodec.rmPrefix(cid.slice(1))
103+
this.multibaseName = (v === 0) ? 'base58btc' : multibaseName
104+
} else {
105+
// version is a raw multihash buffer, so v0
106+
this.version = 0
107+
this.codec = 'dag-pb'
108+
this.multihash = version
109+
this.multibaseName = 'base58btc'
95110
}
111+
CID.validateCID(this)
112+
return
96113
}
97114

98-
/**
99-
* @type {string}
100-
*/
101-
this.codec = codec
115+
// otherwise, assemble the CID from the parameters
102116

103117
/**
104118
* @type {number}
105119
*/
106120
this.version = version
107121

122+
/**
123+
* @type {string}
124+
*/
125+
this.codec = codec
126+
108127
/**
109128
* @type {Buffer}
110129
*/
111130
this.multihash = multihash
112131

132+
/**
133+
* @type {string}
134+
*/
135+
this.multibaseName = multibaseName
136+
113137
CID.validateCID(this)
114138
}
115139

@@ -193,12 +217,10 @@ class CID {
193217
/**
194218
* Encode the CID into a string.
195219
*
196-
* @param {string} [base='base58btc'] - Base encoding to use.
220+
* @param {string} [base=this.multibaseName] - Base encoding to use.
197221
* @returns {string}
198222
*/
199-
toBaseEncodedString (base) {
200-
base = base || 'base58btc'
201-
223+
toBaseEncodedString (base = this.multibaseName) {
202224
switch (this.version) {
203225
case 0: {
204226
if (base !== 'base58btc') {

test/index.spec.js

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,32 @@ describe('CID', () => {
167167
})
168168
})
169169

170+
describe('.toString', () => {
171+
it('returns a CID string', () => {
172+
const cid = new CID(hash)
173+
expect(cid.toString()).to.equal('QmatYkNGZnELf8cAGdyJpUca2PyY4szai3RHyyWofNY1pY')
174+
})
175+
176+
it('returns a string in the same base as the string passed to the constructor - base64 flavour', () => {
177+
const base64Str = 'mAXASIOnrbGCADfkPyOI37VMkbzluh1eaukBqqnl2oFaFnuIt'
178+
const cid = new CID(base64Str)
179+
expect(cid.toString()).to.equal(base64Str)
180+
})
181+
182+
it('returns a string in the same base as the string passed to the constructor - base16 flavour', () => {
183+
const base16Str = 'f01701220e9eb6c60800df90fc8e237ed53246f396e87579aba406aaa7976a056859ee22d'
184+
const cid = new CID(base16Str)
185+
expect(cid.toString()).to.equal(base16Str)
186+
})
187+
188+
it('returns a string in the base provided', () => {
189+
const b58v1Str = 'zdj7Wd8AMwqnhJGQCbFxBVodGSBG84TM7Hs1rcJuQMwTyfEDS'
190+
const b32v1Str = 'bafybeidskjjd4zmr7oh6ku6wp72vvbxyibcli2r6if3ocdcy7jjjusvl2u'
191+
const cid = new CID(b58v1Str)
192+
expect(cid.toString('base32')).to.equal(b32v1Str)
193+
})
194+
})
195+
170196
describe('utilities', () => {
171197
const h1 = 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n'
172198
const h2 = 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1o'
@@ -207,19 +233,6 @@ describe('CID', () => {
207233
CID.isCID(new CID(h1).toV1())
208234
).to.equal(true)
209235
})
210-
211-
it('.toString() outputs default base encoded CID', () => {
212-
const mhStr = 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n'
213-
const cid = new CID(mhStr)
214-
expect(`${cid}`).to.equal(mhStr)
215-
})
216-
217-
it('.toString(base) outputs base encoded CID', () => {
218-
const b58v1Str = 'zdj7Wd8AMwqnhJGQCbFxBVodGSBG84TM7Hs1rcJuQMwTyfEDS'
219-
const b32v1Str = 'bafybeidskjjd4zmr7oh6ku6wp72vvbxyibcli2r6if3ocdcy7jjjusvl2u'
220-
const cid = new CID(b58v1Str)
221-
expect(cid.toString('base32')).to.equal(b32v1Str)
222-
})
223236
})
224237

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

0 commit comments

Comments
 (0)