Skip to content

Commit dcfc803

Browse files
authored
feat: add consume peer record method (libp2p#84)
Add method to patch peer info with certified addresses extracted from a signed peer record.
1 parent d6c3314 commit dcfc803

File tree

3 files changed

+141
-0
lines changed

3 files changed

+141
-0
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@
151151
"@libp2p/interfaces": "^3.2.0",
152152
"@libp2p/logger": "^2.0.7",
153153
"@libp2p/peer-id": "^2.0.0",
154+
"@libp2p/peer-record": "^5.0.3",
154155
"@multiformats/multiaddr": "^12.0.0",
155156
"interface-datastore": "^8.0.0",
156157
"mortice": "^3.0.1",

src/index.ts

+42
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { Datastore } from 'interface-datastore'
66
import type { Multiaddr } from '@multiformats/multiaddr'
77
import type { Libp2pEvents } from '@libp2p/interface-libp2p'
88
import { logger } from '@libp2p/logger'
9+
import { RecordEnvelope, PeerRecord } from '@libp2p/peer-record'
910

1011
const log = logger('libp2p:peer-store')
1112

@@ -164,6 +165,47 @@ export class PersistentPeerStore implements PeerStore {
164165
}
165166
}
166167

168+
async consumePeerRecord (buf: Uint8Array, expectedPeer?: PeerId): Promise<boolean> {
169+
const envelope = await RecordEnvelope.openAndCertify(buf, PeerRecord.DOMAIN)
170+
171+
if (expectedPeer?.equals(envelope.peerId) === false) {
172+
log('envelope peer id was not the expected peer id - expected: %p received: %p', expectedPeer, envelope.peerId)
173+
return false
174+
}
175+
176+
const peerRecord = PeerRecord.createFromProtobuf(envelope.payload)
177+
let peer: Peer | undefined
178+
179+
try {
180+
peer = await this.get(envelope.peerId)
181+
} catch (err: any) {
182+
if (err.code !== 'ERR_NOT_FOUND') {
183+
throw err
184+
}
185+
}
186+
187+
// ensure seq is greater than, or equal to, the last received
188+
if (peer?.peerRecordEnvelope != null) {
189+
const storedEnvelope = await RecordEnvelope.createFromProtobuf(peer.peerRecordEnvelope)
190+
const storedRecord = PeerRecord.createFromProtobuf(storedEnvelope.payload)
191+
192+
if (storedRecord.seqNumber >= peerRecord.seqNumber) {
193+
log('sequence number was lower or equal to existing sequence number - stored: %d received: %d', storedRecord.seqNumber, peerRecord.seqNumber)
194+
return false
195+
}
196+
}
197+
198+
await this.patch(peerRecord.peerId, {
199+
peerRecordEnvelope: buf,
200+
addresses: peerRecord.multiaddrs.map(multiaddr => ({
201+
isCertified: true,
202+
multiaddr
203+
}))
204+
})
205+
206+
return true
207+
}
208+
167209
#emitIfUpdated (id: PeerId, result: PeerUpdate): void {
168210
if (!result.updated) {
169211
return

test/index.spec.ts

+98
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { createEd25519PeerId } from '@libp2p/peer-id-factory'
1010
import delay from 'delay'
1111
import { EventEmitter } from '@libp2p/interfaces/events'
1212
import type { Libp2pEvents } from '@libp2p/interface-libp2p'
13+
import { RecordEnvelope, PeerRecord } from '@libp2p/peer-record'
1314

1415
const addr1 = multiaddr('/ip4/127.0.0.1/tcp/8000')
1516

@@ -162,4 +163,101 @@ describe('PersistentPeerStore', () => {
162163
.that.does.not.have.key(name)
163164
})
164165
})
166+
167+
describe('peer record', () => {
168+
it('consumes a peer record, creating a peer', async () => {
169+
const peerRecord = new PeerRecord({
170+
peerId,
171+
multiaddrs: [
172+
multiaddr('/ip4/127.0.0.1/tcp/1234')
173+
]
174+
})
175+
const signedPeerRecord = await RecordEnvelope.seal(peerRecord, peerId)
176+
177+
await expect(peerStore.has(peerId)).to.eventually.be.false()
178+
await peerStore.consumePeerRecord(signedPeerRecord.marshal())
179+
await expect(peerStore.has(peerId)).to.eventually.be.true()
180+
181+
const peer = await peerStore.get(peerId)
182+
expect(peer.addresses.map(({ multiaddr, isCertified }) => ({
183+
isCertified,
184+
multiaddr: multiaddr.toString()
185+
}))).to.deep.equal([{
186+
isCertified: true,
187+
multiaddr: '/ip4/127.0.0.1/tcp/1234'
188+
}])
189+
})
190+
191+
it('overwrites old addresses with those from a peer record', async () => {
192+
await peerStore.patch(peerId, {
193+
multiaddrs: [
194+
multiaddr('/ip4/127.0.0.1/tcp/1234')
195+
]
196+
})
197+
198+
const peerRecord = new PeerRecord({
199+
peerId,
200+
multiaddrs: [
201+
multiaddr('/ip4/127.0.0.1/tcp/4567')
202+
]
203+
})
204+
const signedPeerRecord = await RecordEnvelope.seal(peerRecord, peerId)
205+
206+
await peerStore.consumePeerRecord(signedPeerRecord.marshal())
207+
208+
await expect(peerStore.has(peerId)).to.eventually.be.true()
209+
210+
const peer = await peerStore.get(peerId)
211+
expect(peer.addresses.map(({ multiaddr, isCertified }) => ({
212+
isCertified,
213+
multiaddr: multiaddr.toString()
214+
}))).to.deep.equal([{
215+
isCertified: true,
216+
multiaddr: '/ip4/127.0.0.1/tcp/4567'
217+
}])
218+
})
219+
220+
it('ignores older peer records', async () => {
221+
const oldSignedPeerRecord = await RecordEnvelope.seal(new PeerRecord({
222+
peerId,
223+
multiaddrs: [
224+
multiaddr('/ip4/127.0.0.1/tcp/1234')
225+
],
226+
seqNumber: 1n
227+
}), peerId)
228+
229+
const newSignedPeerRecord = await RecordEnvelope.seal(new PeerRecord({
230+
peerId,
231+
multiaddrs: [
232+
multiaddr('/ip4/127.0.0.1/tcp/4567')
233+
],
234+
seqNumber: 2n
235+
}), peerId)
236+
237+
await expect(peerStore.consumePeerRecord(newSignedPeerRecord.marshal())).to.eventually.equal(true)
238+
await expect(peerStore.consumePeerRecord(oldSignedPeerRecord.marshal())).to.eventually.equal(false)
239+
240+
const peer = await peerStore.get(peerId)
241+
expect(peer.addresses.map(({ multiaddr, isCertified }) => ({
242+
isCertified,
243+
multiaddr: multiaddr.toString()
244+
}))).to.deep.equal([{
245+
isCertified: true,
246+
multiaddr: '/ip4/127.0.0.1/tcp/4567'
247+
}])
248+
})
249+
250+
it('ignores record for unexpected peer', async () => {
251+
const signedPeerRecord = await RecordEnvelope.seal(new PeerRecord({
252+
peerId,
253+
multiaddrs: [
254+
multiaddr('/ip4/127.0.0.1/tcp/4567')
255+
]
256+
}), peerId)
257+
258+
await expect(peerStore.has(peerId)).to.eventually.be.false()
259+
await expect(peerStore.consumePeerRecord(signedPeerRecord.marshal(), otherPeerId)).to.eventually.equal(false)
260+
await expect(peerStore.has(peerId)).to.eventually.be.false()
261+
})
262+
})
165263
})

0 commit comments

Comments
 (0)