From 5023bced6bbd88ae16c0f5cc67f9e5b927c253cf Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Sat, 20 Jun 2020 19:32:39 +0200 Subject: [PATCH 1/6] feat: exchange signed peer records in identify --- src/errors.js | 6 +- src/identify/consts.js | 8 +- src/identify/index.js | 168 +++++++++++++++++++++++++-- src/identify/message.js | 5 + src/index.js | 3 + src/record/peer-record/index.js | 2 + test/identify/index.spec.js | 198 +++++++++++++++++++++++++++++--- 7 files changed, 362 insertions(+), 28 deletions(-) diff --git a/src/errors.js b/src/errors.js index 18e600c6dc..079e5c89cd 100644 --- a/src/errors.js +++ b/src/errors.js @@ -3,7 +3,9 @@ exports.messages = { NOT_STARTED_YET: 'The libp2p node is not started yet', DHT_DISABLED: 'DHT is not available', - CONN_ENCRYPTION_REQUIRED: 'At least one connection encryption module is required' + CONN_ENCRYPTION_REQUIRED: 'At least one connection encryption module is required', + ERR_INVALID_ENVELOPE: 'Invalid envelope received', + ERR_INVALID_PEER_RECORD: 'Invalid peer record received' } exports.codes = { @@ -20,6 +22,8 @@ exports.codes = { ERR_DUPLICATE_TRANSPORT: 'ERR_DUPLICATE_TRANSPORT', ERR_ENCRYPTION_FAILED: 'ERR_ENCRYPTION_FAILED', ERR_HOP_REQUEST_FAILED: 'ERR_HOP_REQUEST_FAILED', + ERR_INVALID_ENVELOPE: 'ERR_INVALID_ENVELOPE', + ERR_INVALID_PEER_RECORD: 'ERR_INVALID_PEER_RECORD', ERR_INVALID_KEY: 'ERR_INVALID_KEY', ERR_INVALID_MESSAGE: 'ERR_INVALID_MESSAGE', ERR_INVALID_PARAMETERS: 'ERR_INVALID_PARAMETERS', diff --git a/src/identify/consts.js b/src/identify/consts.js index ae8d8432df..3ec0ba7a79 100644 --- a/src/identify/consts.js +++ b/src/identify/consts.js @@ -2,5 +2,9 @@ module.exports.PROTOCOL_VERSION = 'ipfs/0.1.0' module.exports.AGENT_VERSION = 'js-libp2p/0.1.0' -module.exports.MULTICODEC_IDENTIFY = '/ipfs/id/1.0.0' -module.exports.MULTICODEC_IDENTIFY_PUSH = '/ipfs/id/push/1.0.0' +module.exports.MULTICODEC_IDENTIFY = '/p2p/id/1.1.0' +module.exports.MULTICODEC_IDENTIFY_PUSH = '/p2p/id/push/1.1.0' + +// Legacy +module.exports.MULTICODEC_IDENTIFY_LEGACY = '/ipfs/id/1.0.0' +module.exports.MULTICODEC_IDENTIFY_PUSH_LEGACY = '/ipfs/id/push/1.0.0' diff --git a/src/identify/index.js b/src/identify/index.js index cde3275033..65d6f7f044 100644 --- a/src/identify/index.js +++ b/src/identify/index.js @@ -18,13 +18,17 @@ log.error = debug('libp2p:identify:error') const { MULTICODEC_IDENTIFY, + MULTICODEC_IDENTIFY_LEGACY, MULTICODEC_IDENTIFY_PUSH, + MULTICODEC_IDENTIFY_PUSH_LEGACY, AGENT_VERSION, PROTOCOL_VERSION } = require('./consts') const errCode = require('err-code') -const { codes } = require('../errors') +const { messages, codes } = require('../errors') +const Envelope = require('../record-manager/envelope') +const PeerRecord = require('../record-manager/peer-record') class IdentifyService { /** @@ -89,11 +93,27 @@ class IdentifyService { push (connections) { const pushes = connections.map(async connection => { try { - const { stream } = await connection.newStream(MULTICODEC_IDENTIFY_PUSH) + const { protocol, stream } = await connection.newStream([MULTICODEC_IDENTIFY_PUSH, MULTICODEC_IDENTIFY_PUSH_LEGACY]) + + // Handle Legacy + if (protocol === MULTICODEC_IDENTIFY_PUSH_LEGACY) { + return pipe( + [{ + listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer), + protocols: Array.from(this._protocols.keys()) + }], + pb.encode(Message), + stream, + consume + ) + } + + const envelope = this._libp2p.recordManager.getPeerRecord() + const signedPeerRecord = envelope.marshal() await pipe( [{ - listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer), + signedPeerRecord, protocols: Array.from(this._protocols.keys()) }], pb.encode(Message), @@ -135,7 +155,7 @@ class IdentifyService { * @returns {Promise} */ async identify (connection) { - const { stream } = await connection.newStream(MULTICODEC_IDENTIFY) + const { protocol, stream } = await connection.newStream([MULTICODEC_IDENTIFY, MULTICODEC_IDENTIFY_LEGACY]) const [data] = await pipe( [], stream, @@ -160,7 +180,8 @@ class IdentifyService { publicKey, listenAddrs, protocols, - observedAddr + observedAddr, + signedPeerRecord } = message const id = await PeerId.createFromPubKey(publicKey) @@ -172,8 +193,40 @@ class IdentifyService { // Get the observedAddr if there is one observedAddr = IdentifyService.getCleanMultiaddr(observedAddr) + // LEGACY: differentiate message with SignedPeerRecord + if (protocol === MULTICODEC_IDENTIFY_LEGACY) { + // Update peers data in PeerStore + this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr))) + this.peerStore.protoBook.set(id, protocols) + + // TODO: Track our observed address so that we can score it + log('received observed address of %s', observedAddr) + + return + } + + // Open envelope and verify if is authenticated + let envelope + try { + envelope = await Envelope.openAndCertify(signedPeerRecord, PeerRecord.DOMAIN) + } catch (err) { + log('received invalid envelope, discard it') + throw errCode(new Error(messages.ERR_INVALID_ENVELOPE), codes.ERR_INVALID_ENVELOPE) + } + + // Decode peer record + let peerRecord + try { + peerRecord = await PeerRecord.createFromProtobuf(envelope.payload) + } catch (err) { + log('received invalid peer record, discard it') + throw errCode(new Error(messages.ERR_INVALID_PEER_RECORD), codes.ERR_INVALID_PEER_RECORD) + } + + // TODO: Store as certified record + // Update peers data in PeerStore - this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr))) + this.peerStore.addressBook.set(id, peerRecord.multiaddrs.map((addr) => multiaddr(addr))) this.peerStore.protoBook.set(id, protocols) // TODO: Track our observed address so that we can score it @@ -193,16 +246,20 @@ class IdentifyService { switch (protocol) { case MULTICODEC_IDENTIFY: return this._handleIdentify({ connection, stream }) + case MULTICODEC_IDENTIFY_LEGACY: + return this._handleIdentifyLegacy({ connection, stream }) case MULTICODEC_IDENTIFY_PUSH: return this._handlePush({ connection, stream }) + case MULTICODEC_IDENTIFY_PUSH_LEGACY: + return this._handlePushLegacy({ connection, stream }) default: log.error('cannot handle unknown protocol %s', protocol) } } /** - * Sends the `Identify` response to the requesting peer over the - * given `connection` + * Sends the `Identify` response with the Signed Peer Record + * to the requesting peer over the given `connection` * @private * @param {object} options * @param {*} options.stream @@ -214,6 +271,40 @@ class IdentifyService { publicKey = this.peerId.pubKey.bytes } + const envelope = this._libp2p.recordManager.getPeerRecord() + const signedPeerRecord = envelope.marshal() + + const message = Message.encode({ + protocolVersion: PROTOCOL_VERSION, + agentVersion: AGENT_VERSION, + publicKey, + signedPeerRecord, + observedAddr: connection.remoteAddr.buffer, + protocols: Array.from(this._protocols.keys()) + }) + + pipe( + [message], + lp.encode(), + stream, + consume + ) + } + + /** + * Sends the `Identify` response with listen addresses (LEGACY) + * to the requesting peer over the given `connection` + * @private + * @param {object} options + * @param {*} options.stream + * @param {Connection} options.connection + */ + _handleIdentifyLegacy ({ connection, stream }) { + let publicKey = Buffer.alloc(0) + if (this.peerId.pubKey) { + publicKey = this.peerId.pubKey.bytes + } + const message = Message.encode({ protocolVersion: PROTOCOL_VERSION, agentVersion: AGENT_VERSION, @@ -258,6 +349,63 @@ class IdentifyService { return log.error('received invalid message', err) } + // Open envelope and verify if is authenticated + let envelope + try { + envelope = await Envelope.openAndCertify(message.signedPeerRecord, PeerRecord.DOMAIN) + } catch (err) { + log('received invalid envelope, discard it') + throw errCode(new Error(messages.ERR_INVALID_ENVELOPE), codes.ERR_INVALID_ENVELOPE) + } + + // Decode peer record + let peerRecord + try { + peerRecord = await PeerRecord.createFromProtobuf(envelope.payload) + } catch (err) { + log('received invalid peer record, discard it') + throw errCode(new Error(messages.ERR_INVALID_PEER_RECORD), codes.ERR_INVALID_PEER_RECORD) + } + + // Update peers data in PeerStore + const id = connection.remotePeer + try { + // TODO: Store as certified record + + this.peerStore.addressBook.set(id, peerRecord.multiaddrs.map((addr) => multiaddr(addr))) + } catch (err) { + return log.error('received invalid listen addrs', err) + } + + // Update the protocols + this.peerStore.protoBook.set(id, message.protocols) + } + + /** + * Reads the Identify Push message from the given `connection` + * with listen addresses (LEGACY) + * @private + * @param {object} options + * @param {*} options.stream + * @param {Connection} options.connection + */ + async _handlePushLegacy ({ connection, stream }) { + const [data] = await pipe( + [], + stream, + lp.decode(), + take(1), + toBuffer, + collect + ) + + let message + try { + message = Message.decode(data) + } catch (err) { + return log.error('received invalid message', err) + } + // Update peers data in PeerStore const id = connection.remotePeer try { @@ -278,6 +426,8 @@ module.exports.IdentifyService = IdentifyService */ module.exports.multicodecs = { IDENTIFY: MULTICODEC_IDENTIFY, - IDENTIFY_PUSH: MULTICODEC_IDENTIFY_PUSH + IDENTIFY_LEGACY: MULTICODEC_IDENTIFY_LEGACY, + IDENTIFY_PUSH: MULTICODEC_IDENTIFY_PUSH, + IDENTIFY_PUSH_LEGACY: MULTICODEC_IDENTIFY_PUSH_LEGACY } module.exports.Message = Message diff --git a/src/identify/message.js b/src/identify/message.js index d81b1fd4b4..25b003f5c0 100644 --- a/src/identify/message.js +++ b/src/identify/message.js @@ -24,6 +24,11 @@ message Identify { optional bytes observedAddr = 4; repeated string protocols = 3; + + // signedPeerRecord contains a serialized SignedEnvelope containing a PeerRecord, + // signed by the sending node. It contains the same addresses as the listenAddrs field, but + // in a form that lets us share authenticated addrs with other peers. + optional bytes signedPeerRecord = 8; } ` diff --git a/src/index.js b/src/index.js index 1d90b55979..fa3ac8ce84 100644 --- a/src/index.js +++ b/src/index.js @@ -441,6 +441,9 @@ class Libp2p extends EventEmitter { // Listen on the provided transports await this.transportManager.listen() + // Start record Manager + await this.recordManager.start() + // Start PeerStore await this.peerStore.start() diff --git a/src/record/peer-record/index.js b/src/record/peer-record/index.js index 68f987e060..432f95010b 100644 --- a/src/record/peer-record/index.js +++ b/src/record/peer-record/index.js @@ -95,4 +95,6 @@ PeerRecord.createFromProtobuf = (buf) => { return new PeerRecord({ peerId, multiaddrs, seqNumber }) } +PeerRecord.DOMAIN = ENVELOPE_DOMAIN_PEER_RECORD + module.exports = PeerRecord diff --git a/test/identify/index.spec.js b/test/identify/index.spec.js index 9ca892ff05..6d7ca21423 100644 --- a/test/identify/index.spec.js +++ b/test/identify/index.spec.js @@ -14,6 +14,9 @@ const duplexPair = require('it-pair/duplex') const multiaddr = require('multiaddr') const pWaitFor = require('p-wait-for') +const Envelope = require('../../src/record-manager/envelope') +const PeerRecord = require('../../src/record-manager/peer-record') + const { codes: Errors } = require('../../src/errors') const { IdentifyService, multicodecs } = require('../../src/identify') const Peers = require('../fixtures/peers') @@ -22,14 +25,21 @@ const baseOptions = require('../utils/base-options.browser') const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser') const remoteAddr = MULTIADDRS_WEBSOCKETS[0] +const listenMaddrs = [multiaddr('/ip4/127.0.0.1/tcp/15002/ws')] + +const protocols = new Map([ + [multicodecs.IDENTIFY, () => { }], + [multicodecs.IDENTIFY_PUSH, () => { }] +]) + +const protocolsLegacy = new Map([ + [multicodecs.IDENTIFY_LEGACY, () => { }], + [multicodecs.IDENTIFY_PUSH_LEGACY, () => { }] +]) describe('Identify', () => { - let localPeer - let remotePeer - const protocols = new Map([ - [multicodecs.IDENTIFY, () => {}], - [multicodecs.IDENTIFY_PUSH, () => {}] - ]) + let localPeer, localPeerRecord + let remotePeer, remotePeerRecord before(async () => { [localPeer, remotePeer] = (await Promise.all([ @@ -38,10 +48,77 @@ describe('Identify', () => { ])) }) + // Compute peer records + before(async () => { + // Compute PeerRecords + const localRecord = new PeerRecord({ peerId: localPeer, multiaddrs: listenMaddrs }) + localPeerRecord = await Envelope.seal(localRecord, localPeer) + const remoteRecord = new PeerRecord({ peerId: remotePeer, multiaddrs: listenMaddrs }) + remotePeerRecord = await Envelope.seal(remoteRecord, remotePeer) + }) + afterEach(() => { sinon.restore() }) + it('should be able to identify another peer with legacy protocol', async () => { + const localIdentify = new IdentifyService({ + libp2p: { + peerId: localPeer, + connectionManager: new EventEmitter(), + peerStore: { + addressBook: { + set: () => { } + }, + protoBook: { + set: () => { } + } + }, + multiaddrs: listenMaddrs + }, + protocols: protocolsLegacy + }) + + const remoteIdentify = new IdentifyService({ + libp2p: { + peerId: remotePeer, + connectionManager: new EventEmitter(), + multiaddrs: listenMaddrs + }, + protocols: protocolsLegacy + }) + + const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234') + const localConnectionMock = { newStream: () => { }, remotePeer } + const remoteConnectionMock = { remoteAddr: observedAddr } + + const [local, remote] = duplexPair() + sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_LEGACY }) + + sinon.spy(localIdentify.peerStore.addressBook, 'set') + sinon.spy(localIdentify.peerStore.protoBook, 'set') + + // Run identify + await Promise.all([ + localIdentify.identify(localConnectionMock), + remoteIdentify.handleMessage({ + connection: remoteConnectionMock, + stream: remote, + protocol: multicodecs.IDENTIFY_LEGACY + }) + ]) + + expect(localIdentify.peerStore.addressBook.set.callCount).to.equal(1) + expect(localIdentify.peerStore.protoBook.set.callCount).to.equal(1) + + // Validate the remote peer gets updated in the peer store + const call = localIdentify.peerStore.addressBook.set.firstCall + expect(call.args[0].id.bytes).to.equal(remotePeer.bytes) + expect(call.args[1]).to.exist() + expect(call.args[1]).have.lengthOf(listenMaddrs.length) + expect(call.args[1][0].equals(listenMaddrs[0])) + }) + it('should be able to identify another peer', async () => { const localIdentify = new IdentifyService({ libp2p: { @@ -55,15 +132,22 @@ describe('Identify', () => { set: () => { } } }, - multiaddrs: [] + multiaddrs: [], + recordManager: { + getPeerRecord: () => localPeerRecord + } }, protocols }) + const remoteIdentify = new IdentifyService({ libp2p: { peerId: remotePeer, connectionManager: new EventEmitter(), - multiaddrs: [] + multiaddrs: [], + recordManager: { + getPeerRecord: () => remotePeerRecord + } }, protocols }) @@ -90,9 +174,13 @@ describe('Identify', () => { expect(localIdentify.peerStore.addressBook.set.callCount).to.equal(1) expect(localIdentify.peerStore.protoBook.set.callCount).to.equal(1) + // Validate the remote peer gets updated in the peer store const call = localIdentify.peerStore.addressBook.set.firstCall expect(call.args[0].id.bytes).to.equal(remotePeer.bytes) + expect(call.args[1]).to.exist() + expect(call.args[1]).have.lengthOf(listenMaddrs.length) + expect(call.args[1][0].equals(listenMaddrs[0])) }) it('should throw if identified peer is the wrong peer', async () => { @@ -108,7 +196,10 @@ describe('Identify', () => { set: () => { } } }, - multiaddrs: [] + multiaddrs: [], + recordManager: { + getPeerRecord: () => localPeerRecord + } }, protocols }) @@ -116,7 +207,10 @@ describe('Identify', () => { libp2p: { peerId: remotePeer, connectionManager: new EventEmitter(), - multiaddrs: [] + multiaddrs: [], + recordManager: { + getPeerRecord: () => remotePeerRecord + } }, protocols }) @@ -144,8 +238,7 @@ describe('Identify', () => { }) describe('push', () => { - it('should be able to push identify updates to another peer', async () => { - const listeningAddr = multiaddr('/ip4/127.0.0.1/tcp/1234') + it('should be able to push identify updates to another peer with legacy protocol', async () => { const connectionManager = new EventEmitter() connectionManager.getConnection = () => {} @@ -153,7 +246,73 @@ describe('Identify', () => { libp2p: { peerId: localPeer, connectionManager: new EventEmitter(), - multiaddrs: [listeningAddr] + multiaddrs: listenMaddrs + }, + protocols: new Map([ + [multicodecs.IDENTIFY_LEGACY], + [multicodecs.IDENTIFY_PUSH_LEGACY], + ['/echo/1.0.0'] + ]) + }) + const remoteIdentify = new IdentifyService({ + libp2p: { + peerId: remotePeer, + connectionManager, + peerStore: { + addressBook: { + set: () => { } + }, + protoBook: { + set: () => { } + } + }, + multiaddrs: [] + } + }) + + // Setup peer protocols and multiaddrs + const localProtocols = new Set([multicodecs.IDENTIFY_LEGACY, multicodecs.IDENTIFY_PUSH_LEGACY, '/echo/1.0.0']) + const localConnectionMock = { newStream: () => {} } + const remoteConnectionMock = { remotePeer: localPeer } + + const [local, remote] = duplexPair() + sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH_LEGACY }) + + sinon.spy(remoteIdentify.peerStore.addressBook, 'set') + sinon.spy(remoteIdentify.peerStore.protoBook, 'set') + + // Run identify + await Promise.all([ + localIdentify.push([localConnectionMock]), + remoteIdentify.handleMessage({ + connection: remoteConnectionMock, + stream: remote, + protocol: multicodecs.IDENTIFY_PUSH_LEGACY + }) + ]) + + expect(remoteIdentify.peerStore.addressBook.set.callCount).to.equal(1) + expect(remoteIdentify.peerStore.protoBook.set.callCount).to.equal(1) + const [peerId, multiaddrs] = remoteIdentify.peerStore.addressBook.set.firstCall.args + expect(peerId.bytes).to.eql(localPeer.bytes) + expect(multiaddrs).to.eql(listenMaddrs) + const [peerId2, protocols] = remoteIdentify.peerStore.protoBook.set.firstCall.args + expect(peerId2.bytes).to.eql(localPeer.bytes) + expect(protocols).to.eql(Array.from(localProtocols)) + }) + + it('should be able to push identify updates to another peer', async () => { + const connectionManager = new EventEmitter() + connectionManager.getConnection = () => { } + + const localIdentify = new IdentifyService({ + libp2p: { + peerId: localPeer, + connectionManager: new EventEmitter(), + multiaddrs: listenMaddrs, + recordManager: { + getPeerRecord: () => localPeerRecord + } }, protocols: new Map([ [multicodecs.IDENTIFY], @@ -173,13 +332,16 @@ describe('Identify', () => { set: () => { } } }, - multiaddrs: [] + multiaddrs: [], + recordManager: { + getPeerRecord: () => remotePeerRecord + } } }) // Setup peer protocols and multiaddrs const localProtocols = new Set([multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0']) - const localConnectionMock = { newStream: () => {} } + const localConnectionMock = { newStream: () => { } } const remoteConnectionMock = { remotePeer: localPeer } const [local, remote] = duplexPair() @@ -202,7 +364,7 @@ describe('Identify', () => { expect(remoteIdentify.peerStore.protoBook.set.callCount).to.equal(1) const [peerId, multiaddrs] = remoteIdentify.peerStore.addressBook.set.firstCall.args expect(peerId.bytes).to.eql(localPeer.bytes) - expect(multiaddrs).to.eql([listeningAddr]) + expect(multiaddrs).to.eql(listenMaddrs) const [peerId2, protocols] = remoteIdentify.peerStore.protoBook.set.firstCall.args expect(peerId2.bytes).to.eql(localPeer.bytes) expect(protocols).to.eql(Array.from(localProtocols)) @@ -234,6 +396,8 @@ describe('Identify', () => { peerId }) + await libp2p.start() + sinon.spy(libp2p.identifyService, 'identify') const peerStoreSpySet = sinon.spy(libp2p.peerStore.addressBook, 'set') const peerStoreSpyAdd = sinon.spy(libp2p.peerStore.addressBook, 'add') @@ -257,6 +421,8 @@ describe('Identify', () => { peerId }) + await libp2p.start() + sinon.spy(libp2p.identifyService, 'identify') sinon.spy(libp2p.identifyService, 'push') From eb0b02026a7653b02e68dba272158bea37319f9e Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Wed, 24 Jun 2020 15:35:18 +0200 Subject: [PATCH 2/6] feat: create self peer record in identify --- src/identify/index.js | 39 ++++++++++++++++++++++++------- src/index.js | 3 --- test/identify/index.spec.js | 46 +++++++------------------------------ 3 files changed, 39 insertions(+), 49 deletions(-) diff --git a/src/identify/index.js b/src/identify/index.js index 65d6f7f044..137c38b4a7 100644 --- a/src/identify/index.js +++ b/src/identify/index.js @@ -1,7 +1,11 @@ 'use strict' -const { Buffer } = require('buffer') const debug = require('debug') +const log = debug('libp2p:identify') +log.error = debug('libp2p:identify:error') + +const errCode = require('err-code') +const { Buffer } = require('buffer') const pb = require('it-protocol-buffers') const lp = require('it-length-prefixed') const pipe = require('it-pipe') @@ -13,8 +17,8 @@ const { toBuffer } = require('it-buffer') const Message = require('./message') -const log = debug('libp2p:identify') -log.error = debug('libp2p:identify:error') +const Envelope = require('../record/envelope') +const PeerRecord = require('../record/peer-record') const { MULTICODEC_IDENTIFY, @@ -25,10 +29,7 @@ const { PROTOCOL_VERSION } = require('./consts') -const errCode = require('err-code') const { messages, codes } = require('../errors') -const Envelope = require('../record-manager/envelope') -const PeerRecord = require('../record-manager/peer-record') class IdentifyService { /** @@ -83,6 +84,9 @@ class IdentifyService { this._protocols = protocols this.handleMessage = this.handleMessage.bind(this) + + // TODO: this should be stored in the certified AddressBook in follow up PR + this._selfRecord = undefined } /** @@ -108,7 +112,7 @@ class IdentifyService { ) } - const envelope = this._libp2p.recordManager.getPeerRecord() + const envelope = await this._getSelfPeerRecord() const signedPeerRecord = envelope.marshal() await pipe( @@ -271,7 +275,7 @@ class IdentifyService { publicKey = this.peerId.pubKey.bytes } - const envelope = this._libp2p.recordManager.getPeerRecord() + const envelope = await this._getSelfPeerRecord() const signedPeerRecord = envelope.marshal() const message = Message.encode({ @@ -417,6 +421,25 @@ class IdentifyService { // Update the protocols this.peerStore.protoBook.set(id, message.protocols) } + + /** + * Get self signed peer record envelope. + * @return {Envelope} + */ + async _getSelfPeerRecord () { + // TODO: Verify if updated + if (this._selfRecord) { + return this._selfRecord + } + + const peerRecord = new PeerRecord({ + peerId: this.peerId, + multiaddrs: this._libp2p.multiaddrs + }) + this._selfRecord = await Envelope.seal(peerRecord, this.peerId) + + return this._selfRecord + } } module.exports.IdentifyService = IdentifyService diff --git a/src/index.js b/src/index.js index fa3ac8ce84..1d90b55979 100644 --- a/src/index.js +++ b/src/index.js @@ -441,9 +441,6 @@ class Libp2p extends EventEmitter { // Listen on the provided transports await this.transportManager.listen() - // Start record Manager - await this.recordManager.start() - // Start PeerStore await this.peerStore.start() diff --git a/test/identify/index.spec.js b/test/identify/index.spec.js index 6d7ca21423..4a9b47ae0a 100644 --- a/test/identify/index.spec.js +++ b/test/identify/index.spec.js @@ -14,9 +14,6 @@ const duplexPair = require('it-pair/duplex') const multiaddr = require('multiaddr') const pWaitFor = require('p-wait-for') -const Envelope = require('../../src/record-manager/envelope') -const PeerRecord = require('../../src/record-manager/peer-record') - const { codes: Errors } = require('../../src/errors') const { IdentifyService, multicodecs } = require('../../src/identify') const Peers = require('../fixtures/peers') @@ -38,8 +35,8 @@ const protocolsLegacy = new Map([ ]) describe('Identify', () => { - let localPeer, localPeerRecord - let remotePeer, remotePeerRecord + let localPeer + let remotePeer before(async () => { [localPeer, remotePeer] = (await Promise.all([ @@ -48,15 +45,6 @@ describe('Identify', () => { ])) }) - // Compute peer records - before(async () => { - // Compute PeerRecords - const localRecord = new PeerRecord({ peerId: localPeer, multiaddrs: listenMaddrs }) - localPeerRecord = await Envelope.seal(localRecord, localPeer) - const remoteRecord = new PeerRecord({ peerId: remotePeer, multiaddrs: listenMaddrs }) - remotePeerRecord = await Envelope.seal(remoteRecord, remotePeer) - }) - afterEach(() => { sinon.restore() }) @@ -132,10 +120,7 @@ describe('Identify', () => { set: () => { } } }, - multiaddrs: [], - recordManager: { - getPeerRecord: () => localPeerRecord - } + multiaddrs: listenMaddrs }, protocols }) @@ -144,10 +129,7 @@ describe('Identify', () => { libp2p: { peerId: remotePeer, connectionManager: new EventEmitter(), - multiaddrs: [], - recordManager: { - getPeerRecord: () => remotePeerRecord - } + multiaddrs: listenMaddrs }, protocols }) @@ -196,10 +178,7 @@ describe('Identify', () => { set: () => { } } }, - multiaddrs: [], - recordManager: { - getPeerRecord: () => localPeerRecord - } + multiaddrs: [] }, protocols }) @@ -207,10 +186,7 @@ describe('Identify', () => { libp2p: { peerId: remotePeer, connectionManager: new EventEmitter(), - multiaddrs: [], - recordManager: { - getPeerRecord: () => remotePeerRecord - } + multiaddrs: [] }, protocols }) @@ -309,10 +285,7 @@ describe('Identify', () => { libp2p: { peerId: localPeer, connectionManager: new EventEmitter(), - multiaddrs: listenMaddrs, - recordManager: { - getPeerRecord: () => localPeerRecord - } + multiaddrs: listenMaddrs }, protocols: new Map([ [multicodecs.IDENTIFY], @@ -332,10 +305,7 @@ describe('Identify', () => { set: () => { } } }, - multiaddrs: [], - recordManager: { - getPeerRecord: () => remotePeerRecord - } + multiaddrs: [] } }) From c2fa11a0aa173df1e47f6505d325265f252aeaf0 Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Wed, 24 Jun 2020 15:54:24 +0200 Subject: [PATCH 3/6] chore: address review --- src/identify/consts.js | 4 +- src/identify/index.js | 135 +++++++++--------------------------- test/identify/index.spec.js | 22 +++--- 3 files changed, 47 insertions(+), 114 deletions(-) diff --git a/src/identify/consts.js b/src/identify/consts.js index 3ec0ba7a79..30dcd7ab5a 100644 --- a/src/identify/consts.js +++ b/src/identify/consts.js @@ -6,5 +6,5 @@ module.exports.MULTICODEC_IDENTIFY = '/p2p/id/1.1.0' module.exports.MULTICODEC_IDENTIFY_PUSH = '/p2p/id/push/1.1.0' // Legacy -module.exports.MULTICODEC_IDENTIFY_LEGACY = '/ipfs/id/1.0.0' -module.exports.MULTICODEC_IDENTIFY_PUSH_LEGACY = '/ipfs/id/push/1.0.0' +module.exports.MULTICODEC_IDENTIFY_1_0_0 = '/ipfs/id/1.0.0' +module.exports.MULTICODEC_IDENTIFY_PUSH_1_0_0 = '/ipfs/id/push/1.0.0' diff --git a/src/identify/index.js b/src/identify/index.js index 137c38b4a7..c6c872ef31 100644 --- a/src/identify/index.js +++ b/src/identify/index.js @@ -22,9 +22,9 @@ const PeerRecord = require('../record/peer-record') const { MULTICODEC_IDENTIFY, - MULTICODEC_IDENTIFY_LEGACY, + MULTICODEC_IDENTIFY_1_0_0, MULTICODEC_IDENTIFY_PUSH, - MULTICODEC_IDENTIFY_PUSH_LEGACY, + MULTICODEC_IDENTIFY_PUSH_1_0_0, AGENT_VERSION, PROTOCOL_VERSION } = require('./consts') @@ -97,26 +97,12 @@ class IdentifyService { push (connections) { const pushes = connections.map(async connection => { try { - const { protocol, stream } = await connection.newStream([MULTICODEC_IDENTIFY_PUSH, MULTICODEC_IDENTIFY_PUSH_LEGACY]) - - // Handle Legacy - if (protocol === MULTICODEC_IDENTIFY_PUSH_LEGACY) { - return pipe( - [{ - listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer), - protocols: Array.from(this._protocols.keys()) - }], - pb.encode(Message), - stream, - consume - ) - } - - const envelope = await this._getSelfPeerRecord() - const signedPeerRecord = envelope.marshal() + const { stream } = await connection.newStream([MULTICODEC_IDENTIFY_PUSH, MULTICODEC_IDENTIFY_PUSH_1_0_0]) + const signedPeerRecord = await this._getSelfPeerRecord() await pipe( [{ + listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer), signedPeerRecord, protocols: Array.from(this._protocols.keys()) }], @@ -159,7 +145,7 @@ class IdentifyService { * @returns {Promise} */ async identify (connection) { - const { protocol, stream } = await connection.newStream([MULTICODEC_IDENTIFY, MULTICODEC_IDENTIFY_LEGACY]) + const { protocol, stream } = await connection.newStream([MULTICODEC_IDENTIFY, MULTICODEC_IDENTIFY_1_0_0]) const [data] = await pipe( [], stream, @@ -198,7 +184,7 @@ class IdentifyService { observedAddr = IdentifyService.getCleanMultiaddr(observedAddr) // LEGACY: differentiate message with SignedPeerRecord - if (protocol === MULTICODEC_IDENTIFY_LEGACY) { + if (protocol === MULTICODEC_IDENTIFY_1_0_0) { // Update peers data in PeerStore this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr))) this.peerStore.protoBook.set(id, protocols) @@ -249,13 +235,11 @@ class IdentifyService { handleMessage ({ connection, stream, protocol }) { switch (protocol) { case MULTICODEC_IDENTIFY: + case MULTICODEC_IDENTIFY_1_0_0: return this._handleIdentify({ connection, stream }) - case MULTICODEC_IDENTIFY_LEGACY: - return this._handleIdentifyLegacy({ connection, stream }) case MULTICODEC_IDENTIFY_PUSH: + case MULTICODEC_IDENTIFY_PUSH_1_0_0: return this._handlePush({ connection, stream }) - case MULTICODEC_IDENTIFY_PUSH_LEGACY: - return this._handlePushLegacy({ connection, stream }) default: log.error('cannot handle unknown protocol %s', protocol) } @@ -275,45 +259,14 @@ class IdentifyService { publicKey = this.peerId.pubKey.bytes } - const envelope = await this._getSelfPeerRecord() - const signedPeerRecord = envelope.marshal() - - const message = Message.encode({ - protocolVersion: PROTOCOL_VERSION, - agentVersion: AGENT_VERSION, - publicKey, - signedPeerRecord, - observedAddr: connection.remoteAddr.buffer, - protocols: Array.from(this._protocols.keys()) - }) - - pipe( - [message], - lp.encode(), - stream, - consume - ) - } - - /** - * Sends the `Identify` response with listen addresses (LEGACY) - * to the requesting peer over the given `connection` - * @private - * @param {object} options - * @param {*} options.stream - * @param {Connection} options.connection - */ - _handleIdentifyLegacy ({ connection, stream }) { - let publicKey = Buffer.alloc(0) - if (this.peerId.pubKey) { - publicKey = this.peerId.pubKey.bytes - } + const signedPeerRecord = await this._getSelfPeerRecord() const message = Message.encode({ protocolVersion: PROTOCOL_VERSION, agentVersion: AGENT_VERSION, publicKey, listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer), + signedPeerRecord, observedAddr: connection.remoteAddr.buffer, protocols: Array.from(this._protocols.keys()) }) @@ -353,6 +306,22 @@ class IdentifyService { return log.error('received invalid message', err) } + const id = connection.remotePeer + + // Legacy + if (!message.signedPeerRecord) { + try { + this.peerStore.addressBook.set(id, message.listenAddrs.map((addr) => multiaddr(addr))) + } catch (err) { + return log.error('received invalid listen addrs', err) + } + + // Update the protocols + this.peerStore.protoBook.set(id, message.protocols) + + return + } + // Open envelope and verify if is authenticated let envelope try { @@ -372,7 +341,6 @@ class IdentifyService { } // Update peers data in PeerStore - const id = connection.remotePeer try { // TODO: Store as certified record @@ -386,45 +354,8 @@ class IdentifyService { } /** - * Reads the Identify Push message from the given `connection` - * with listen addresses (LEGACY) - * @private - * @param {object} options - * @param {*} options.stream - * @param {Connection} options.connection - */ - async _handlePushLegacy ({ connection, stream }) { - const [data] = await pipe( - [], - stream, - lp.decode(), - take(1), - toBuffer, - collect - ) - - let message - try { - message = Message.decode(data) - } catch (err) { - return log.error('received invalid message', err) - } - - // Update peers data in PeerStore - const id = connection.remotePeer - try { - this.peerStore.addressBook.set(id, message.listenAddrs.map((addr) => multiaddr(addr))) - } catch (err) { - return log.error('received invalid listen addrs', err) - } - - // Update the protocols - this.peerStore.protoBook.set(id, message.protocols) - } - - /** - * Get self signed peer record envelope. - * @return {Envelope} + * Get self signed peer record raw envelope. + * @return {Buffer} */ async _getSelfPeerRecord () { // TODO: Verify if updated @@ -436,7 +367,9 @@ class IdentifyService { peerId: this.peerId, multiaddrs: this._libp2p.multiaddrs }) - this._selfRecord = await Envelope.seal(peerRecord, this.peerId) + const envelope = await Envelope.seal(peerRecord, this.peerId) + + this._selfRecord = envelope.marshal() return this._selfRecord } @@ -449,8 +382,8 @@ module.exports.IdentifyService = IdentifyService */ module.exports.multicodecs = { IDENTIFY: MULTICODEC_IDENTIFY, - IDENTIFY_LEGACY: MULTICODEC_IDENTIFY_LEGACY, + IDENTIFY_1_0_0: MULTICODEC_IDENTIFY_1_0_0, IDENTIFY_PUSH: MULTICODEC_IDENTIFY_PUSH, - IDENTIFY_PUSH_LEGACY: MULTICODEC_IDENTIFY_PUSH_LEGACY + IDENTIFY_PUSH_1_0_0: MULTICODEC_IDENTIFY_PUSH_1_0_0 } module.exports.Message = Message diff --git a/test/identify/index.spec.js b/test/identify/index.spec.js index 4a9b47ae0a..d5d1ed67bb 100644 --- a/test/identify/index.spec.js +++ b/test/identify/index.spec.js @@ -30,8 +30,8 @@ const protocols = new Map([ ]) const protocolsLegacy = new Map([ - [multicodecs.IDENTIFY_LEGACY, () => { }], - [multicodecs.IDENTIFY_PUSH_LEGACY, () => { }] + [multicodecs.IDENTIFY_1_0_0, () => { }], + [multicodecs.IDENTIFY_PUSH_1_0_0, () => { }] ]) describe('Identify', () => { @@ -49,7 +49,7 @@ describe('Identify', () => { sinon.restore() }) - it('should be able to identify another peer with legacy protocol', async () => { + it('should be able to identify another peer with 1.0.0 legacy protocol', async () => { const localIdentify = new IdentifyService({ libp2p: { peerId: localPeer, @@ -81,7 +81,7 @@ describe('Identify', () => { const remoteConnectionMock = { remoteAddr: observedAddr } const [local, remote] = duplexPair() - sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_LEGACY }) + sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_1_0_0 }) sinon.spy(localIdentify.peerStore.addressBook, 'set') sinon.spy(localIdentify.peerStore.protoBook, 'set') @@ -92,7 +92,7 @@ describe('Identify', () => { remoteIdentify.handleMessage({ connection: remoteConnectionMock, stream: remote, - protocol: multicodecs.IDENTIFY_LEGACY + protocol: multicodecs.IDENTIFY_1_0_0 }) ]) @@ -214,7 +214,7 @@ describe('Identify', () => { }) describe('push', () => { - it('should be able to push identify updates to another peer with legacy protocol', async () => { + it('should be able to push identify updates to another peer with 1.0.0 legacy protocols', async () => { const connectionManager = new EventEmitter() connectionManager.getConnection = () => {} @@ -225,8 +225,8 @@ describe('Identify', () => { multiaddrs: listenMaddrs }, protocols: new Map([ - [multicodecs.IDENTIFY_LEGACY], - [multicodecs.IDENTIFY_PUSH_LEGACY], + [multicodecs.IDENTIFY_1_0_0], + [multicodecs.IDENTIFY_PUSH_1_0_0], ['/echo/1.0.0'] ]) }) @@ -247,12 +247,12 @@ describe('Identify', () => { }) // Setup peer protocols and multiaddrs - const localProtocols = new Set([multicodecs.IDENTIFY_LEGACY, multicodecs.IDENTIFY_PUSH_LEGACY, '/echo/1.0.0']) + const localProtocols = new Set([multicodecs.IDENTIFY_1_0_0, multicodecs.IDENTIFY_PUSH_1_0_0, '/echo/1.0.0']) const localConnectionMock = { newStream: () => {} } const remoteConnectionMock = { remotePeer: localPeer } const [local, remote] = duplexPair() - sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH_LEGACY }) + sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH_1_0_0 }) sinon.spy(remoteIdentify.peerStore.addressBook, 'set') sinon.spy(remoteIdentify.peerStore.protoBook, 'set') @@ -263,7 +263,7 @@ describe('Identify', () => { remoteIdentify.handleMessage({ connection: remoteConnectionMock, stream: remote, - protocol: multicodecs.IDENTIFY_PUSH_LEGACY + protocol: multicodecs.IDENTIFY_PUSH_1_0_0 }) ]) From d67190ba5809d330e6acb19b309cfed4d803cf8f Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Wed, 15 Jul 2020 15:47:45 +0200 Subject: [PATCH 4/6] fix: revert new identify protocol versions --- src/errors.js | 6 +- src/identify/consts.js | 8 +-- src/identify/index.js | 111 ++++++++++++------------------------ test/identify/index.spec.js | 57 +++++++++--------- 4 files changed, 68 insertions(+), 114 deletions(-) diff --git a/src/errors.js b/src/errors.js index 079e5c89cd..18e600c6dc 100644 --- a/src/errors.js +++ b/src/errors.js @@ -3,9 +3,7 @@ exports.messages = { NOT_STARTED_YET: 'The libp2p node is not started yet', DHT_DISABLED: 'DHT is not available', - CONN_ENCRYPTION_REQUIRED: 'At least one connection encryption module is required', - ERR_INVALID_ENVELOPE: 'Invalid envelope received', - ERR_INVALID_PEER_RECORD: 'Invalid peer record received' + CONN_ENCRYPTION_REQUIRED: 'At least one connection encryption module is required' } exports.codes = { @@ -22,8 +20,6 @@ exports.codes = { ERR_DUPLICATE_TRANSPORT: 'ERR_DUPLICATE_TRANSPORT', ERR_ENCRYPTION_FAILED: 'ERR_ENCRYPTION_FAILED', ERR_HOP_REQUEST_FAILED: 'ERR_HOP_REQUEST_FAILED', - ERR_INVALID_ENVELOPE: 'ERR_INVALID_ENVELOPE', - ERR_INVALID_PEER_RECORD: 'ERR_INVALID_PEER_RECORD', ERR_INVALID_KEY: 'ERR_INVALID_KEY', ERR_INVALID_MESSAGE: 'ERR_INVALID_MESSAGE', ERR_INVALID_PARAMETERS: 'ERR_INVALID_PARAMETERS', diff --git a/src/identify/consts.js b/src/identify/consts.js index 30dcd7ab5a..ae8d8432df 100644 --- a/src/identify/consts.js +++ b/src/identify/consts.js @@ -2,9 +2,5 @@ module.exports.PROTOCOL_VERSION = 'ipfs/0.1.0' module.exports.AGENT_VERSION = 'js-libp2p/0.1.0' -module.exports.MULTICODEC_IDENTIFY = '/p2p/id/1.1.0' -module.exports.MULTICODEC_IDENTIFY_PUSH = '/p2p/id/push/1.1.0' - -// Legacy -module.exports.MULTICODEC_IDENTIFY_1_0_0 = '/ipfs/id/1.0.0' -module.exports.MULTICODEC_IDENTIFY_PUSH_1_0_0 = '/ipfs/id/push/1.0.0' +module.exports.MULTICODEC_IDENTIFY = '/ipfs/id/1.0.0' +module.exports.MULTICODEC_IDENTIFY_PUSH = '/ipfs/id/push/1.0.0' diff --git a/src/identify/index.js b/src/identify/index.js index c6c872ef31..f2de9a25d9 100644 --- a/src/identify/index.js +++ b/src/identify/index.js @@ -22,14 +22,12 @@ const PeerRecord = require('../record/peer-record') const { MULTICODEC_IDENTIFY, - MULTICODEC_IDENTIFY_1_0_0, MULTICODEC_IDENTIFY_PUSH, - MULTICODEC_IDENTIFY_PUSH_1_0_0, AGENT_VERSION, PROTOCOL_VERSION } = require('./consts') -const { messages, codes } = require('../errors') +const { codes } = require('../errors') class IdentifyService { /** @@ -97,7 +95,7 @@ class IdentifyService { push (connections) { const pushes = connections.map(async connection => { try { - const { stream } = await connection.newStream([MULTICODEC_IDENTIFY_PUSH, MULTICODEC_IDENTIFY_PUSH_1_0_0]) + const { stream } = await connection.newStream(MULTICODEC_IDENTIFY_PUSH) const signedPeerRecord = await this._getSelfPeerRecord() await pipe( @@ -145,7 +143,7 @@ class IdentifyService { * @returns {Promise} */ async identify (connection) { - const { protocol, stream } = await connection.newStream([MULTICODEC_IDENTIFY, MULTICODEC_IDENTIFY_1_0_0]) + const { stream } = await connection.newStream(MULTICODEC_IDENTIFY) const [data] = await pipe( [], stream, @@ -183,40 +181,26 @@ class IdentifyService { // Get the observedAddr if there is one observedAddr = IdentifyService.getCleanMultiaddr(observedAddr) - // LEGACY: differentiate message with SignedPeerRecord - if (protocol === MULTICODEC_IDENTIFY_1_0_0) { - // Update peers data in PeerStore - this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr))) - this.peerStore.protoBook.set(id, protocols) + let addresses - // TODO: Track our observed address so that we can score it - log('received observed address of %s', observedAddr) - - return - } - - // Open envelope and verify if is authenticated - let envelope try { - envelope = await Envelope.openAndCertify(signedPeerRecord, PeerRecord.DOMAIN) + const envelope = await Envelope.openAndCertify(signedPeerRecord, PeerRecord.DOMAIN) + const peerRecord = await PeerRecord.createFromProtobuf(envelope.payload) + + addresses = peerRecord.multiaddrs } catch (err) { - log('received invalid envelope, discard it') - throw errCode(new Error(messages.ERR_INVALID_ENVELOPE), codes.ERR_INVALID_ENVELOPE) + log('received invalid envelope, discard it and fallback to listenAddrs is available') + // Try Legacy + addresses = listenAddrs } - // Decode peer record - let peerRecord + // Update peers data in PeerStore try { - peerRecord = await PeerRecord.createFromProtobuf(envelope.payload) + this.peerStore.addressBook.set(id, addresses.map((addr) => multiaddr(addr))) } catch (err) { - log('received invalid peer record, discard it') - throw errCode(new Error(messages.ERR_INVALID_PEER_RECORD), codes.ERR_INVALID_PEER_RECORD) + log.error('received invalid addrs', err) } - // TODO: Store as certified record - - // Update peers data in PeerStore - this.peerStore.addressBook.set(id, peerRecord.multiaddrs.map((addr) => multiaddr(addr))) this.peerStore.protoBook.set(id, protocols) // TODO: Track our observed address so that we can score it @@ -235,10 +219,8 @@ class IdentifyService { handleMessage ({ connection, stream, protocol }) { switch (protocol) { case MULTICODEC_IDENTIFY: - case MULTICODEC_IDENTIFY_1_0_0: return this._handleIdentify({ connection, stream }) case MULTICODEC_IDENTIFY_PUSH: - case MULTICODEC_IDENTIFY_PUSH_1_0_0: return this._handlePush({ connection, stream }) default: log.error('cannot handle unknown protocol %s', protocol) @@ -308,45 +290,23 @@ class IdentifyService { const id = connection.remotePeer - // Legacy - if (!message.signedPeerRecord) { - try { - this.peerStore.addressBook.set(id, message.listenAddrs.map((addr) => multiaddr(addr))) - } catch (err) { - return log.error('received invalid listen addrs', err) - } - - // Update the protocols - this.peerStore.protoBook.set(id, message.protocols) - - return - } + let addresses - // Open envelope and verify if is authenticated - let envelope try { - envelope = await Envelope.openAndCertify(message.signedPeerRecord, PeerRecord.DOMAIN) - } catch (err) { - log('received invalid envelope, discard it') - throw errCode(new Error(messages.ERR_INVALID_ENVELOPE), codes.ERR_INVALID_ENVELOPE) - } + const envelope = await Envelope.openAndCertify(message.signedPeerRecord, PeerRecord.DOMAIN) + const peerRecord = await PeerRecord.createFromProtobuf(envelope.payload) - // Decode peer record - let peerRecord - try { - peerRecord = await PeerRecord.createFromProtobuf(envelope.payload) + addresses = peerRecord.multiaddrs } catch (err) { - log('received invalid peer record, discard it') - throw errCode(new Error(messages.ERR_INVALID_PEER_RECORD), codes.ERR_INVALID_PEER_RECORD) + log('received invalid envelope, discard it and fallback to listenAddrs is available') + // Try Legacy + addresses = message.listenAddrs } - // Update peers data in PeerStore try { - // TODO: Store as certified record - - this.peerStore.addressBook.set(id, peerRecord.multiaddrs.map((addr) => multiaddr(addr))) + this.peerStore.addressBook.set(id, addresses.map((addr) => multiaddr(addr))) } catch (err) { - return log.error('received invalid listen addrs', err) + log.error('received invalid addrs', err) } // Update the protocols @@ -358,20 +318,25 @@ class IdentifyService { * @return {Buffer} */ async _getSelfPeerRecord () { - // TODO: Verify if updated + // TODO: support invalidation when dynamic multiaddrs are supported if (this._selfRecord) { return this._selfRecord } - const peerRecord = new PeerRecord({ - peerId: this.peerId, - multiaddrs: this._libp2p.multiaddrs - }) - const envelope = await Envelope.seal(peerRecord, this.peerId) + try { + const peerRecord = new PeerRecord({ + peerId: this.peerId, + multiaddrs: this._libp2p.multiaddrs + }) + const envelope = await Envelope.seal(peerRecord, this.peerId) - this._selfRecord = envelope.marshal() + this._selfRecord = envelope.marshal() - return this._selfRecord + return this._selfRecord + } catch (err) { + log.error('failed to get self peer record') + } + return null } } @@ -382,8 +347,6 @@ module.exports.IdentifyService = IdentifyService */ module.exports.multicodecs = { IDENTIFY: MULTICODEC_IDENTIFY, - IDENTIFY_1_0_0: MULTICODEC_IDENTIFY_1_0_0, - IDENTIFY_PUSH: MULTICODEC_IDENTIFY_PUSH, - IDENTIFY_PUSH_1_0_0: MULTICODEC_IDENTIFY_PUSH_1_0_0 + IDENTIFY_PUSH: MULTICODEC_IDENTIFY_PUSH } module.exports.Message = Message diff --git a/test/identify/index.spec.js b/test/identify/index.spec.js index d5d1ed67bb..4560bfd3d7 100644 --- a/test/identify/index.spec.js +++ b/test/identify/index.spec.js @@ -18,25 +18,20 @@ const { codes: Errors } = require('../../src/errors') const { IdentifyService, multicodecs } = require('../../src/identify') const Peers = require('../fixtures/peers') const Libp2p = require('../../src') +const Envelope = require('../../src/record/envelope') const baseOptions = require('../utils/base-options.browser') const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser') const remoteAddr = MULTIADDRS_WEBSOCKETS[0] const listenMaddrs = [multiaddr('/ip4/127.0.0.1/tcp/15002/ws')] -const protocols = new Map([ - [multicodecs.IDENTIFY, () => { }], - [multicodecs.IDENTIFY_PUSH, () => { }] -]) - -const protocolsLegacy = new Map([ - [multicodecs.IDENTIFY_1_0_0, () => { }], - [multicodecs.IDENTIFY_PUSH_1_0_0, () => { }] -]) - describe('Identify', () => { let localPeer let remotePeer + const protocols = new Map([ + [multicodecs.IDENTIFY, () => {}], + [multicodecs.IDENTIFY_PUSH, () => {}] + ]) before(async () => { [localPeer, remotePeer] = (await Promise.all([ @@ -49,7 +44,7 @@ describe('Identify', () => { sinon.restore() }) - it('should be able to identify another peer with 1.0.0 legacy protocol', async () => { + it('should be able to identify another peer', async () => { const localIdentify = new IdentifyService({ libp2p: { peerId: localPeer, @@ -64,7 +59,7 @@ describe('Identify', () => { }, multiaddrs: listenMaddrs }, - protocols: protocolsLegacy + protocols }) const remoteIdentify = new IdentifyService({ @@ -73,15 +68,15 @@ describe('Identify', () => { connectionManager: new EventEmitter(), multiaddrs: listenMaddrs }, - protocols: protocolsLegacy + protocols }) const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234') - const localConnectionMock = { newStream: () => { }, remotePeer } + const localConnectionMock = { newStream: () => {}, remotePeer } const remoteConnectionMock = { remoteAddr: observedAddr } const [local, remote] = duplexPair() - sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_1_0_0 }) + sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY }) sinon.spy(localIdentify.peerStore.addressBook, 'set') sinon.spy(localIdentify.peerStore.protoBook, 'set') @@ -92,7 +87,7 @@ describe('Identify', () => { remoteIdentify.handleMessage({ connection: remoteConnectionMock, stream: remote, - protocol: multicodecs.IDENTIFY_1_0_0 + protocol: multicodecs.IDENTIFY }) ]) @@ -107,17 +102,18 @@ describe('Identify', () => { expect(call.args[1][0].equals(listenMaddrs[0])) }) - it('should be able to identify another peer', async () => { + // LEGACY + it('should be able to identify another peer with no certified peer records support', async () => { const localIdentify = new IdentifyService({ libp2p: { peerId: localPeer, connectionManager: new EventEmitter(), peerStore: { addressBook: { - set: () => { } + set: () => {} }, protoBook: { - set: () => { } + set: () => {} } }, multiaddrs: listenMaddrs @@ -140,6 +136,7 @@ describe('Identify', () => { const [local, remote] = duplexPair() sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY }) + sinon.stub(Envelope, 'openAndCertify').throws() sinon.spy(localIdentify.peerStore.addressBook, 'set') sinon.spy(localIdentify.peerStore.protoBook, 'set') @@ -214,9 +211,9 @@ describe('Identify', () => { }) describe('push', () => { - it('should be able to push identify updates to another peer with 1.0.0 legacy protocols', async () => { + it('should be able to push identify updates to another peer', async () => { const connectionManager = new EventEmitter() - connectionManager.getConnection = () => {} + connectionManager.getConnection = () => { } const localIdentify = new IdentifyService({ libp2p: { @@ -225,8 +222,8 @@ describe('Identify', () => { multiaddrs: listenMaddrs }, protocols: new Map([ - [multicodecs.IDENTIFY_1_0_0], - [multicodecs.IDENTIFY_PUSH_1_0_0], + [multicodecs.IDENTIFY], + [multicodecs.IDENTIFY_PUSH], ['/echo/1.0.0'] ]) }) @@ -247,12 +244,12 @@ describe('Identify', () => { }) // Setup peer protocols and multiaddrs - const localProtocols = new Set([multicodecs.IDENTIFY_1_0_0, multicodecs.IDENTIFY_PUSH_1_0_0, '/echo/1.0.0']) - const localConnectionMock = { newStream: () => {} } + const localProtocols = new Set([multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0']) + const localConnectionMock = { newStream: () => { } } const remoteConnectionMock = { remotePeer: localPeer } const [local, remote] = duplexPair() - sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH_1_0_0 }) + sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH }) sinon.spy(remoteIdentify.peerStore.addressBook, 'set') sinon.spy(remoteIdentify.peerStore.protoBook, 'set') @@ -263,7 +260,7 @@ describe('Identify', () => { remoteIdentify.handleMessage({ connection: remoteConnectionMock, stream: remote, - protocol: multicodecs.IDENTIFY_PUSH_1_0_0 + protocol: multicodecs.IDENTIFY_PUSH }) ]) @@ -277,7 +274,8 @@ describe('Identify', () => { expect(protocols).to.eql(Array.from(localProtocols)) }) - it('should be able to push identify updates to another peer', async () => { + // LEGACY + it('should be able to push identify updates to another peer with no certified peer records support', async () => { const connectionManager = new EventEmitter() connectionManager.getConnection = () => { } @@ -311,11 +309,12 @@ describe('Identify', () => { // Setup peer protocols and multiaddrs const localProtocols = new Set([multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0']) - const localConnectionMock = { newStream: () => { } } + const localConnectionMock = { newStream: () => {} } const remoteConnectionMock = { remotePeer: localPeer } const [local, remote] = duplexPair() sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH }) + sinon.stub(Envelope, 'openAndCertify').throws() sinon.spy(remoteIdentify.peerStore.addressBook, 'set') sinon.spy(remoteIdentify.peerStore.protoBook, 'set') From 931b71b4bfb11c5757d00bd7796522c990de7bdf Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Wed, 15 Jul 2020 17:42:05 +0200 Subject: [PATCH 5/6] chore: apply suggestions from code review Co-authored-by: Jacob Heun --- src/identify/index.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/identify/index.js b/src/identify/index.js index f2de9a25d9..304352afe7 100644 --- a/src/identify/index.js +++ b/src/identify/index.js @@ -92,17 +92,20 @@ class IdentifyService { * @param {Array} connections * @returns {Promise} */ - push (connections) { + async push (connections) { + const signedPeerRecord = await this._getSelfPeerRecord() + const listenAddrs = this._libp2p.multiaddrs.map((ma) => ma.buffer) + const protocols = Array.from(this._protocols.keys()) + const pushes = connections.map(async connection => { try { const { stream } = await connection.newStream(MULTICODEC_IDENTIFY_PUSH) - const signedPeerRecord = await this._getSelfPeerRecord() await pipe( [{ - listenAddrs: this._libp2p.multiaddrs.map((ma) => ma.buffer), + listenAddrs, signedPeerRecord, - protocols: Array.from(this._protocols.keys()) + protocols }], pb.encode(Message), stream, @@ -189,7 +192,7 @@ class IdentifyService { addresses = peerRecord.multiaddrs } catch (err) { - log('received invalid envelope, discard it and fallback to listenAddrs is available') + log('received invalid envelope, discard it and fallback to listenAddrs is available', err) // Try Legacy addresses = listenAddrs } @@ -298,7 +301,7 @@ class IdentifyService { addresses = peerRecord.multiaddrs } catch (err) { - log('received invalid envelope, discard it and fallback to listenAddrs is available') + log('received invalid envelope, discard it and fallback to listenAddrs is available', err) // Try Legacy addresses = message.listenAddrs } From d935021e630f3882cf915ed1d9679d4ba7ba0d2a Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Wed, 15 Jul 2020 18:31:55 +0200 Subject: [PATCH 6/6] chore: do not get signed peer record n times