From 63583ab210645f75348ba0fcb5ca83391c3859af Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Mon, 30 Mar 2020 18:54:35 +0200 Subject: [PATCH] chore: integrate new peer-store with code using adapters for other modules --- src/dialer/index.js | 36 ++- src/get-peer-info.js | 2 +- src/identify/index.js | 55 +--- src/index.js | 13 +- src/peer-store/address-book.js | 58 +++- src/peer-store/book.js | 5 + src/peer-store/index.js | 323 +++++++++------------ src/peer-store/proto-book.js | 29 +- src/registrar.js | 26 +- src/upgrader.js | 4 +- test/content-routing/dht/operation.node.js | 7 +- test/dialing/direct.node.js | 15 +- test/dialing/direct.spec.js | 46 ++- test/dialing/relay.node.js | 7 +- test/identify/index.spec.js | 56 ++-- test/peer-discovery/index.spec.js | 8 +- test/peer-store/address-book.spec.js | 29 +- test/peer-store/peer-store.spec.js | 225 +++++--------- test/peer-store/proto-book.spec.js | 29 +- test/registrar/registrar.spec.js | 18 +- 20 files changed, 477 insertions(+), 514 deletions(-) diff --git a/src/dialer/index.js b/src/dialer/index.js index 7638ffe572..9d257cb5f3 100644 --- a/src/dialer/index.js +++ b/src/dialer/index.js @@ -5,7 +5,6 @@ const errCode = require('err-code') const TimeoutController = require('timeout-abort-controller') const anySignal = require('any-signal') const PeerId = require('peer-id') -const PeerInfo = require('peer-info') const debug = require('debug') const log = debug('libp2p:dialer') log.error = debug('libp2p:dialer:error') @@ -62,13 +61,13 @@ class Dialer { * The dial to the first address that is successfully able to upgrade a connection * will be used. * - * @param {PeerInfo|Multiaddr} peer The peer to dial + * @param {PeerId|Multiaddr} peerId The peer to dial * @param {object} [options] * @param {AbortSignal} [options.signal] An AbortController signal * @returns {Promise} */ - async connectToPeer (peer, options = {}) { - const dialTarget = this._createDialTarget(peer) + async connectToPeer (peerId, options = {}) { + const dialTarget = this._createDialTarget(peerId) if (dialTarget.addrs.length === 0) { throw errCode(new Error('The dial request has no addresses'), codes.ERR_NO_VALID_ADDRESSES) } @@ -100,7 +99,7 @@ class Dialer { * Creates a DialTarget. The DialTarget is used to create and track * the DialRequest to a given peer. * @private - * @param {PeerInfo|Multiaddr} peer A PeerId or Multiaddr + * @param {PeerId|Multiaddr} peer A PeerId or Multiaddr * @returns {DialTarget} */ _createDialTarget (peer) { @@ -111,7 +110,10 @@ class Dialer { addrs: [dialable] } } - const addrs = this.peerStore.multiaddrsForPeer(dialable) + + dialable.multiaddrs && this.peerStore.addressBook.set(dialable.id, Array.from(dialable.multiaddrs), { replace: false }) + const addrs = this.peerStore.addressBook.getMultiaddrsForPeer(dialable.id) + return { id: dialable.id.toB58String(), addrs @@ -179,21 +181,27 @@ class Dialer { this.tokens.push(token) } + /** + * PeerInfo object + * @typedef {Object} peerInfo + * @property {Multiaddr} multiaddr peer multiaddr. + * @property {PeerId} id peer id. + */ + /** * Converts the given `peer` into a `PeerInfo` or `Multiaddr`. * @static - * @param {PeerInfo|PeerId|Multiaddr|string} peer - * @returns {PeerInfo|Multiaddr} + * @param {PeerId|Multiaddr|string} peer + * @returns {peerInfo|Multiaddr} */ static getDialable (peer) { - if (PeerInfo.isPeerInfo(peer)) return peer if (typeof peer === 'string') { peer = multiaddr(peer) } - let addr + let addrs if (multiaddr.isMultiaddr(peer)) { - addr = peer + addrs = new Set([peer]) // TODO: after peer-info removal, a Set should not be needed try { peer = PeerId.createFromCID(peer.getPeerId()) } catch (err) { @@ -202,10 +210,12 @@ class Dialer { } if (PeerId.isPeerId(peer)) { - peer = new PeerInfo(peer) + peer = { + id: peer, + multiaddrs: addrs + } } - addr && peer.multiaddrs.add(addr) return peer } } diff --git a/src/get-peer-info.js b/src/get-peer-info.js index 01a6bc49f2..83135eebe4 100644 --- a/src/get-peer-info.js +++ b/src/get-peer-info.js @@ -38,7 +38,7 @@ function getPeerInfo (peer, peerStore) { addr && peer.multiaddrs.add(addr) - return peerStore ? peerStore.put(peer) : peer + return peerStore ? peerStore.put(peer, { replace: false }) : peer } /** diff --git a/src/identify/index.js b/src/identify/index.js index d8643611b9..3fe06f4f5c 100644 --- a/src/identify/index.js +++ b/src/identify/index.js @@ -6,7 +6,6 @@ const lp = require('it-length-prefixed') const pipe = require('it-pipe') const { collect, take, consume } = require('streaming-iterables') -const PeerInfo = require('peer-info') const PeerId = require('peer-id') const multiaddr = require('multiaddr') const { toBuffer } = require('it-buffer') @@ -27,39 +26,6 @@ const errCode = require('err-code') const { codes } = require('../errors') class IdentifyService { - /** - * Replaces the multiaddrs on the given `peerInfo`, - * with the provided `multiaddrs` - * @param {PeerInfo} peerInfo - * @param {Array|Array} multiaddrs - */ - static updatePeerAddresses (peerInfo, multiaddrs) { - if (multiaddrs && multiaddrs.length > 0) { - peerInfo.multiaddrs.clear() - multiaddrs.forEach(ma => { - try { - peerInfo.multiaddrs.add(ma) - } catch (err) { - log.error('could not add multiaddr', err) - } - }) - } - } - - /** - * Replaces the protocols on the given `peerInfo`, - * with the provided `protocols` - * @static - * @param {PeerInfo} peerInfo - * @param {Array} protocols - */ - static updatePeerProtocols (peerInfo, protocols) { - if (protocols && protocols.length > 0) { - peerInfo.protocols.clear() - protocols.forEach(proto => peerInfo.protocols.add(proto)) - } - } - /** * Takes the `addr` and converts it to a Multiaddr if possible * @param {Buffer|String} addr @@ -181,7 +147,7 @@ class IdentifyService { } = message const id = await PeerId.createFromPubKey(publicKey) - const peerInfo = new PeerInfo(id) + if (connection.remotePeer.toB58String() !== id.toB58String()) { throw errCode(new Error('identified peer does not match the expected peer'), codes.ERR_INVALID_PEER) } @@ -189,11 +155,10 @@ class IdentifyService { // Get the observedAddr if there is one observedAddr = IdentifyService.getCleanMultiaddr(observedAddr) - // Copy the listenAddrs and protocols - IdentifyService.updatePeerAddresses(peerInfo, listenAddrs) - IdentifyService.updatePeerProtocols(peerInfo, protocols) + // Update peers data in PeerStore + this.registrar.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr))) + this.registrar.peerStore.protoBook.set(id, protocols) - this.registrar.peerStore.replace(peerInfo) // TODO: Track our observed address so that we can score it log('received observed address of %s', observedAddr) } @@ -273,20 +238,16 @@ class IdentifyService { return log.error('received invalid message', err) } - // Update the listen addresses - const peerInfo = new PeerInfo(connection.remotePeer) - + // Update peers data in PeerStore + const id = connection.remotePeer try { - IdentifyService.updatePeerAddresses(peerInfo, message.listenAddrs) + this.registrar.peerStore.addressBook.set(id, message.listenAddrs.map((addr) => multiaddr(addr))) } catch (err) { return log.error('received invalid listen addrs', err) } // Update the protocols - IdentifyService.updatePeerProtocols(peerInfo, message.protocols) - - // Update the peer in the PeerStore - this.registrar.peerStore.replace(peerInfo) + this.registrar.peerStore.protoBook.set(id, message.protocols) } } diff --git a/src/index.js b/src/index.js index a7ee76b649..db597cae8b 100644 --- a/src/index.js +++ b/src/index.js @@ -59,7 +59,7 @@ class Libp2p extends EventEmitter { localPeer: this.peerInfo.id, metrics: this.metrics, onConnection: (connection) => { - const peerInfo = this.peerStore.put(new PeerInfo(connection.remotePeer), { silent: true }) + const peerInfo = new PeerInfo(connection.remotePeer) this.registrar.onConnect(peerInfo, connection) this.connectionManager.onConnect(connection) this.emit('peer:connect', peerInfo) @@ -289,7 +289,11 @@ class Libp2p extends EventEmitter { const dialable = Dialer.getDialable(peer) let connection if (PeerInfo.isPeerInfo(dialable)) { - this.peerStore.put(dialable, { silent: true }) + // TODO Inconsistency from: getDialable adds a set, while regular peerInfo uses a Multiaddr set + // This should be handled on `peer-info` removal + const multiaddrs = dialable.multiaddrs.toArray ? dialable.multiaddrs.toArray() : Array.from(dialable.multiaddrs) + this.peerStore.addressBook.set(dialable.id, multiaddrs, { replace: false }) + connection = this.registrar.getConnection(dialable) } @@ -430,7 +434,10 @@ class Libp2p extends EventEmitter { log.error(new Error(codes.ERR_DISCOVERED_SELF)) return } - this.peerStore.put(peerInfo) + + // TODO: once we deprecate peer-info, we should only set if we have data + this.peerStore.addressBook.set(peerInfo.id, peerInfo.multiaddrs.toArray(), { replace: false }) + this.peerStore.protoBook.set(peerInfo.id, Array.from(peerInfo.protocols), { replace: false }) } /** diff --git a/src/peer-store/address-book.js b/src/peer-store/address-book.js index 1ec7aaaf98..c3c3ed25fa 100644 --- a/src/peer-store/address-book.js +++ b/src/peer-store/address-book.js @@ -7,6 +7,7 @@ log.error = debug('libp2p:peer-store:address-book:error') const multiaddr = require('multiaddr') const PeerId = require('peer-id') +const PeerInfo = require('peer-info') const Book = require('./book') @@ -52,15 +53,17 @@ class AddressBook extends Book { * @param {PeerId} peerId * @param {Array|Multiaddr} addresses * @param {Object} [options] - * @param {boolean} [options.replace = true] wether addresses received replace stored ones or a unique union is performed. + * @param {boolean} [options.replace = true] whether addresses received replace stored ones or a unique union is performed. * @returns {Array} */ set (peerId, addresses, { replace = true } = {}) { if (!PeerId.isPeerId(peerId)) { + log.error('peerId must be an instance of peer-id to store data') throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS) } if (!addresses) { + log.error('addresses must be provided to store data') throw errcode(new Error('addresses must be provided'), ERR_INVALID_PARAMETERS) } @@ -72,6 +75,7 @@ class AddressBook extends Book { const multiaddrInfos = [] addresses.forEach((addr) => { if (!multiaddr.isMultiaddr(addr)) { + log.error(`multiaddr ${addr} must be an instance of multiaddr`) throw errcode(new Error(`multiaddr ${addr} must be an instance of multiaddr`), ERR_INVALID_PARAMETERS) } @@ -88,7 +92,7 @@ class AddressBook extends Book { } /** - * Replace known addresses to a provided peer. + * Replace known addresses of a provided peer. * If the peer is not known, it is set with the given addresses. * @param {PeerId} peerId * @param {Array} multiaddrInfos @@ -98,22 +102,39 @@ class AddressBook extends Book { const id = peerId.toString() const rec = this.data.get(id) - // Already know the peer + // Not replace multiaddrs + if (!multiaddrInfos.length) { + return rec ? [...rec] : [] + } + + // Already knows the peer if (rec && rec.length === multiaddrInfos.length) { const intersection = rec.filter((mi) => multiaddrInfos.some((newMi) => mi.multiaddr === newMi.multiaddr)) - // New addresses equal the old ones? + // Are new addresses equal to the old ones? // If yes, no changes needed! if (intersection.length === rec.length) { + log(`the addresses provided to store are equal to the already stored for ${id}`) return [...multiaddrInfos] } } this.data.set(id, multiaddrInfos) + log(`stored provided multiaddrs for ${id}`) + + // TODO: Remove peerInfo and its usage on peer-info deprecate + const peerInfo = new PeerInfo(peerId) + multiaddrInfos.forEach((mi) => peerInfo.multiaddrs.add(mi.multiaddr)) + + // Notify the existance of a new peer + if (!rec) { + // this._ps.emit('peer', peerId) + this._ps.emit('peer', peerInfo) + } - this._ps.emit('peer', peerId) this._ps.emit('change:multiaddrs', { peerId, + peerInfo, multiaddrs: multiaddrInfos.map((mi) => mi.multiaddr) }) @@ -129,31 +150,40 @@ class AddressBook extends Book { */ _add (peerId, multiaddrInfos) { const id = peerId.toString() - const rec = this.data.get(id) || [] + const rec = this.data.get(id) - // Add recorded uniquely to the new array - rec.forEach((mi) => { + // Add recorded uniquely to the new array (Union) + rec && rec.forEach((mi) => { if (!multiaddrInfos.find(r => r.multiaddr === mi.multiaddr)) { multiaddrInfos.push(mi) } }) - // If the recorded length is equal to the new after the uniquely union + // If the recorded length is equal to the new after the unique union // The content is the same, no need to update. - if (rec.length === multiaddrInfos.length) { + if (rec && rec.length === multiaddrInfos.length) { + log(`the addresses provided to store are already stored for ${id}`) return [...multiaddrInfos] } this.data.set(id, multiaddrInfos) + + log(`added provided multiaddrs for ${id}`) + + // TODO: Remove peerInfo and its usage on peer-info deprecate + const peerInfo = new PeerInfo(peerId) + multiaddrInfos.forEach((mi) => peerInfo.multiaddrs.add(mi.multiaddr)) + this._ps.emit('change:multiaddrs', { peerId, + peerInfo, multiaddrs: multiaddrInfos.map((mi) => mi.multiaddr) }) // Notify the existance of a new peer - // TODO: do we need this? if (!rec) { - this._ps.emit('peer', peerId) + // this._ps.emit('peer', peerId) + this._ps.emit('peer', peerInfo) } return [...multiaddrInfos] @@ -179,7 +209,9 @@ class AddressBook extends Book { return record.map((multiaddrInfo) => { const addr = multiaddrInfo.multiaddr - if (addr.getPeerId()) return addr + const idString = addr.getPeerId() + if (idString && idString === peerId.toB58String()) return addr + return addr.encapsulate(`/p2p/${peerId.toB58String()}`) }) } diff --git a/src/peer-store/book.js b/src/peer-store/book.js index af4725ed1c..02168d2386 100644 --- a/src/peer-store/book.js +++ b/src/peer-store/book.js @@ -2,6 +2,7 @@ const errcode = require('err-code') const PeerId = require('peer-id') +const PeerInfo = require('peer-info') const { ERR_INVALID_PARAMETERS @@ -63,8 +64,12 @@ class Book { return false } + // TODO: Remove peerInfo and its usage on peer-info deprecate + const peerInfo = new PeerInfo(peerId) + this.eventEmitter.emit(this.eventName, { peerId, + peerInfo, [this.eventProperty]: [] }) diff --git a/src/peer-store/index.js b/src/peer-store/index.js index 6e61b73923..597b69e178 100644 --- a/src/peer-store/index.js +++ b/src/peer-store/index.js @@ -9,251 +9,212 @@ const { EventEmitter } = require('events') const PeerId = require('peer-id') const PeerInfo = require('peer-info') + +const AddressBook = require('./address-book') +const ProtoBook = require('./proto-book') + const { ERR_INVALID_PARAMETERS } = require('../errors') /** - * Responsible for managing known peers, as well as their addresses and metadata - * @fires PeerStore#peer Emitted when a peer is connected to this node - * @fires PeerStore#change:protocols - * @fires PeerStore#change:multiaddrs + * Responsible for managing known peers, as well as their addresses, protocols and metadata. + * @fires PeerStore#peer Emitted when a new peer is added. + * @fires PeerStore#change:protocols Emitted when a known peer supports a different set of protocols. + * @fires PeerStore#change:multiaddrs Emitted when a known peer has a different set of multiaddrs. */ class PeerStore extends EventEmitter { + /** + * PeerInfo object + * @typedef {Object} peerInfo + * @property {Array} multiaddrsInfos peer's information of the multiaddrs. + * @property {Array} protocols peer's supported protocols. + */ + constructor () { super() /** - * Map of peers - * - * @type {Map} + * AddressBook containing a map of peerIdStr to multiaddrsInfo */ - this.peers = new Map() + this.addressBook = new AddressBook(this) /** - * Map known peers to their known multiaddrs. - * @type {Map} + * ProtoBook containing a map of peerIdStr to supported protocols. */ - this.addressBook = new Map() - - /** - * Map known peers to their known supported protocols. - * @type {Map} - */ - this.protoBook = new Map() + this.protoBook = new ProtoBook(this) } + // TODO: Temporary adapter for modules using PeerStore + // This should be removed under a breaking change /** - * Stores the peerInfo of a new peer. - * If already exist, its info is updated. If `silent` is set to - * true, no 'peer' event will be emitted. This can be useful if you - * are already in the process of dialing the peer. The peer is technically - * known, but may not have been added to the PeerStore yet. + * Stores the peerInfo of a new peer on each book. * @param {PeerInfo} peerInfo * @param {object} [options] - * @param {boolean} [options.silent] (Default=false) - * @return {PeerInfo} - */ - put (peerInfo, options = { silent: false }) { - if (!PeerInfo.isPeerInfo(peerInfo)) { - throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS) - } - - let peer - // Already know the peer? - if (this.has(peerInfo.id)) { - peer = this.update(peerInfo) - } else { - peer = this.add(peerInfo) - - // Emit the peer if silent = false - !options.silent && this.emit('peer', peerInfo) - } - return peer - } - - /** - * Add a new peer to the store. - * @param {PeerInfo} peerInfo + * @param {boolean} [options.replace = true] * @return {PeerInfo} */ - add (peerInfo) { - if (!PeerInfo.isPeerInfo(peerInfo)) { - throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS) - } + put (peerInfo, options) { + const multiaddrs = peerInfo.multiaddrs.toArray() + const protocols = Array.from(peerInfo.protocols || new Set()) - // Create new instance and add values to it - const newPeerInfo = new PeerInfo(peerInfo.id) - - peerInfo.multiaddrs.forEach((ma) => newPeerInfo.multiaddrs.add(ma)) - peerInfo.protocols.forEach((p) => newPeerInfo.protocols.add(p)) - - // const connectedMa = peerInfo.isConnected() - // connectedMa && newPeerInfo.connect(connectedMa) - - const peerProxy = new Proxy(newPeerInfo, { - set: (obj, prop, value) => { - if (prop === 'multiaddrs') { - this.emit('change:multiaddrs', { - peerInfo: obj, - multiaddrs: value.toArray() - }) - } else if (prop === 'protocols') { - this.emit('change:protocols', { - peerInfo: obj, - protocols: Array.from(value) - }) - } - return Reflect.set(...arguments) - } - }) - - this.peers.set(peerInfo.id.toB58String(), peerProxy) - return peerProxy - } + this.addressBook.set(peerInfo.id, multiaddrs, options) + this.protoBook.set(peerInfo.id, protocols, options) - /** - * Updates an already known peer. - * @param {PeerInfo} peerInfo - * @return {PeerInfo} - */ - update (peerInfo) { - if (!PeerInfo.isPeerInfo(peerInfo)) { - throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS) - } - - const id = peerInfo.id.toB58String() - const recorded = this.peers.get(id) - - // pass active connection state - // const ma = peerInfo.isConnected() - // if (ma) { - // recorded.connect(ma) - // } - - // Verify new multiaddrs - // TODO: better track added and removed multiaddrs - const multiaddrsIntersection = [ - ...recorded.multiaddrs.toArray() - ].filter((m) => peerInfo.multiaddrs.has(m)) - - if (multiaddrsIntersection.length !== peerInfo.multiaddrs.size || - multiaddrsIntersection.length !== recorded.multiaddrs.size) { - for (const ma of peerInfo.multiaddrs.toArray()) { - recorded.multiaddrs.add(ma) - } - - this.emit('change:multiaddrs', { - peerInfo: recorded, - multiaddrs: recorded.multiaddrs.toArray() - }) - } - - // Update protocols - // TODO: better track added and removed protocols - const protocolsIntersection = new Set( - [...recorded.protocols].filter((p) => peerInfo.protocols.has(p)) - ) - - if (protocolsIntersection.size !== peerInfo.protocols.size || - protocolsIntersection.size !== recorded.protocols.size) { - for (const protocol of peerInfo.protocols) { - recorded.protocols.add(protocol) - } + const peer = this.find(peerInfo.id) + const pInfo = new PeerInfo(peerInfo.id) - this.emit('change:protocols', { - peerInfo: recorded, - protocols: Array.from(recorded.protocols) - }) + if (!peer) { + return pInfo } - // Add the public key if missing - if (!recorded.id.pubKey && peerInfo.id.pubKey) { - recorded.id.pubKey = peerInfo.id.pubKey - } + peer.protocols.forEach((p) => pInfo.protocols.add(p)) + peer.multiaddrInfos.forEach((mi) => pInfo.multiaddrs.add(mi.multiaddr)) - return recorded + return pInfo } + // TODO: Temporary adapter for modules using PeerStore + // This should be removed under a breaking change /** - * Get the info to the given id. - * @param {PeerId|string} peerId b58str id + * Get the info of the given id. + * @param {peerId} peerId * @returns {PeerInfo} */ get (peerId) { - // TODO: deprecate this and just accept `PeerId` instances - if (PeerId.isPeerId(peerId)) { - peerId = peerId.toB58String() - } + const peer = this.find(peerId) + + const pInfo = new PeerInfo(peerId) + peer.protocols.forEach((p) => pInfo.protocols.add(p)) + peer.multiaddrInfos.forEach((mi) => pInfo.multiaddrs.add(mi.multiaddr)) - return this.peers.get(peerId) + return pInfo } + // TODO: Temporary adapter for modules using PeerStore + // This should be removed under a breaking change /** * Has the info to the given id. - * @param {PeerId|string} peerId b58str id + * @param {PeerId} peerId * @returns {boolean} */ has (peerId) { - // TODO: deprecate this and just accept `PeerId` instances - if (PeerId.isPeerId(peerId)) { - peerId = peerId.toB58String() - } - - return this.peers.has(peerId) + return Boolean(this.find(peerId)) } + // TODO: Temporary adapter for modules using PeerStore + // This should be removed under a breaking change /** - * Removes the Peer with the matching `peerId` from the PeerStore - * @param {PeerId|string} peerId b58str id + * Removes the peer provided. + * @param {PeerId} peerId * @returns {boolean} true if found and removed */ remove (peerId) { - // TODO: deprecate this and just accept `PeerId` instances - if (PeerId.isPeerId(peerId)) { - peerId = peerId.toB58String() - } - - return this.peers.delete(peerId) + return this.delete(peerId) } + // TODO: Temporary adapter for modules using PeerStore + // This should be removed under a breaking change /** * Completely replaces the existing peers metadata with the given `peerInfo` * @param {PeerInfo} peerInfo * @returns {void} */ replace (peerInfo) { - if (!PeerInfo.isPeerInfo(peerInfo)) { - throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS) + this.put(peerInfo) + } + + // TODO: Temporary adapter for modules using PeerStore + // This should be removed under a breaking change + /** + * Returns the known multiaddrs for a given `PeerInfo`. All returned multiaddrs + * will include the encapsulated `PeerId` of the peer. + * @param {PeerInfo} peerInfo + * @returns {Array} + */ + multiaddrsForPeer (peerInfo) { + return this.addressBook.getMultiaddrsForPeer(peerInfo.id) + } + + /** + * Get all the stored information of every peer. + * @returns {Map} + */ + get peers () { + const peerInfos = new Map() + + // AddressBook + for (const [idStr, multiaddrInfos] of this.addressBook.data.entries()) { + // TODO: Remove peerInfo and its usage on peer-info deprecate + const peerInfo = new PeerInfo(PeerId.createFromCID(idStr)) + + multiaddrInfos.forEach((mi) => peerInfo.multiaddrs.add((mi.multiaddr))) + + const protocols = this.protoBook.data.get(idStr) || [] + protocols.forEach((p) => peerInfo.protocols.add(p)) + + peerInfos.set(idStr, peerInfo) + // TODO + // peerInfos.set(idStr, { + // id: PeerId.createFromCID(idStr), + // multiaddrInfos, + // protocols: this.protoBook.data.get(idStr) || [] + // }) } - this.remove(peerInfo.id.toB58String()) - this.add(peerInfo) + // ProtoBook + for (const [idStr, protocols] of this.protoBook.data.entries()) { + // TODO: Remove peerInfo and its usage on peer-info deprecate + const peerInfo = peerInfos.get(idStr) + + if (!peerInfo) { + const peerInfo = new PeerInfo(PeerId.createFromCID(idStr)) + + protocols.forEach((p) => peerInfo.protocols.add(p)) + peerInfos.set(idStr, peerInfo) + // peerInfos.set(idStr, { + // id: PeerId.createFromCID(idStr), + // multiaddrInfos: [], + // protocols: protocols + // }) + } + } - // This should be cleaned up in PeerStore v2 - this.emit('change:multiaddrs', { - peerInfo, - multiaddrs: peerInfo.multiaddrs.toArray() - }) + return peerInfos + } - this.emit('change:protocols', { - peerInfo, - protocols: Array.from(peerInfo.protocols) - }) + /** + * Delete the information of the given peer in every book. + * @param {PeerId} peerId + * @returns {boolean} true if found and removed + */ + delete (peerId) { + const addressesDeleted = this.addressBook.delete(peerId) + const protocolsDeleted = this.protoBook.delete(peerId) + return addressesDeleted || protocolsDeleted } /** - * Returns the known multiaddrs for a given `PeerInfo`. All returned multiaddrs - * will include the encapsulated `PeerId` of the peer. - * @param {PeerInfo} peer - * @returns {Array} + * Find the stored information of a given peer. + * @param {PeerId} peerId + * @returns {peerInfo} */ - multiaddrsForPeer (peer) { - return this.put(peer, true).multiaddrs.toArray().map(addr => { - const idString = addr.getPeerId() - if (idString && idString === peer.id.toB58String()) return addr - return addr.encapsulate(`/p2p/${peer.id.toB58String()}`) - }) + find (peerId) { + if (!PeerId.isPeerId(peerId)) { + throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS) + } + + const multiaddrInfos = this.addressBook.get(peerId) + const protocols = this.protoBook.get(peerId) + + if (!multiaddrInfos && !protocols) { + return undefined + } + + return { + multiaddrInfos: multiaddrInfos || [], + protocols: protocols || [] + } } } diff --git a/src/peer-store/proto-book.js b/src/peer-store/proto-book.js index 7c859ce858..be7762e4b0 100644 --- a/src/peer-store/proto-book.js +++ b/src/peer-store/proto-book.js @@ -6,6 +6,7 @@ const log = debug('libp2p:peer-store:proto-book') log.error = debug('libp2p:peer-store:proto-book:error') const PeerId = require('peer-id') +const PeerInfo = require('peer-info') const Book = require('./book') @@ -14,7 +15,7 @@ const { } = require('../errors') /** - * The ProtoBook is responsible for keeping the known suppoerted + * The ProtoBook is responsible for keeping the known supported * protocols of a peer. * @fires ProtoBook#change:protocols */ @@ -46,15 +47,17 @@ class ProtoBook extends Book { * @param {PeerId} peerId * @param {Array|string} protocols * @param {Object} [options] - * @param {boolean} [options.replace = true] wether protocols received replace stored ones or a unique union is performed. + * @param {boolean} [options.replace = true] whether protocols received replace stored ones or a unique union is performed. * @returns {Array} */ set (peerId, protocols, { replace = true } = {}) { if (!PeerId.isPeerId(peerId)) { + log.error('peerId must be an instance of peer-id to store data') throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS) } if (!protocols) { + log.error('protocols must be provided to store data') throw errcode(new Error('protocols must be provided'), ERR_INVALID_PARAMETERS) } @@ -70,7 +73,7 @@ class ProtoBook extends Book { } /** - * Replace known protocols to a provided peer. + * Replace known protocols of a provided peer. * If the peer is not known, it is set with the given protocols. * @param {PeerId} peerId * @param {Array} protocols @@ -83,15 +86,23 @@ class ProtoBook extends Book { const isSetEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value)) - // Already know the peer and the recorded protocols are the same? + // Already knows the peer and the recorded protocols are the same? // If yes, no changes needed! if (recSet && isSetEqual(recSet, newSet)) { + log(`the protocols provided to store are equal to the already stored for ${id}`) return protocols } this.data.set(id, newSet) + log(`stored provided protocols for ${id}`) + + // TODO: Remove peerInfo and its usage on peer-info deprecate + const peerInfo = new PeerInfo(peerId) + protocols.forEach((p) => peerInfo.protocols.add(p)) + this._ps.emit('change:protocols', { peerId, + peerInfo, protocols }) @@ -108,18 +119,26 @@ class ProtoBook extends Book { _add (peerId, protocols) { const id = peerId.toString() const recSet = this.data.get(id) || new Set() - const newSet = new Set([...recSet, ...protocols]) + const newSet = new Set([...recSet, ...protocols]) // Set Union // Any new protocol added? if (recSet.size === newSet.size) { + log(`the protocols provided to store are already stored for ${id}`) return protocols } protocols = [...newSet] this.data.set(id, newSet) + log(`added provided protocols for ${id}`) + + // TODO: Remove peerInfo and its usage on peer-info deprecate + const peerInfo = new PeerInfo(peerId) + protocols.forEach((p) => peerInfo.protocols.add(p)) + this._ps.emit('change:protocols', { peerId, + peerInfo, protocols }) diff --git a/src/registrar.js b/src/registrar.js index 43fa235d03..2aa6bcf85e 100644 --- a/src/registrar.js +++ b/src/registrar.js @@ -10,7 +10,6 @@ const { } = require('./errors') const Topology = require('libp2p-interfaces/src/topology') const { Connection } = require('libp2p-interfaces/src/connection') -const PeerInfo = require('peer-info') /** * Responsible for notifying registered protocols of events in the network. @@ -23,7 +22,6 @@ class Registrar { */ constructor ({ peerStore }) { // Used on topology to listen for protocol changes - // TODO: should we only provide the protobook? this.peerStore = peerStore /** @@ -76,9 +74,11 @@ class Registrar { * @returns {void} */ onConnect (peerInfo, conn) { - if (!PeerInfo.isPeerInfo(peerInfo)) { - throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS) - } + // TODO: This is not a `peer-info` instance anymore, but an object with the data. + // This can be modified to `peer-id` though, once `peer-info` is deprecated. + // if (!PeerInfo.isPeerInfo(peerInfo)) { + // throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS) + // } if (!Connection.isConnection(conn)) { throw errcode(new Error('conn must be an instance of interface-connection'), ERR_INVALID_PARAMETERS) @@ -103,9 +103,11 @@ class Registrar { * @returns {void} */ onDisconnect (peerInfo, connection, error) { - if (!PeerInfo.isPeerInfo(peerInfo)) { - throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS) - } + // TODO: This is not a `peer-info` instance anymore, but an object with the data. + // This can be modified to `peer-id` though, once `peer-info` is deprecated. + // if (!PeerInfo.isPeerInfo(peerInfo)) { + // throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS) + // } const id = peerInfo.id.toB58String() let storedConn = this.connections.get(id) @@ -128,9 +130,11 @@ class Registrar { * @returns {Connection} */ getConnection (peerInfo) { - if (!PeerInfo.isPeerInfo(peerInfo)) { - throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS) - } + // TODO: This is not a `peer-info` instance anymore, but an object with the data. + // This can be modified to `peer-id` though, once `peer-info` is deprecated. + // if (!PeerInfo.isPeerInfo(peerInfo)) { + // throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS) + // } const connections = this.connections.get(peerInfo.id.toB58String()) // Return the first, open connection diff --git a/src/upgrader.js b/src/upgrader.js index 25cb6d3f11..ad2391971f 100644 --- a/src/upgrader.js +++ b/src/upgrader.js @@ -317,7 +317,7 @@ class Upgrader { * Attempts to encrypt the incoming `connection` with the provided `cryptos`. * @private * @async - * @param {PeerId} localPeer The initiators PeerInfo + * @param {PeerId} localPeer The initiators PeerId * @param {*} connection * @param {Map} cryptos * @returns {CryptoResult} An encrypted connection, remote peer `PeerId` and the protocol of the `Crypto` used @@ -346,7 +346,7 @@ class Upgrader { * The first `Crypto` module to succeed will be used * @private * @async - * @param {PeerId} localPeer The initiators PeerInfo + * @param {PeerId} localPeer The initiators PeerId * @param {*} connection * @param {PeerId} remotePeerId * @param {Map} cryptos diff --git a/test/content-routing/dht/operation.node.js b/test/content-routing/dht/operation.node.js index 8520b28548..0f91ebb23b 100644 --- a/test/content-routing/dht/operation.node.js +++ b/test/content-routing/dht/operation.node.js @@ -43,7 +43,8 @@ describe('DHT subsystem operates correctly', () => { remoteLibp2p.start() ]) - remAddr = libp2p.peerStore.multiaddrsForPeer(remotePeerInfo)[0] + libp2p.peerStore.addressBook.set(remotePeerInfo.id, remoteListenAddr) + remAddr = libp2p.peerStore.addressBook.getMultiaddrsForPeer(remotePeerInfo.id)[0] }) afterEach(() => Promise.all([ @@ -67,7 +68,6 @@ describe('DHT subsystem operates correctly', () => { const value = Buffer.from('world') await libp2p.dialProtocol(remAddr, subsystemMulticodecs) - await Promise.all([ pWaitFor(() => libp2p._dht.routingTable.size === 1), pWaitFor(() => remoteLibp2p._dht.routingTable.size === 1) @@ -98,7 +98,8 @@ describe('DHT subsystem operates correctly', () => { await libp2p.start() await remoteLibp2p.start() - remAddr = libp2p.peerStore.multiaddrsForPeer(remotePeerInfo)[0] + libp2p.peerStore.addressBook.set(remotePeerInfo.id, remoteListenAddr) + remAddr = libp2p.peerStore.addressBook.getMultiaddrsForPeer(remotePeerInfo.id)[0] }) afterEach(() => Promise.all([ diff --git a/test/dialing/direct.node.js b/test/dialing/direct.node.js index c846e8ad68..c87d8814f9 100644 --- a/test/dialing/direct.node.js +++ b/test/dialing/direct.node.js @@ -99,7 +99,10 @@ describe('Dialing (direct, TCP)', () => { const dialer = new Dialer({ transportManager: localTM, peerStore: { - multiaddrsForPeer: () => [remoteAddr] + addressBook: { + set: () => {}, + getMultiaddrsForPeer: () => [remoteAddr] + } } }) const peerId = await PeerId.createFromJSON(Peers[0]) @@ -131,7 +134,10 @@ describe('Dialing (direct, TCP)', () => { const dialer = new Dialer({ transportManager: localTM, peerStore: { - multiaddrsForPeer: () => [unsupportedAddr] + addressBook: { + set: () => {}, + getMultiaddrsForPeer: () => [unsupportedAddr] + } } }) const peerId = await PeerId.createFromJSON(Peers[0]) @@ -172,7 +178,10 @@ describe('Dialing (direct, TCP)', () => { transportManager: localTM, concurrency: 2, peerStore: { - multiaddrsForPeer: () => addrs + addressBook: { + set: () => {}, + getMultiaddrsForPeer: () => addrs + } } }) diff --git a/test/dialing/direct.spec.js b/test/dialing/direct.spec.js index f6c716cbd1..6651b3de61 100644 --- a/test/dialing/direct.spec.js +++ b/test/dialing/direct.spec.js @@ -87,7 +87,10 @@ describe('Dialing (direct, WebSockets)', () => { const dialer = new Dialer({ transportManager: localTM, peerStore: { - multiaddrsForPeer: () => [remoteAddr] + addressBook: { + set: () => {}, + getMultiaddrsForPeer: () => [remoteAddr] + } } }) @@ -100,7 +103,10 @@ describe('Dialing (direct, WebSockets)', () => { const dialer = new Dialer({ transportManager: localTM, peerStore: { - multiaddrsForPeer: () => [remoteAddr] + addressBook: { + set: () => {}, + getMultiaddrsForPeer: () => [remoteAddr] + } } }) @@ -121,7 +127,10 @@ describe('Dialing (direct, WebSockets)', () => { const dialer = new Dialer({ transportManager: localTM, peerStore: { - multiaddrsForPeer: () => [remoteAddr] + addressBook: { + set: () => {}, + getMultiaddrsForPeer: () => [remoteAddr] + } } }) const peerId = await PeerId.createFromJSON(Peers[0]) @@ -135,7 +144,10 @@ describe('Dialing (direct, WebSockets)', () => { const dialer = new Dialer({ transportManager: localTM, peerStore: { - multiaddrsForPeer: () => [unsupportedAddr] + addressBook: { + set: () => {}, + getMultiaddrsForPeer: () => [unsupportedAddr] + } } }) const peerId = await PeerId.createFromJSON(Peers[0]) @@ -150,7 +162,10 @@ describe('Dialing (direct, WebSockets)', () => { transportManager: localTM, timeout: 50, peerStore: { - multiaddrsForPeer: () => [remoteAddr] + addressBook: { + set: () => {}, + getMultiaddrsForPeer: () => [remoteAddr] + } } }) sinon.stub(localTM, 'dial').callsFake(async (addr, options) => { @@ -172,7 +187,10 @@ describe('Dialing (direct, WebSockets)', () => { transportManager: localTM, concurrency: 2, peerStore: { - multiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr] + addressBook: { + set: () => {}, + getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr] + } } }) @@ -208,7 +226,10 @@ describe('Dialing (direct, WebSockets)', () => { transportManager: localTM, concurrency: 2, peerStore: { - multiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr] + addressBook: { + set: () => {}, + getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr] + } } }) @@ -316,7 +337,7 @@ describe('Dialing (direct, WebSockets)', () => { }) sinon.spy(libp2p.dialer, 'connectToPeer') - sinon.spy(libp2p.peerStore, 'put') + sinon.spy(libp2p.peerStore.addressBook, 'set') const connection = await libp2p.dial(remoteAddr) expect(connection).to.exist() @@ -325,7 +346,7 @@ describe('Dialing (direct, WebSockets)', () => { expect(protocol).to.equal('/echo/1.0.0') await connection.close() expect(libp2p.dialer.connectToPeer.callCount).to.equal(1) - expect(libp2p.peerStore.put.callCount).to.be.at.least(1) + expect(libp2p.peerStore.addressBook.set.callCount).to.be.at.least(1) }) it('should run identify automatically after connecting', async () => { @@ -339,19 +360,22 @@ describe('Dialing (direct, WebSockets)', () => { }) sinon.spy(libp2p.identifyService, 'identify') - sinon.spy(libp2p.peerStore, 'replace') sinon.spy(libp2p.upgrader, 'onConnection') const connection = await libp2p.dial(remoteAddr) expect(connection).to.exist() + sinon.spy(libp2p.peerStore.addressBook, 'set') + sinon.spy(libp2p.peerStore.protoBook, 'set') + // Wait for onConnection to be called await pWaitFor(() => libp2p.upgrader.onConnection.callCount === 1) expect(libp2p.identifyService.identify.callCount).to.equal(1) await libp2p.identifyService.identify.firstCall.returnValue - expect(libp2p.peerStore.replace.callCount).to.equal(1) + expect(libp2p.peerStore.addressBook.set.callCount).to.equal(1) + expect(libp2p.peerStore.protoBook.set.callCount).to.equal(1) }) it('should be able to use hangup to close connections', async () => { diff --git a/test/dialing/relay.node.js b/test/dialing/relay.node.js index 8dde1c32e5..250e9bf092 100644 --- a/test/dialing/relay.node.js +++ b/test/dialing/relay.node.js @@ -11,6 +11,8 @@ const multiaddr = require('multiaddr') const { collect } = require('streaming-iterables') const pipe = require('it-pipe') const AggregateError = require('aggregate-error') +const PeerId = require('peer-id') + const { createPeerInfo } = require('../utils/creators/peer') const baseOptions = require('../utils/base-options') const Libp2p = require('../../src') @@ -51,8 +53,9 @@ describe('Dialing (via relay, TCP)', () => { return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(async libp2p => { await libp2p.stop() // Clear the peer stores - for (const peerId of libp2p.peerStore.peers.keys()) { - libp2p.peerStore.remove(peerId) + for (const peerIdStr of libp2p.peerStore.peers.keys()) { + const peerId = PeerId.createFromCID(peerIdStr) + libp2p.peerStore.delete(peerId) } })) }) diff --git a/test/identify/index.spec.js b/test/identify/index.spec.js index ca32e023f2..e585233f73 100644 --- a/test/identify/index.spec.js +++ b/test/identify/index.spec.js @@ -48,7 +48,12 @@ describe('Identify', () => { protocols, registrar: { peerStore: { - replace: () => {} + addressBook: { + set: () => { } + }, + protoBook: { + set: () => { } + } } } }) @@ -64,7 +69,8 @@ describe('Identify', () => { const [local, remote] = duplexPair() sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY }) - sinon.spy(localIdentify.registrar.peerStore, 'replace') + sinon.spy(localIdentify.registrar.peerStore.addressBook, 'set') + sinon.spy(localIdentify.registrar.peerStore.protoBook, 'set') // Run identify await Promise.all([ @@ -76,9 +82,10 @@ describe('Identify', () => { }) ]) - expect(localIdentify.registrar.peerStore.replace.callCount).to.equal(1) + expect(localIdentify.registrar.peerStore.addressBook.set.callCount).to.equal(1) + expect(localIdentify.registrar.peerStore.protoBook.set.callCount).to.equal(1) // Validate the remote peer gets updated in the peer store - const call = localIdentify.registrar.peerStore.replace.firstCall + const call = localIdentify.registrar.peerStore.addressBook.set.firstCall expect(call.args[0].id.bytes).to.equal(remotePeer.id.bytes) }) @@ -88,7 +95,12 @@ describe('Identify', () => { protocols, registrar: { peerStore: { - replace: () => {} + addressBook: { + set: () => { } + }, + protoBook: { + set: () => { } + } } } }) @@ -134,7 +146,12 @@ describe('Identify', () => { peerInfo: remotePeer, registrar: { peerStore: { - replace: () => {} + addressBook: { + set: () => {} + }, + protoBook: { + set: () => { } + } } } }) @@ -152,9 +169,8 @@ describe('Identify', () => { const [local, remote] = duplexPair() sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH }) - sinon.spy(IdentifyService, 'updatePeerAddresses') - sinon.spy(IdentifyService, 'updatePeerProtocols') - sinon.spy(remoteIdentify.registrar.peerStore, 'replace') + sinon.spy(remoteIdentify.registrar.peerStore.addressBook, 'set') + sinon.spy(remoteIdentify.registrar.peerStore.protoBook, 'set') // Run identify await Promise.all([ @@ -166,14 +182,14 @@ describe('Identify', () => { }) ]) - expect(IdentifyService.updatePeerAddresses.callCount).to.equal(1) - expect(IdentifyService.updatePeerProtocols.callCount).to.equal(1) - - expect(remoteIdentify.registrar.peerStore.replace.callCount).to.equal(1) - const [peerInfo] = remoteIdentify.registrar.peerStore.replace.firstCall.args - expect(peerInfo.id.bytes).to.eql(localPeer.id.bytes) - expect(peerInfo.multiaddrs.toArray()).to.eql([listeningAddr]) - expect(peerInfo.protocols).to.eql(localProtocols) + expect(remoteIdentify.registrar.peerStore.addressBook.set.callCount).to.equal(1) + expect(remoteIdentify.registrar.peerStore.protoBook.set.callCount).to.equal(1) + const [peerId, multiaddrs] = remoteIdentify.registrar.peerStore.addressBook.set.firstCall.args + expect(peerId.bytes).to.eql(localPeer.id.bytes) + expect(multiaddrs).to.eql([listeningAddr]) + const [peerId2, protocols] = remoteIdentify.registrar.peerStore.protoBook.set.firstCall.args + expect(peerId2.bytes).to.eql(localPeer.id.bytes) + expect(protocols).to.eql(Array.from(localProtocols)) }) }) @@ -204,13 +220,14 @@ describe('Identify', () => { }) sinon.spy(libp2p.identifyService, 'identify') - const peerStoreSpy = sinon.spy(libp2p.peerStore, 'replace') + const peerStoreSpy = sinon.spy(libp2p.peerStore.addressBook, 'set') const connection = await libp2p.dialer.connectToPeer(remoteAddr) expect(connection).to.exist() // Wait for peer store to be updated - await pWaitFor(() => peerStoreSpy.callCount === 1) + // Dialer._createDialTarget (add), Identify (replace) + await pWaitFor(() => peerStoreSpy.callCount === 2) expect(libp2p.identifyService.identify.callCount).to.equal(1) // The connection should have no open streams @@ -226,7 +243,6 @@ describe('Identify', () => { sinon.spy(libp2p.identifyService, 'identify') sinon.spy(libp2p.identifyService, 'push') - sinon.spy(libp2p.peerStore, 'update') const connection = await libp2p.dialer.connectToPeer(remoteAddr) expect(connection).to.exist() diff --git a/test/peer-discovery/index.spec.js b/test/peer-discovery/index.spec.js index 31fc38cdb1..f230ee190e 100644 --- a/test/peer-discovery/index.spec.js +++ b/test/peer-discovery/index.spec.js @@ -36,7 +36,9 @@ describe('peer discovery', () => { ...baseOptions, peerInfo }) - libp2p.peerStore.add(remotePeerInfo) + libp2p.peerStore.addressBook.set(remotePeerInfo.id, remotePeerInfo.multiaddrs.toArray()) + libp2p.peerStore.protoBook.set(remotePeerInfo.id, Array.from(remotePeerInfo.protocols)) + const deferred = defer() sinon.stub(libp2p.dialer, 'connectToPeer').callsFake((remotePeerInfo) => { expect(remotePeerInfo).to.equal(remotePeerInfo) @@ -47,7 +49,9 @@ describe('peer discovery', () => { libp2p.start() await deferred.promise - expect(spy.getCall(0).args).to.eql([remotePeerInfo]) + + expect(spy.calledOnce).to.eql(true) + expect(spy.getCall(0).args[0].id.toString()).to.eql(remotePeerInfo.id.toString()) }) it('should ignore self on discovery', async () => { diff --git a/test/peer-store/address-book.spec.js b/test/peer-store/address-book.spec.js index 8b96638e79..140699eb05 100644 --- a/test/peer-store/address-book.spec.js +++ b/test/peer-store/address-book.spec.js @@ -8,12 +8,11 @@ const sinon = require('sinon') const { EventEmitter } = require('events') const pDefer = require('p-defer') - const multiaddr = require('multiaddr') -const PeerId = require('peer-id') const AddressBook = require('../../src/peer-store/address-book') +const peerUtils = require('../utils/creators/peer') const { ERR_INVALID_PARAMETERS } = require('../../src/errors') @@ -25,14 +24,15 @@ const addr3 = multiaddr('/ip4/127.0.0.1/tcp/8002') const arraysAreEqual = (a, b) => a.length === b.length && a.sort().every((item, index) => b[index] === item) describe('addressBook', () => { + let peerId + + before(async () => { + [peerId] = await peerUtils.createPeerId() + }) + describe('addressBook.set', () => { - let peerId let ee, ab - before(async () => { - peerId = await PeerId.create() - }) - beforeEach(() => { ee = new EventEmitter() ab = new AddressBook(ee) @@ -225,13 +225,8 @@ describe('addressBook', () => { }) describe('addressBook.get', () => { - let peerId let ee, ab - before(async () => { - peerId = await PeerId.create() - }) - beforeEach(() => { ee = new EventEmitter() ab = new AddressBook(ee) @@ -261,13 +256,8 @@ describe('addressBook', () => { }) describe('addressBook.getMultiaddrsForPeer', () => { - let peerId let ee, ab - before(async () => { - peerId = await PeerId.create() - }) - beforeEach(() => { ee = new EventEmitter() ab = new AddressBook(ee) @@ -298,13 +288,8 @@ describe('addressBook', () => { }) describe('addressBook.delete', () => { - let peerId let ee, ab - before(async () => { - peerId = await PeerId.create() - }) - beforeEach(() => { ee = new EventEmitter() ab = new AddressBook(ee) diff --git a/test/peer-store/peer-store.spec.js b/test/peer-store/peer-store.spec.js index 91628432cc..c4be9598d5 100644 --- a/test/peer-store/peer-store.spec.js +++ b/test/peer-store/peer-store.spec.js @@ -4,185 +4,114 @@ const chai = require('chai') chai.use(require('dirty-chai')) const { expect } = chai -const sinon = require('sinon') - -const pDefer = require('p-defer') const PeerStore = require('../../src/peer-store') const multiaddr = require('multiaddr') -const peerUtils = require('../utils/creators/peer') - -const addr = multiaddr('/ip4/127.0.0.1/tcp/8000') - -describe('peer-store', () => { - let peerStore - beforeEach(() => { - peerStore = new PeerStore() - }) - - it('should add a new peer and emit it when it does not exist', async () => { - const defer = pDefer() +const peerUtils = require('../utils/creators/peer') - sinon.spy(peerStore, 'put') - sinon.spy(peerStore, 'add') - sinon.spy(peerStore, 'update') +const addr1 = multiaddr('/ip4/127.0.0.1/tcp/8000') +const addr2 = multiaddr('/ip4/127.0.0.1/tcp/8001') +const addr3 = multiaddr('/ip4/127.0.0.1/tcp/8002') +const addr4 = multiaddr('/ip4/127.0.0.1/tcp/8003') - const [peerInfo] = await peerUtils.createPeerInfo() +const proto1 = '/protocol1' +const proto2 = '/protocol2' +const proto3 = '/protocol3' - peerStore.on('peer', (peer) => { - expect(peer).to.exist() - defer.resolve() +describe('peer-store', () => { + let peerIds + before(async () => { + peerIds = await peerUtils.createPeerId({ + number: 3 }) - peerStore.put(peerInfo) - - // Wait for peerStore to emit the peer - await defer.promise - - expect(peerStore.put.callCount).to.equal(1) - expect(peerStore.add.callCount).to.equal(1) - expect(peerStore.update.callCount).to.equal(0) }) - it('should update peer when it is already in the store', async () => { - const [peerInfo] = await peerUtils.createPeerInfo() - - // Put the peer in the store - peerStore.put(peerInfo) - - sinon.spy(peerStore, 'add') - sinon.spy(peerStore, 'update') + describe('empty books', () => { + let peerStore - // When updating, peer event must not be emitted - peerStore.on('peer', () => { - throw new Error('should not emit twice') - }) - // If no multiaddrs change, the event should not be emitted - peerStore.on('change:multiaddrs', () => { - throw new Error('should not emit change:multiaddrs') - }) - // If no protocols change, the event should not be emitted - peerStore.on('change:protocols', () => { - throw new Error('should not emit change:protocols') + beforeEach(() => { + peerStore = new PeerStore() }) - peerStore.put(peerInfo) - - expect(peerStore.add.callCount).to.equal(0) - expect(peerStore.update.callCount).to.equal(1) - }) - - it('should emit the "change:multiaddrs" event when a peer has new multiaddrs', async () => { - const defer = pDefer() - const [createdPeerInfo] = await peerUtils.createPeerInfo() - - // Put the peer in the store - peerStore.put(createdPeerInfo) + it('has an empty map of peers', () => { + const peers = peerStore.peers + expect(peers.size).to.equal(0) + }) - // When updating, "change:multiaddrs" event must not be emitted - peerStore.on('change:multiaddrs', ({ peerInfo, multiaddrs }) => { - expect(peerInfo).to.exist() - expect(peerInfo.id).to.eql(createdPeerInfo.id) - expect(peerInfo.protocols).to.eql(createdPeerInfo.protocols) - expect(multiaddrs).to.exist() - expect(multiaddrs).to.eql(createdPeerInfo.multiaddrs.toArray()) - defer.resolve() + it('returns false on trying to delete a non existant peerId', () => { + const deleted = peerStore.delete(peerIds[0]) + expect(deleted).to.equal(false) }) - // If no protocols change, the event should not be emitted - peerStore.on('change:protocols', () => { - throw new Error('should not emit change:protocols') + + it('returns undefined on trying to find a non existant peerId', () => { + const peerInfo = peerStore.find(peerIds[0]) + expect(peerInfo).to.not.exist() }) + }) - createdPeerInfo.multiaddrs.add(addr) - peerStore.put(createdPeerInfo) + describe('previously populated books', () => { + let peerStore - // Wait for peerStore to emit the event - await defer.promise - }) + beforeEach(() => { + peerStore = new PeerStore() - it('should emit the "change:protocols" event when a peer has new protocols', async () => { - const defer = pDefer() - const [createdPeerInfo] = await peerUtils.createPeerInfo() + // Add peer0 with { addr1, addr2 } and { proto1 } + peerStore.addressBook.set(peerIds[0], [addr1, addr2]) + peerStore.protoBook.set(peerIds[0], proto1) - // Put the peer in the store - peerStore.put(createdPeerInfo) + // Add peer1 with { addr3 } and { proto2, proto3 } + peerStore.addressBook.set(peerIds[1], [addr3]) + peerStore.protoBook.set(peerIds[1], [proto2, proto3]) - // If no multiaddrs change, the event should not be emitted - peerStore.on('change:multiaddrs', () => { - throw new Error('should not emit change:multiaddrs') - }) - // When updating, "change:protocols" event must be emitted - peerStore.on('change:protocols', ({ peerInfo, protocols }) => { - expect(peerInfo).to.exist() - expect(peerInfo.id).to.eql(createdPeerInfo.id) - expect(peerInfo.multiaddrs).to.eql(createdPeerInfo.multiaddrs) - expect(protocols).to.exist() - expect(protocols).to.eql(Array.from(createdPeerInfo.protocols)) - defer.resolve() + // Add peer2 { addr4 } + peerStore.addressBook.set(peerIds[2], [addr4]) }) - createdPeerInfo.protocols.add('/new-protocol/1.0.0') - peerStore.put(createdPeerInfo) + it('has peers', () => { + const peers = peerStore.peers - // Wait for peerStore to emit the event - await defer.promise - }) + expect(peers.size).to.equal(3) + expect(Array.from(peers.keys())).to.have.members([ + peerIds[0].toString(), + peerIds[1].toString(), + peerIds[2].toString() + ]) + }) - it('should be able to retrieve a peer from store through its b58str id', async () => { - const [peerInfo] = await peerUtils.createPeerInfo() - const id = peerInfo.id + it('returns true on deleting a stored peer', () => { + const deleted = peerStore.delete(peerIds[0]) + expect(deleted).to.equal(true) - let retrievedPeer = peerStore.get(id) - expect(retrievedPeer).to.not.exist() + const peers = peerStore.peers + expect(peers.size).to.equal(2) + expect(Array.from(peers.keys())).to.not.have.members([peerIds[0].toString()]) + }) - // Put the peer in the store - peerStore.put(peerInfo) + it('returns true on deleting a stored peer which is only on one book', () => { + const deleted = peerStore.delete(peerIds[2]) + expect(deleted).to.equal(true) - retrievedPeer = peerStore.get(id) - expect(retrievedPeer).to.exist() - expect(retrievedPeer.id).to.equal(peerInfo.id) - expect(retrievedPeer.multiaddrs).to.eql(peerInfo.multiaddrs) - expect(retrievedPeer.protocols).to.eql(peerInfo.protocols) - }) - - it('should be able to remove a peer from store through its b58str id', async () => { - const [peerInfo] = await peerUtils.createPeerInfo() - const id = peerInfo.id + const peers = peerStore.peers + expect(peers.size).to.equal(2) + }) - let removed = peerStore.remove(id) - expect(removed).to.eql(false) + it('finds the stored information of a peer in all its books', () => { + const peerInfo = peerStore.find(peerIds[0]) + expect(peerInfo).to.exist() + expect(peerInfo.protocols).to.have.members([proto1]) - // Put the peer in the store - peerStore.put(peerInfo) - expect(peerStore.peers.size).to.equal(1) + const peerMultiaddrs = peerInfo.multiaddrInfos.map((mi) => mi.multiaddr) + expect(peerMultiaddrs).to.have.members([addr1, addr2]) + }) - removed = peerStore.remove(id) - expect(removed).to.eql(true) - expect(peerStore.peers.size).to.equal(0) - }) + it('finds the stored information of a peer that is not present in all its books', () => { + const peerInfo = peerStore.find(peerIds[2]) + expect(peerInfo).to.exist() + expect(peerInfo.protocols.length).to.eql(0) - it('should be able to get the multiaddrs for a peer', async () => { - const [peerInfo, relayInfo] = await peerUtils.createPeerInfo({ number: 2 }) - const id = peerInfo.id - const ma1 = multiaddr('/ip4/127.0.0.1/tcp/4001') - const ma2 = multiaddr('/ip4/127.0.0.1/tcp/4002/ws') - const ma3 = multiaddr(`/ip4/127.0.0.1/tcp/4003/ws/p2p/${relayInfo.id.toB58String()}/p2p-circuit`) - - peerInfo.multiaddrs.add(ma1) - peerInfo.multiaddrs.add(ma2) - peerInfo.multiaddrs.add(ma3) - - const multiaddrs = peerStore.multiaddrsForPeer(peerInfo) - const expectedAddrs = [ - ma1.encapsulate(`/p2p/${id.toB58String()}`), - ma2.encapsulate(`/p2p/${id.toB58String()}`), - ma3.encapsulate(`/p2p/${id.toB58String()}`) - ] - - expect(multiaddrs).to.eql(expectedAddrs) + const peerMultiaddrs = peerInfo.multiaddrInfos.map((mi) => mi.multiaddr) + expect(peerMultiaddrs).to.have.members([addr4]) + }) }) }) - -describe('peer-store on discovery', () => { - // TODO: implement with discovery -}) diff --git a/test/peer-store/proto-book.spec.js b/test/peer-store/proto-book.spec.js index cb8d6bbc69..5c9fe5b983 100644 --- a/test/peer-store/proto-book.spec.js +++ b/test/peer-store/proto-book.spec.js @@ -9,10 +9,9 @@ const sinon = require('sinon') const { EventEmitter } = require('events') const pDefer = require('p-defer') -const PeerId = require('peer-id') - const ProtoBook = require('../../src/peer-store/proto-book') +const peerUtils = require('../utils/creators/peer') const { ERR_INVALID_PARAMETERS } = require('../../src/errors') @@ -20,14 +19,15 @@ const { const arraysAreEqual = (a, b) => a.length === b.length && a.sort().every((item, index) => b[index] === item) describe('protoBook', () => { + let peerId + + before(async () => { + [peerId] = await peerUtils.createPeerId() + }) + describe('protoBook.set', () => { - let peerId let ee, pb - before(async () => { - peerId = await PeerId.create() - }) - beforeEach(() => { ee = new EventEmitter() pb = new ProtoBook(ee) @@ -209,13 +209,8 @@ describe('protoBook', () => { }) describe('protoBook.get', () => { - let peerId let ee, pb - before(async () => { - peerId = await PeerId.create() - }) - beforeEach(() => { ee = new EventEmitter() pb = new ProtoBook(ee) @@ -244,13 +239,8 @@ describe('protoBook', () => { }) describe('protoBook.supports', () => { - let peerId let ee, pb - before(async () => { - peerId = await PeerId.create() - }) - beforeEach(() => { ee = new EventEmitter() pb = new ProtoBook(ee) @@ -296,13 +286,8 @@ describe('protoBook', () => { }) describe('protoBook.delete', () => { - let peerId let ee, pb - before(async () => { - peerId = await PeerId.create() - }) - beforeEach(() => { ee = new EventEmitter() pb = new ProtoBook(ee) diff --git a/test/registrar/registrar.spec.js b/test/registrar/registrar.spec.js index 056c4b49ab..a8c17c935e 100644 --- a/test/registrar/registrar.spec.js +++ b/test/registrar/registrar.spec.js @@ -89,7 +89,9 @@ describe('registrar', () => { remotePeerInfo.protocols.add(multicodec) // Add connected peer to peerStore and registrar - peerStore.put(remotePeerInfo) + peerStore.addressBook.set(remotePeerInfo.id, remotePeerInfo.multiaddrs.toArray()) + peerStore.protoBook.set(remotePeerInfo.id, Array.from(remotePeerInfo.protocols)) + registrar.onConnect(remotePeerInfo, conn) expect(registrar.connections.size).to.eql(1) @@ -156,18 +158,23 @@ describe('registrar', () => { const peerInfo = await PeerInfo.create(conn.remotePeer) // Add connected peer to peerStore and registrar - peerStore.put(peerInfo) + peerStore.addressBook.set(peerInfo.id, peerInfo.multiaddrs.toArray()) + peerStore.protoBook.set(peerInfo.id, Array.from(peerInfo.protocols)) + registrar.onConnect(peerInfo, conn) // Add protocol to peer and update it peerInfo.protocols.add(multicodec) - peerStore.put(peerInfo) + peerStore.addressBook.set(peerInfo.id, peerInfo.multiaddrs.toArray(), { replace: false }) + peerStore.protoBook.set(peerInfo.id, Array.from(peerInfo.protocols), { replace: false }) await onConnectDefer.promise // Remove protocol to peer and update it peerInfo.protocols.delete(multicodec) - peerStore.replace(peerInfo) + + peerStore.addressBook.set(peerInfo.id, peerInfo.multiaddrs.toArray()) + peerStore.protoBook.set(peerInfo.id, Array.from(peerInfo.protocols)) await onDisconnectDefer.promise }) @@ -197,7 +204,8 @@ describe('registrar', () => { const id = peerInfo.id.toB58String() // Add connection to registrar - peerStore.put(peerInfo) + peerStore.addressBook.set(peerInfo.id, peerInfo.multiaddrs.toArray()) + peerStore.protoBook.set(peerInfo.id, Array.from(peerInfo.protocols)) registrar.onConnect(peerInfo, conn1) registrar.onConnect(peerInfo, conn2)