Skip to content

Commit 69d7a01

Browse files
achingbrainvasco-santos
andauthoredJul 6, 2021
chore: update to new multiformats (#149)
Replaces multihashes with the new multiformats module BREAKING CHANGE: uses the CID class from the new multiformats module Co-authored-by: Vasco Santos <vasco.santos@moxy.studio>
1 parent 173b3b3 commit 69d7a01

File tree

5 files changed

+125
-53
lines changed

5 files changed

+125
-53
lines changed
 

‎README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
- [`createFromPrivKey(privKey)`](#createfromprivkeyprivkey)
4343
- [`createFromJSON(obj)`](#createfromjsonobj)
4444
- [`createFromProtobuf(buf)`](#createfromprotobufbuf)
45+
- [`parse(str)`](#parsestr)
4546
- [Export](#export)
4647
- [`toHexString()`](#tohexstring)
4748
- [`toBytes()`](#tobytes)
@@ -169,7 +170,7 @@ Returns `PeerId`.
169170

170171
### `createFromCID(cid)`
171172

172-
- `cid: CID|String|Buffer` - The multihash encoded as [CID](https://github.com/ipld/js-cid) (object, `String` or `Buffer`)
173+
- `cid: CID` - The multihash encoded as [CID](https://github.com/multiformats/js-multiformats/blob/master/src/cid.js) object
173174

174175
Creates a Peer ID from a CID representation of the key's multihash ([RFC 0001](https://github.com/libp2p/specs/blob/master/RFC/0001-text-peerid-cid.md)).
175176

@@ -209,6 +210,10 @@ Returns `Promise<PeerId>`.
209210

210211
`buf` is a protocol-buffers encoded PeerId (see `marshal()`)
211212

213+
### `parse(str)`
214+
215+
Attempts to create a PeerId from a base58btc encoded string or a CID encoded as a string.
216+
212217
## Export
213218

214219
### `toHexString()`

‎package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"bin": "src/bin.js",
99
"scripts": {
1010
"lint": "aegir lint",
11+
"prepare": "npm run build",
1112
"build": "npm run build:proto && npm run build:proto-types && aegir build --no-types",
1213
"build:proto": "pbjs -t static-module -w commonjs -r libp2p-peer-id --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/proto.js ./src/proto.proto",
1314
"build:proto-types": "pbts -o src/proto.d.ts src/proto.js",
@@ -41,14 +42,14 @@
4142
"@types/dirty-chai": "^2.0.2",
4243
"@types/mocha": "^8.2.0",
4344
"aegir": "^33.0.0",
45+
"multihashes": "^4.0.2",
4446
"util": "^0.12.3"
4547
},
4648
"dependencies": {
47-
"cids": "^1.1.5",
4849
"class-is": "^1.1.0",
4950
"libp2p-crypto": "^0.19.0",
5051
"minimist": "^1.2.5",
51-
"multihashes": "^4.0.2",
52+
"multiformats": "^9.0.0",
5253
"protobufjs": "^6.10.2",
5354
"uint8arrays": "^2.0.5"
5455
},

‎src/index.d.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { PrivateKey, PublicKey, KeyType } from "libp2p-crypto";
2-
import CID from 'cids'
2+
import { CID } from 'multiformats/cid'
33

44
declare namespace PeerId {
55
/**
@@ -68,7 +68,7 @@ declare namespace PeerId {
6868
* Create PeerId from CID.
6969
* @param cid The CID.
7070
*/
71-
function createFromCID(cid: CID | Uint8Array | string | object): PeerId;
71+
function createFromCID(cid: CID): PeerId;
7272

7373
/**
7474
* Create PeerId from public key.
@@ -94,6 +94,12 @@ declare namespace PeerId {
9494
* @param buf Protobuf bytes, as Uint8Array or hex-encoded string.
9595
*/
9696
function createFromProtobuf(buf: Uint8Array | string): Promise<PeerId>;
97+
98+
/**
99+
* Parse a PeerId from a string.
100+
* @param str encoded public key string.
101+
*/
102+
function parse(str: string): PeerId;
97103
}
98104

99105
/**

‎src/index.js

+57-19
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,21 @@
44

55
'use strict'
66

7-
const mh = require('multihashes')
8-
const CID = require('cids')
7+
const { CID } = require('multiformats/cid')
8+
const { base58btc } = require('multiformats/bases/base58')
9+
const { base16 } = require('multiformats/bases/base16')
10+
const Digest = require('multiformats/hashes/digest')
911
const cryptoKeys = require('libp2p-crypto/src/keys')
1012
const withIs = require('class-is')
1113
const { PeerIdProto } = require('./proto')
1214
const uint8ArrayEquals = require('uint8arrays/equals')
1315
const uint8ArrayFromString = require('uint8arrays/from-string')
1416
const uint8ArrayToString = require('uint8arrays/to-string')
17+
const { identity } = require('multiformats/hashes/identity')
18+
19+
// these values are from https://github.com/multiformats/multicodec/blob/master/table.csv
20+
const DAG_PB_CODE = 0x70
21+
const LIBP2P_KEY_CODE = 0x72
1522

1623
class PeerId {
1724
constructor (id, privKey, pubKey) {
@@ -24,7 +31,7 @@ class PeerId {
2431
}
2532

2633
this._id = id
27-
this._idB58String = mh.toB58String(this.id)
34+
this._idB58String = base58btc.encode(this.id).substring(1)
2835
this._privKey = privKey
2936
this._pubKey = pubKey
3037
}
@@ -55,9 +62,9 @@ class PeerId {
5562
}
5663

5764
try {
58-
const decoded = mh.decode(this.id)
65+
const decoded = Digest.decode(this.id)
5966

60-
if (decoded.name === 'identity') {
67+
if (decoded.code === identity.code) {
6168
this._pubKey = cryptoKeys.unmarshalPublicKey(decoded.digest)
6269
}
6370
} catch (_) {
@@ -121,7 +128,7 @@ class PeerId {
121128

122129
// encode/decode functions
123130
toHexString () {
124-
return mh.toHexString(this.id)
131+
return base16.encode(this.id).substring(1)
125132
}
126133

127134
toBytes () {
@@ -136,10 +143,10 @@ class PeerId {
136143
// in default format from RFC 0001: https://github.com/libp2p/specs/pull/209
137144
toString () {
138145
if (!this._idCIDString) {
139-
const cid = new CID(1, 'libp2p-key', this.id, 'base32')
146+
const cid = CID.createV1(LIBP2P_KEY_CODE, Digest.decode(this.id))
140147

141148
Object.defineProperty(this, '_idCIDString', {
142-
value: cid.toBaseEncodedString('base32'),
149+
value: cid.toString(),
143150
enumerable: false
144151
})
145152
}
@@ -192,8 +199,9 @@ class PeerId {
192199
*/
193200
hasInlinePublicKey () {
194201
try {
195-
const decoded = mh.decode(this.id)
196-
if (decoded.name === 'identity') {
202+
const decoded = Digest.decode(this.id)
203+
204+
if (decoded.code === identity.code) {
197205
return true
198206
}
199207
} catch (_) {
@@ -213,7 +221,7 @@ exports = module.exports = PeerIdWithIs
213221

214222
const computeDigest = (pubKey) => {
215223
if (pubKey.bytes.length <= 42) {
216-
return mh.encode(pubKey.bytes, 'identity')
224+
return Digest.create(identity.code, pubKey.bytes).bytes
217225
} else {
218226
return pubKey.hash()
219227
}
@@ -235,26 +243,46 @@ exports.create = async (opts) => {
235243
}
236244

237245
exports.createFromHexString = (str) => {
238-
return new PeerIdWithIs(mh.fromHexString(str))
246+
return new PeerIdWithIs(base16.decode('f' + str))
239247
}
240248

241249
exports.createFromBytes = (buf) => {
242-
return new PeerIdWithIs(buf)
250+
try {
251+
const cid = CID.decode(buf)
252+
253+
if (!validMulticodec(cid)) {
254+
throw new Error('Supplied PeerID CID is invalid')
255+
}
256+
257+
return exports.createFromCID(cid)
258+
} catch {
259+
const digest = Digest.decode(buf)
260+
261+
if (digest.code !== identity.code) {
262+
throw new Error('Supplied PeerID CID is invalid')
263+
}
264+
265+
return new PeerIdWithIs(buf)
266+
}
243267
}
244268

245269
exports.createFromB58String = (str) => {
246-
return exports.createFromCID(str) // B58String is CIDv0
270+
return exports.createFromBytes(base58btc.decode('z' + str))
247271
}
248272

249273
const validMulticodec = (cid) => {
250274
// supported: 'libp2p-key' (CIDv1) and 'dag-pb' (CIDv0 converted to CIDv1)
251-
return cid.codec === 'libp2p-key' || cid.codec === 'dag-pb'
275+
return cid.code === LIBP2P_KEY_CODE || cid.code === DAG_PB_CODE
252276
}
253277

254278
exports.createFromCID = (cid) => {
255-
cid = CID.isCID(cid) ? cid : new CID(cid)
256-
if (!validMulticodec(cid)) throw new Error('Supplied PeerID CID has invalid multicodec: ' + cid.codec)
257-
return new PeerIdWithIs(cid.multihash)
279+
cid = CID.asCID(cid)
280+
281+
if (!cid || !validMulticodec(cid)) {
282+
throw new Error('Supplied PeerID CID is invalid')
283+
}
284+
285+
return new PeerIdWithIs(cid.multihash.bytes)
258286
}
259287

260288
// Public Key input will be a Uint8Array
@@ -288,7 +316,7 @@ exports.createFromPrivKey = async (key) => {
288316
}
289317

290318
exports.createFromJSON = async (obj) => {
291-
const id = mh.fromB58String(obj.id)
319+
const id = base58btc.decode('z' + obj.id)
292320
const rawPrivKey = obj.privKey && uint8ArrayFromString(obj.privKey, 'base64pad')
293321
const rawPubKey = obj.pubKey && uint8ArrayFromString(obj.pubKey, 'base64pad')
294322
const pub = rawPubKey && await cryptoKeys.unmarshalPublicKey(rawPubKey)
@@ -360,6 +388,16 @@ exports.createFromProtobuf = async (buf) => {
360388
throw new Error('Protobuf did not contain any usable key material')
361389
}
362390

391+
exports.parse = (str) => {
392+
if (str.charAt(0) === '1') {
393+
// base58btc encoded public key
394+
return exports.createFromBytes(base58btc.decode(`z${str}`))
395+
}
396+
397+
// try to parse it as a regular base58btc multihash or base32 encoded CID
398+
return exports.createFromCID(CID.parse(str))
399+
}
400+
363401
exports.isPeerId = (peerId) => {
364402
return Boolean(typeof peerId === 'object' &&
365403
peerId._id &&

‎test/peer-id.spec.js

+51-29
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,26 @@
55
const { expect } = require('aegir/utils/chai')
66
const crypto = require('libp2p-crypto')
77
const mh = require('multihashes')
8-
const CID = require('cids')
8+
const { CID } = require('multiformats/cid')
9+
const Digest = require('multiformats/hashes/digest')
910
const uint8ArrayFromString = require('uint8arrays/from-string')
1011
const uint8ArrayToString = require('uint8arrays/to-string')
1112

1213
const PeerId = require('../src')
1314

1415
const util = require('util')
1516

17+
const DAG_PB_CODE = 0x70
18+
const LIBP2P_KEY_CODE = 0x72
19+
const RAW_CODE = 0x55
20+
1621
const testId = require('./fixtures/sample-id')
1722
const testIdHex = testId.id
1823
const testIdBytes = mh.fromHexString(testId.id)
24+
const testIdDigest = Digest.decode(testIdBytes)
1925
const testIdB58String = mh.toB58String(testIdBytes)
20-
const testIdCID = new CID(1, 'libp2p-key', testIdBytes)
21-
const testIdCIDString = testIdCID.toBaseEncodedString('base32')
26+
const testIdCID = CID.createV1(LIBP2P_KEY_CODE, testIdDigest)
27+
const testIdCIDString = testIdCID.toString()
2228

2329
const goId = require('./fixtures/go-private-key')
2430

@@ -90,63 +96,55 @@ describe('PeerId', () => {
9096
})
9197

9298
it('recreate from Base58 String (CIDv0))', () => {
93-
const id = PeerId.createFromCID(testIdB58String)
99+
const id = PeerId.createFromCID(CID.parse(testIdB58String))
94100
expect(testIdCIDString).to.equal(id.toString())
95101
expect(testIdBytes).to.deep.equal(id.toBytes())
96102
})
97103

98104
it('recreate from CIDv1 Base32 (libp2p-key multicodec)', () => {
99-
const cid = new CID(1, 'libp2p-key', testIdBytes)
100-
const cidString = cid.toBaseEncodedString('base32')
101-
const id = PeerId.createFromCID(cidString)
102-
expect(cidString).to.equal(id.toString())
105+
const cid = CID.createV1(LIBP2P_KEY_CODE, testIdDigest)
106+
const id = PeerId.createFromCID(cid)
107+
expect(cid.toString()).to.equal(id.toString())
103108
expect(testIdBytes).to.deep.equal(id.toBytes())
104109
})
105110

106111
it('recreate from CIDv1 Base32 (dag-pb multicodec)', () => {
107-
const cid = new CID(1, 'dag-pb', testIdBytes)
108-
const cidString = cid.toBaseEncodedString('base32')
109-
const id = PeerId.createFromCID(cidString)
112+
const cid = CID.createV1(DAG_PB_CODE, testIdDigest)
113+
const id = PeerId.createFromCID(cid)
110114
// toString should return CID with multicodec set to libp2p-key
111-
expect(new CID(id.toString()).codec).to.equal('libp2p-key')
115+
expect(CID.parse(id.toString()).code).to.equal(LIBP2P_KEY_CODE)
112116
expect(testIdBytes).to.deep.equal(id.toBytes())
113117
})
114118

115119
it('recreate from CID Uint8Array', () => {
116-
const id = PeerId.createFromCID(testIdCID.bytes)
120+
const id = PeerId.createFromBytes(testIdCID.bytes)
117121
expect(testIdCIDString).to.equal(id.toString())
118122
expect(testIdBytes).to.deep.equal(id.toBytes())
119123
})
120124

121125
it('throws on invalid CID multicodec', () => {
122126
// only libp2p and dag-pb are supported
123-
const invalidCID = new CID(1, 'raw', testIdBytes).toBaseEncodedString('base32')
127+
const invalidCID = CID.createV1(RAW_CODE, testIdDigest)
124128
expect(() => {
125129
PeerId.createFromCID(invalidCID)
126-
}).to.throw(/Supplied PeerID CID has invalid multicodec: raw/)
130+
}).to.throw(/invalid/i)
127131
})
128132

129-
it('throws on invalid CID value', () => {
130-
// using function code that does not represent valid hash function
133+
it('throws on invalid multihash value', () => {
134+
// using function code 0x50 that does not represent valid hash function
131135
// https://github.com/multiformats/js-multihash/blob/b85999d5768bf06f1b0f16b926ef2cb6d9c14265/src/constants.js#L345
132-
const invalidCID = 'QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L'
136+
const invalidMultihash = uint8ArrayToString(Uint8Array.from([0x50, 0x1, 0x0]), 'base58btc')
133137
expect(() => {
134-
PeerId.createFromCID(invalidCID)
135-
}).to.throw(/multihash unknown function code: 0x50/)
138+
PeerId.createFromB58String(invalidMultihash)
139+
}).to.throw(/invalid/i)
136140
})
137141

138142
it('throws on invalid CID object', () => {
139143
const invalidCID = {}
140144
expect(() => {
145+
// @ts-expect-error invalid cid is invalid type
141146
PeerId.createFromCID(invalidCID)
142-
}).to.throw(/Invalid version, must be a number equal to 1 or 0/)
143-
})
144-
145-
it('throws on invalid CID object', () => {
146-
const invalidCID = {}
147-
expect(() => {
148-
PeerId.createFromCID(invalidCID)
149-
}).to.throw(/Invalid version, must be a number equal to 1 or 0/)
147+
}).to.throw(/invalid/i)
150148
})
151149

152150
it('recreate from a Public Key', async () => {
@@ -174,6 +172,28 @@ describe('PeerId', () => {
174172
expect(uint8ArrayToString(id.marshal(), 'base16')).to.deep.equal(testId.marshaled)
175173
})
176174

175+
it('recreate from embedded ed25519 key', async () => {
176+
const key = '12D3KooWRm8J3iL796zPFi2EtGGtUJn58AG67gcqzMFHZnnsTzqD'
177+
const id = await PeerId.parse(key)
178+
expect(id.toB58String()).to.equal(key)
179+
const expB58 = mh.toB58String(mh.encode(id.pubKey.bytes, 'identity'))
180+
expect(id.toB58String()).to.equal(expB58)
181+
})
182+
183+
it('recreate from embedded secp256k1 key', async () => {
184+
const key = '16Uiu2HAm5qw8UyXP2RLxQUx5KvtSN8DsTKz8quRGqGNC3SYiaB8E'
185+
const id = await PeerId.parse(key)
186+
expect(id.toB58String()).to.equal(key)
187+
const expB58 = mh.toB58String(mh.encode(id.pubKey.bytes, 'identity'))
188+
expect(id.toB58String()).to.equal(expB58)
189+
})
190+
191+
it('recreate from string key', async () => {
192+
const key = 'QmRsooYQasV5f5r834NSpdUtmejdQcpxXkK6qsozZWEihC'
193+
const id = await PeerId.parse(key)
194+
expect(id.toB58String()).to.equal(key)
195+
})
196+
177197
it('can be created from a Secp256k1 public key', async () => {
178198
const privKey = await crypto.keys.generateKeyPair('secp256k1', 256)
179199
const id = await PeerId.createFromPubKey(privKey.public.bytes)
@@ -209,7 +229,8 @@ describe('PeerId', () => {
209229

210230
it('Pretty printing', async () => {
211231
const id1 = await PeerId.create(testOpts)
212-
const id2 = await PeerId.createFromPrivKey((id1.toJSON()).privKey)
232+
const json = id1.toJSON()
233+
const id2 = await PeerId.createFromPrivKey(json.privKey || 'invalid, should not happen')
213234
expect(id1.toPrint()).to.be.eql(id2.toPrint())
214235
expect(id1.toPrint()).to.equal('<peer.ID ' + id1.toB58String().substr(2, 6) + '>')
215236
})
@@ -375,6 +396,7 @@ describe('PeerId', () => {
375396
})
376397

377398
it('invalid id', () => {
399+
// @ts-expect-error incorrect constructor arg type
378400
expect(() => new PeerId('hello world')).to.throw(/invalid id/)
379401
})
380402
})

0 commit comments

Comments
 (0)
Please sign in to comment.