Skip to content

Commit 544ca7d

Browse files
lidelvasco-santos
authored andcommitted
feat: support Peer ID represented as CID (#105)
* feat: support Peer ID represented as CID This change adds two functions: - createFromCID accepts CID as String|CID|Buffer and creates PeerId from the multihash value inside of it - toCIDString serializes PeerId multihash into a CIDv1 in Base32, as agreed in libp2p/specs#209 License: MIT Signed-off-by: Marcin Rataj <[email protected]> * refactor: rename toCIDString to toString CIDv1 is self describing, and toString was not defined. Makes sense to use generic toString in this case. This change also: - remembers string with CID, so it is lazily generated only once - switches createFromB58String to createFromCID (b58 is CIDv0), making it easier to migrate existing codebases. License: MIT Signed-off-by: Marcin Rataj <[email protected]> * docs: comment tests License: MIT Signed-off-by: Marcin Rataj <[email protected]> * feat: validate CID multicodec - require CID with 'libp2p-key' (CIDv1) or 'dag-pb' (CIDv0 converted to CIDv1) - delegate CID validation to CID constructor License: MIT Signed-off-by: Marcin Rataj <[email protected]>
1 parent 11d4ec1 commit 544ca7d

File tree

4 files changed

+124
-10
lines changed

4 files changed

+124
-10
lines changed

README.md

+21-2
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,16 @@
3232
- [Import](#import)
3333
- [`createFromHexString(str)`](#createfromhexstringstr)
3434
- [`createFromBytes(buf)`](#createfrombytesbuf)
35+
- [`createFromCID(cid)`](#createfromcidcid)
3536
- [`createFromB58String(str)`](#createfromb58stringstr)
3637
- [`createFromPubKey(pubKey)`](#createfrompubkeypubkey)
3738
- [`createFromPrivKey(privKey)`](#createfromprivkeyprivkey)
3839
- [`createFromJSON(obj)`](#createfromjsonobj)
3940
- [Export](#export)
40-
- [`toHexString()`](#tohexstring)
4141
- [`toBytes()`](#tobytes)
42+
- [`toString()`](#tostring)
4243
- [`toB58String()`](#tob58string)
44+
- [`toHexString()`](#tohexstring)
4345
- [`toJSON()`](#tojson)
4446
- [`toPrint()`](#toprint)
4547
- [License](#license)
@@ -145,6 +147,14 @@ Creates a Peer ID from a buffer representing the key's multihash.
145147

146148
Returns `PeerId`.
147149

150+
### `createFromCID(cid)`
151+
152+
- `cid: CID|String|Buffer` - The multihash encoded as [CID](https://github.com/ipld/js-cid) (object, `String` or `Buffer`)
153+
154+
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)).
155+
156+
Returns `PeerId`.
157+
148158
### `createFromB58String(str)`
149159

150160
Creates a Peer ID from a Base58 string representing the key's multihash.
@@ -197,9 +207,18 @@ Returns the Peer ID's `id` as a buffer.
197207
<Buffer 12 20 d6 24 39 98 f2 fc 56 34 3a d7 ed 03 42 ab 78 86 a4 eb 18 d7 36 f1 b6 7d 44 b3 7f cc 81 e0 f3 9f>
198208
```
199209

210+
211+
### `toString()`
212+
213+
Returns the Peer ID's `id` as a self-describing CIDv1 in Base32 ([RFC 0001](https://github.com/libp2p/specs/blob/master/RFC/0001-text-peerid-cid.md))
214+
215+
```
216+
bafzbeigweq4zr4x4ky2dvv7nanbkw6egutvrrvzw6g3h2rftp7gidyhtt4
217+
```
218+
200219
### `toB58String()`
201220

202-
Returns the Peer ID's `id` as a base58 string.
221+
Returns the Peer ID's `id` as a base58 string (multihash/CIDv0).
203222

204223
```
205224
QmckZzdVd72h9QUFuJJpQqhsZqGLwjhh81qSvZ9BhB2FQi

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"dirty-chai": "^2.0.1"
4141
},
4242
"dependencies": {
43+
"cids": "~0.7.1",
4344
"class-is": "^1.1.0",
4445
"libp2p-crypto": "~0.17.0",
4546
"multihashes": "~0.4.15",

src/index.js

+23-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
'use strict'
66

77
const mh = require('multihashes')
8+
const CID = require('cids')
89
const cryptoKeys = require('libp2p-crypto/src/keys')
910
const assert = require('assert')
1011
const withIs = require('class-is')
@@ -122,6 +123,16 @@ class PeerId {
122123
return this._idB58String
123124
}
124125

126+
// return self-describing String representation
127+
// in default format from RFC 0001: https://github.com/libp2p/specs/pull/209
128+
toString () {
129+
if (!this._idCIDString) {
130+
const cid = new CID(1, 'libp2p-key', this.id, 'base32')
131+
this._idCIDString = cid.toBaseEncodedString('base32')
132+
}
133+
return this._idCIDString
134+
}
135+
125136
isEqual (id) {
126137
if (Buffer.isBuffer(id)) {
127138
return this.id.equals(id)
@@ -184,7 +195,18 @@ exports.createFromBytes = (buf) => {
184195
}
185196

186197
exports.createFromB58String = (str) => {
187-
return new PeerIdWithIs(mh.fromB58String(str))
198+
return exports.createFromCID(str) // B58String is CIDv0
199+
}
200+
201+
const validMulticodec = (cid) => {
202+
// supported: 'libp2p-key' (CIDv1) and 'dag-pb' (CIDv0 converted to CIDv1)
203+
return cid.codec === 'libp2p-key' || cid.codec === 'dag-pb'
204+
}
205+
206+
exports.createFromCID = (cid) => {
207+
cid = CID.isCID(cid) ? cid : new CID(cid)
208+
if (!validMulticodec(cid)) throw new Error('Supplied PeerID CID has invalid multicodec: ' + cid.codec)
209+
return new PeerIdWithIs(cid.multihash)
188210
}
189211

190212
// Public Key input will be a buffer

test/peer-id.spec.js

+79-7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ chai.use(dirtyChai)
88
const expect = chai.expect
99
const crypto = require('libp2p-crypto')
1010
const mh = require('multihashes')
11+
const CID = require('cids')
1112

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

@@ -17,6 +18,8 @@ const testId = require('./fixtures/sample-id')
1718
const testIdHex = testId.id
1819
const testIdBytes = mh.fromHexString(testId.id)
1920
const testIdB58String = mh.toB58String(testIdBytes)
21+
const testIdCID = new CID(1, 'libp2p-key', testIdBytes)
22+
const testIdCIDString = testIdCID.toBaseEncodedString('base32')
2023

2124
const goId = require('./fixtures/go-private-key')
2225

@@ -63,27 +66,96 @@ describe('PeerId', () => {
6366
}).to.throw(/immutable/)
6467
})
6568

66-
it('recreate an Id from Hex string', () => {
69+
it('recreate from Hex string', () => {
6770
const id = PeerId.createFromHexString(testIdHex)
68-
expect(testIdBytes).to.deep.equal(id.id)
71+
expect(testIdBytes).to.deep.equal(id.toBytes())
6972
})
7073

71-
it('Recreate an Id from a Buffer', () => {
74+
it('recreate from a Buffer', () => {
7275
const id = PeerId.createFromBytes(testIdBytes)
7376
expect(testId.id).to.equal(id.toHexString())
77+
expect(testIdBytes).to.deep.equal(id.toBytes())
7478
})
7579

76-
it('Recreate a B58 String', () => {
80+
it('recreate from a B58 String', () => {
7781
const id = PeerId.createFromB58String(testIdB58String)
7882
expect(testIdB58String).to.equal(id.toB58String())
83+
expect(testIdBytes).to.deep.equal(id.toBytes())
7984
})
8085

81-
it('Recreate from a Public Key', async () => {
86+
it('recreate from CID object', () => {
87+
const id = PeerId.createFromCID(testIdCID)
88+
expect(testIdCIDString).to.equal(id.toString())
89+
expect(testIdBytes).to.deep.equal(id.toBytes())
90+
})
91+
92+
it('recreate from Base58 String (CIDv0))', () => {
93+
const id = PeerId.createFromCID(testIdB58String)
94+
expect(testIdCIDString).to.equal(id.toString())
95+
expect(testIdBytes).to.deep.equal(id.toBytes())
96+
})
97+
98+
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())
103+
expect(testIdBytes).to.deep.equal(id.toBytes())
104+
})
105+
106+
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)
110+
// toString should return CID with multicodec set to libp2p-key
111+
expect(new CID(id.toString()).codec).to.equal('libp2p-key')
112+
expect(testIdBytes).to.deep.equal(id.toBytes())
113+
})
114+
115+
it('recreate from CID Buffer', () => {
116+
const id = PeerId.createFromCID(testIdCID.buffer)
117+
expect(testIdCIDString).to.equal(id.toString())
118+
expect(testIdBytes).to.deep.equal(id.toBytes())
119+
})
120+
121+
it('throws on invalid CID multicodec', () => {
122+
// only libp2p and dag-pb are supported
123+
const invalidCID = new CID(1, 'raw', testIdBytes).toBaseEncodedString('base32')
124+
expect(() => {
125+
PeerId.createFromCID(invalidCID)
126+
}).to.throw(/Supplied PeerID CID has invalid multicodec: raw/)
127+
})
128+
129+
it('throws on invalid CID value', () => {
130+
// using function code that does not represent valid hash function
131+
// https://github.com/multiformats/js-multihash/blob/b85999d5768bf06f1b0f16b926ef2cb6d9c14265/src/constants.js#L345
132+
const invalidCID = 'QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L'
133+
expect(() => {
134+
PeerId.createFromCID(invalidCID)
135+
}).to.throw(/multihash unknown function code: 0x50/)
136+
})
137+
138+
it('throws on invalid CID object', () => {
139+
const invalidCID = {}
140+
expect(() => {
141+
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/)
150+
})
151+
152+
it('recreate from a Public Key', async () => {
82153
const id = await PeerId.createFromPubKey(testId.pubKey)
83154
expect(testIdB58String).to.equal(id.toB58String())
155+
expect(testIdBytes).to.deep.equal(id.toBytes())
84156
})
85157

86-
it('Recreate from a Private Key', async () => {
158+
it('recreate from a Private Key', async () => {
87159
const id = await PeerId.createFromPrivKey(testId.privKey)
88160
expect(testIdB58String).to.equal(id.toB58String())
89161
const encoded = Buffer.from(testId.privKey, 'base64')
@@ -92,7 +164,7 @@ describe('PeerId', () => {
92164
expect(id.marshalPubKey()).to.deep.equal(id2.marshalPubKey())
93165
})
94166

95-
it('Recreate from Protobuf', async () => {
167+
it('recreate from Protobuf', async () => {
96168
const id = await PeerId.createFromProtobuf(testId.marshaled)
97169
expect(testIdB58String).to.equal(id.toB58String())
98170
const encoded = Buffer.from(testId.privKey, 'base64')

0 commit comments

Comments
 (0)