Skip to content

Commit f835457

Browse files
vasco-santosjacobheun
authored andcommitted
feat: exchange signed peer records in identify
1 parent ce8b771 commit f835457

File tree

7 files changed

+361
-28
lines changed

7 files changed

+361
-28
lines changed

src/errors.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
exports.messages = {
44
NOT_STARTED_YET: 'The libp2p node is not started yet',
55
DHT_DISABLED: 'DHT is not available',
6-
CONN_ENCRYPTION_REQUIRED: 'At least one connection encryption module is required'
6+
CONN_ENCRYPTION_REQUIRED: 'At least one connection encryption module is required',
7+
ERR_INVALID_ENVELOPE: 'Invalid envelope received',
8+
ERR_INVALID_PEER_RECORD: 'Invalid peer record received'
79
}
810

911
exports.codes = {
@@ -20,6 +22,8 @@ exports.codes = {
2022
ERR_DUPLICATE_TRANSPORT: 'ERR_DUPLICATE_TRANSPORT',
2123
ERR_ENCRYPTION_FAILED: 'ERR_ENCRYPTION_FAILED',
2224
ERR_HOP_REQUEST_FAILED: 'ERR_HOP_REQUEST_FAILED',
25+
ERR_INVALID_ENVELOPE: 'ERR_INVALID_ENVELOPE',
26+
ERR_INVALID_PEER_RECORD: 'ERR_INVALID_PEER_RECORD',
2327
ERR_INVALID_KEY: 'ERR_INVALID_KEY',
2428
ERR_INVALID_MESSAGE: 'ERR_INVALID_MESSAGE',
2529
ERR_INVALID_PARAMETERS: 'ERR_INVALID_PARAMETERS',

src/identify/consts.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,9 @@ const libp2pVersion = require('../../package.json').version
44

55
module.exports.PROTOCOL_VERSION = 'ipfs/0.1.0'
66
module.exports.AGENT_VERSION = `js-libp2p/${libp2pVersion}`
7-
module.exports.MULTICODEC_IDENTIFY = '/ipfs/id/1.0.0'
8-
module.exports.MULTICODEC_IDENTIFY_PUSH = '/ipfs/id/push/1.0.0'
7+
module.exports.MULTICODEC_IDENTIFY = '/p2p/id/1.1.0'
8+
module.exports.MULTICODEC_IDENTIFY_PUSH = '/p2p/id/push/1.1.0'
9+
10+
// Legacy
11+
module.exports.MULTICODEC_IDENTIFY_LEGACY = '/ipfs/id/1.0.0'
12+
module.exports.MULTICODEC_IDENTIFY_PUSH_LEGACY = '/ipfs/id/push/1.0.0'

src/identify/index.js

+159-9
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@ log.error = debug('libp2p:identify:error')
1818

1919
const {
2020
MULTICODEC_IDENTIFY,
21+
MULTICODEC_IDENTIFY_LEGACY,
2122
MULTICODEC_IDENTIFY_PUSH,
23+
MULTICODEC_IDENTIFY_PUSH_LEGACY,
2224
AGENT_VERSION,
2325
PROTOCOL_VERSION
2426
} = require('./consts')
2527

2628
const errCode = require('err-code')
27-
const { codes } = require('../errors')
29+
const { messages, codes } = require('../errors')
30+
const Envelope = require('../record-manager/envelope')
31+
const PeerRecord = require('../record-manager/peer-record')
2832

2933
class IdentifyService {
3034
/**
@@ -89,11 +93,27 @@ class IdentifyService {
8993
push (connections) {
9094
const pushes = connections.map(async connection => {
9195
try {
92-
const { stream } = await connection.newStream(MULTICODEC_IDENTIFY_PUSH)
96+
const { protocol, stream } = await connection.newStream([MULTICODEC_IDENTIFY_PUSH, MULTICODEC_IDENTIFY_PUSH_LEGACY])
97+
98+
// Handle Legacy
99+
if (protocol === MULTICODEC_IDENTIFY_PUSH_LEGACY) {
100+
return pipe(
101+
[{
102+
listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer),
103+
protocols: Array.from(this._protocols.keys())
104+
}],
105+
pb.encode(Message),
106+
stream,
107+
consume
108+
)
109+
}
110+
111+
const envelope = this._libp2p.recordManager.getPeerRecord()
112+
const signedPeerRecord = envelope.marshal()
93113

94114
await pipe(
95115
[{
96-
listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer),
116+
signedPeerRecord,
97117
protocols: Array.from(this._protocols.keys())
98118
}],
99119
pb.encode(Message),
@@ -135,7 +155,7 @@ class IdentifyService {
135155
* @returns {Promise<void>}
136156
*/
137157
async identify (connection) {
138-
const { stream } = await connection.newStream(MULTICODEC_IDENTIFY)
158+
const { protocol, stream } = await connection.newStream([MULTICODEC_IDENTIFY, MULTICODEC_IDENTIFY_LEGACY])
139159
const [data] = await pipe(
140160
[],
141161
stream,
@@ -160,7 +180,8 @@ class IdentifyService {
160180
publicKey,
161181
listenAddrs,
162182
protocols,
163-
observedAddr
183+
observedAddr,
184+
signedPeerRecord
164185
} = message
165186

166187
const id = await PeerId.createFromPubKey(publicKey)
@@ -172,8 +193,40 @@ class IdentifyService {
172193
// Get the observedAddr if there is one
173194
observedAddr = IdentifyService.getCleanMultiaddr(observedAddr)
174195

196+
// LEGACY: differentiate message with SignedPeerRecord
197+
if (protocol === MULTICODEC_IDENTIFY_LEGACY) {
198+
// Update peers data in PeerStore
199+
this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr)))
200+
this.peerStore.protoBook.set(id, protocols)
201+
202+
// TODO: Track our observed address so that we can score it
203+
log('received observed address of %s', observedAddr)
204+
205+
return
206+
}
207+
208+
// Open envelope and verify if is authenticated
209+
let envelope
210+
try {
211+
envelope = await Envelope.openAndCertify(signedPeerRecord, PeerRecord.DOMAIN)
212+
} catch (err) {
213+
log('received invalid envelope, discard it')
214+
throw errCode(new Error(messages.ERR_INVALID_ENVELOPE), codes.ERR_INVALID_ENVELOPE)
215+
}
216+
217+
// Decode peer record
218+
let peerRecord
219+
try {
220+
peerRecord = await PeerRecord.createFromProtobuf(envelope.payload)
221+
} catch (err) {
222+
log('received invalid peer record, discard it')
223+
throw errCode(new Error(messages.ERR_INVALID_PEER_RECORD), codes.ERR_INVALID_PEER_RECORD)
224+
}
225+
226+
// TODO: Store as certified record
227+
175228
// Update peers data in PeerStore
176-
this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr)))
229+
this.peerStore.addressBook.set(id, peerRecord.multiaddrs.map((addr) => multiaddr(addr)))
177230
this.peerStore.protoBook.set(id, protocols)
178231
this.peerStore.metadataBook.set(id, 'AgentVersion', Buffer.from(message.agentVersion))
179232

@@ -194,16 +247,20 @@ class IdentifyService {
194247
switch (protocol) {
195248
case MULTICODEC_IDENTIFY:
196249
return this._handleIdentify({ connection, stream })
250+
case MULTICODEC_IDENTIFY_LEGACY:
251+
return this._handleIdentifyLegacy({ connection, stream })
197252
case MULTICODEC_IDENTIFY_PUSH:
198253
return this._handlePush({ connection, stream })
254+
case MULTICODEC_IDENTIFY_PUSH_LEGACY:
255+
return this._handlePushLegacy({ connection, stream })
199256
default:
200257
log.error('cannot handle unknown protocol %s', protocol)
201258
}
202259
}
203260

204261
/**
205-
* Sends the `Identify` response to the requesting peer over the
206-
* given `connection`
262+
* Sends the `Identify` response with the Signed Peer Record
263+
* to the requesting peer over the given `connection`
207264
* @private
208265
* @param {object} options
209266
* @param {*} options.stream
@@ -215,6 +272,40 @@ class IdentifyService {
215272
publicKey = this.peerId.pubKey.bytes
216273
}
217274

275+
const envelope = this._libp2p.recordManager.getPeerRecord()
276+
const signedPeerRecord = envelope.marshal()
277+
278+
const message = Message.encode({
279+
protocolVersion: PROTOCOL_VERSION,
280+
agentVersion: AGENT_VERSION,
281+
publicKey,
282+
signedPeerRecord,
283+
observedAddr: connection.remoteAddr.buffer,
284+
protocols: Array.from(this._protocols.keys())
285+
})
286+
287+
pipe(
288+
[message],
289+
lp.encode(),
290+
stream,
291+
consume
292+
)
293+
}
294+
295+
/**
296+
* Sends the `Identify` response with listen addresses (LEGACY)
297+
* to the requesting peer over the given `connection`
298+
* @private
299+
* @param {object} options
300+
* @param {*} options.stream
301+
* @param {Connection} options.connection
302+
*/
303+
_handleIdentifyLegacy ({ connection, stream }) {
304+
let publicKey = Buffer.alloc(0)
305+
if (this.peerId.pubKey) {
306+
publicKey = this.peerId.pubKey.bytes
307+
}
308+
218309
const message = Message.encode({
219310
protocolVersion: PROTOCOL_VERSION,
220311
agentVersion: AGENT_VERSION,
@@ -259,6 +350,63 @@ class IdentifyService {
259350
return log.error('received invalid message', err)
260351
}
261352

353+
// Open envelope and verify if is authenticated
354+
let envelope
355+
try {
356+
envelope = await Envelope.openAndCertify(message.signedPeerRecord, PeerRecord.DOMAIN)
357+
} catch (err) {
358+
log('received invalid envelope, discard it')
359+
throw errCode(new Error(messages.ERR_INVALID_ENVELOPE), codes.ERR_INVALID_ENVELOPE)
360+
}
361+
362+
// Decode peer record
363+
let peerRecord
364+
try {
365+
peerRecord = await PeerRecord.createFromProtobuf(envelope.payload)
366+
} catch (err) {
367+
log('received invalid peer record, discard it')
368+
throw errCode(new Error(messages.ERR_INVALID_PEER_RECORD), codes.ERR_INVALID_PEER_RECORD)
369+
}
370+
371+
// Update peers data in PeerStore
372+
const id = connection.remotePeer
373+
try {
374+
// TODO: Store as certified record
375+
376+
this.peerStore.addressBook.set(id, peerRecord.multiaddrs.map((addr) => multiaddr(addr)))
377+
} catch (err) {
378+
return log.error('received invalid listen addrs', err)
379+
}
380+
381+
// Update the protocols
382+
this.peerStore.protoBook.set(id, message.protocols)
383+
}
384+
385+
/**
386+
* Reads the Identify Push message from the given `connection`
387+
* with listen addresses (LEGACY)
388+
* @private
389+
* @param {object} options
390+
* @param {*} options.stream
391+
* @param {Connection} options.connection
392+
*/
393+
async _handlePushLegacy ({ connection, stream }) {
394+
const [data] = await pipe(
395+
[],
396+
stream,
397+
lp.decode(),
398+
take(1),
399+
toBuffer,
400+
collect
401+
)
402+
403+
let message
404+
try {
405+
message = Message.decode(data)
406+
} catch (err) {
407+
return log.error('received invalid message', err)
408+
}
409+
262410
// Update peers data in PeerStore
263411
const id = connection.remotePeer
264412
try {
@@ -279,6 +427,8 @@ module.exports.IdentifyService = IdentifyService
279427
*/
280428
module.exports.multicodecs = {
281429
IDENTIFY: MULTICODEC_IDENTIFY,
282-
IDENTIFY_PUSH: MULTICODEC_IDENTIFY_PUSH
430+
IDENTIFY_LEGACY: MULTICODEC_IDENTIFY_LEGACY,
431+
IDENTIFY_PUSH: MULTICODEC_IDENTIFY_PUSH,
432+
IDENTIFY_PUSH_LEGACY: MULTICODEC_IDENTIFY_PUSH_LEGACY
283433
}
284434
module.exports.Message = Message

src/identify/message.js

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ message Identify {
2424
optional bytes observedAddr = 4;
2525
2626
repeated string protocols = 3;
27+
28+
// signedPeerRecord contains a serialized SignedEnvelope containing a PeerRecord,
29+
// signed by the sending node. It contains the same addresses as the listenAddrs field, but
30+
// in a form that lets us share authenticated addrs with other peers.
31+
optional bytes signedPeerRecord = 8;
2732
}
2833
`
2934

src/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,9 @@ class Libp2p extends EventEmitter {
446446
// Listen on the provided transports
447447
await this.transportManager.listen()
448448

449+
// Start record Manager
450+
await this.recordManager.start()
451+
449452
// Start PeerStore
450453
await this.peerStore.start()
451454

src/record/peer-record/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,6 @@ PeerRecord.createFromProtobuf = (buf) => {
9595
return new PeerRecord({ peerId, multiaddrs, seqNumber })
9696
}
9797

98+
PeerRecord.DOMAIN = ENVELOPE_DOMAIN_PEER_RECORD
99+
98100
module.exports = PeerRecord

0 commit comments

Comments
 (0)