From d3e4ad2d697a843a8ddc87c02c297b00d6b13d30 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 --- doc/API.md | 350 +++++++++++++++++++++ src/dialer/index.js | 36 ++- src/get-peer-info.js | 2 +- src/identify/index.js | 58 +--- src/index.js | 13 +- src/peer-store/README.md | 63 ++-- src/peer-store/address-book.js | 58 +++- src/peer-store/book.js | 5 + src/peer-store/index.js | 331 +++++++++---------- 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 | 31 +- test/peer-store/peer-store.spec.js | 231 +++++--------- test/peer-store/proto-book.spec.js | 31 +- test/registrar/registrar.spec.js | 18 +- 22 files changed, 874 insertions(+), 551 deletions(-) diff --git a/doc/API.md b/doc/API.md index 3f12f22d..60513623 100644 --- a/doc/API.md +++ b/doc/API.md @@ -17,6 +17,17 @@ * [`contentRouting.put`](#contentroutingput) * [`contentRouting.get`](#contentroutingget) * [`contentRouting.getMany`](#contentroutinggetmany) + * [`peerStore.addressBook.delete`](#peerstoreaddressbookdelete) + * [`peerStore.addressBook.get`](#peerstoreaddressbookget) + * [`peerStore.addressBook.getMultiaddrsForPeer`](#peerstoreaddressbookgetmultiaddrsforpeer) + * [`peerStore.addressBook.set`](#peerstoreaddressbookset) + * [`peerStore.protoBook.delete`](#peerstoreprotobookdelete) + * [`peerStore.protoBook.get`](#peerstoreprotobookget) + * [`peerStore.protoBook.set`](#peerstoreprotobookset) + * [`peerStore.protoBook.supports`](#peerstoreprotobooksupports) + * [`peerStore.delete`](#peerstoredelete) + * [`peerStore.find`](#peerstorefind) + * [`peerStore.peers`](#peerstorepeers) * [`pubsub.getSubscribers`](#pubsubgetsubscribers) * [`pubsub.getTopics`](#pubsubgettopics) * [`pubsub.publish`](#pubsubpublish) @@ -507,6 +518,345 @@ const key = '/key' const { from, val } = await libp2p.contentRouting.get(key) ``` +### peerStore.addressBook.delete + +Delete the provided peer from the book. + +`peerStore.addressBook.delete(peerId)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | `peerid` | peerId to remove | + +#### Returns + +| Type | Description | +|------|-------------| +| `boolean` | true if found and removed | + +#### Example + +```js +peerStore.addressBook.delete(peerId) +// false +peerStore.addressBook.set(peerId, discoveredMultiaddr) +peerStore.addressBook.delete(peerId) +// true +``` + +### peerStore.addressBook.get + +Get the known `multiaddrInfos` of a provided peer. + +`peerStore.addressBook.get(peerId)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | `peerid` | peerId to get | + +#### Returns + +| Type | Description | +|------|-------------| +| `Array` | Array of peer's multiaddr with their relevant information | + +#### Example + +```js +peerStore.addressBook.get(peerId) +// undefined +peerStore.addressBook.set(peerId, discoveredMultiaddr) +peerStore.addressBook.get(peerId) +// [ +// { +// multiaddr: /ip4/140.10.2.1/tcp/8000, +// ... +// }, +// { +// multiaddr: /ip4/140.10.2.1/ws/8001 +// ... +// }, +// ] +``` + +## peerStore.addressBook.getMultiaddrsForPeer + +Get the known `multiaddr` of a provided peer. All returned multiaddrs will include the encapsulated `PeerId` of the peer. + +`peerStore.addressBook.getMultiaddrsForPeer(peerId)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | `peerid` | peerId to get | + +#### Returns + +| Type | Description | +|------|-------------| +| `Array` | Array of peer's multiaddr | + +#### Example + +```js +peerStore.addressBook.getMultiaddrsForPeer(peerId) +// undefined +peerStore.addressBook.set(peerId, discoveredMultiaddr) +peerStore.addressBook.getMultiaddrsForPeer(peerId) +// [ +// /ip4/140.10.2.1/tcp/8000/p2p/QmW8rAgaaA6sRydK1k6vonShQME47aDxaFidbtMevWs73t +// /ip4/140.10.2.1/ws/8001/p2p/QmW8rAgaaA6sRydK1k6vonShQME47aDxaFidbtMevWs73t +// ] +``` + +### peerStore.addressBook.set + +Set known `multiaddrs` of a given peer. + +`peerStore.addressBook.set(peerId, multiaddrs, options)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | `peerid` | peerId to set | +| multiaddrs | `multiaddr|Array` | multiaddrs to store | +| [options] | `object` | options to set | +| [options.replace] | `Object` | replace stored data (if exists) or unique union (default: true) | + +#### Returns + +| Type | Description | +|------|-------------| +| `Array` | Array of peer's multiaddr with their relevant information | + +#### Example + +```js +peerStore.addressBook.set(peerId, discoveredMultiaddr) +// [ +// { +// multiaddr: /ip4/140.10.2.1/tcp/8000, +// ... +// }, +// { +// multiaddr: /ip4/140.10.2.1/ws/8001 +// ... +// }, +// ] +``` + +### peerStore.protoBook.delete + +Delete the provided peer from the book. + +`peerStore.protoBook.delete(peerId)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | `peerid` | peerId to remove | + +#### Returns + +| Type | Description | +|------|-------------| +| `boolean` | true if found and removed | + +#### Example + +```js +peerStore.protoBook.delete(peerId) +// false +peerStore.protoBook.set(peerId, supportedProtocols) +peerStore.protoBook.delete(peerId) +// true +``` + +### peerStore.protoBook.get + +Get the known `protocols` of a provided peer. + +`peerStore.protoBook.get(peerId)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | `peerid` | peerId to get | + +#### Returns + +| Type | Description | +|------|-------------| +| `Array` | Array of peer's supported protocols | + +#### Example + +```js +peerStore.protoBook.get(peerId) +// undefined +peerStore.protoBook.set(peerId, supportedProtocols) +peerStore.protoBook.get(peerId) +// [ '/proto/1.0.0', '/proto/1.1.0' ] +``` + +### peerStore.protoBook.set + +Set known `protocols` of a given peer. + +`peerStore.protoBook.set(peerId, protocols, options)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | `peerid` | peerId to set | +| protocols | `string|Array` | protocols to store | +| [options] | `object` | options to set | +| [options.replace] | `Object` | replace stored data (if exists) or unique union (default: true) | + +#### Returns + +| Type | Description | +|------|-------------| +| `Array` | Array of peer's supported protocols | + +#### Example + +```js +peerStore.protoBook.set(peerId, supportedProtocols) +// [ '/proto/1.0.0', '/proto/1.1.0' ] +``` + +### peerStore.protoBook.supports + +Verify if the provided peer supports the given `protocols`. + +`peerStore.protoBook.supports(peerId, protocols)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | `peerid` | peerId to get | +| protocols | `string|Array` | protocols to verify | + +#### Returns + +| Type | Description | +|------|-------------| +| `boolean` | true if found and removed | + +#### Example + +```js +const supportedProtocols = [ '/proto/1.0.0', '/proto/1.1.0' ] +peerStore.protoBook.supports(peerId, supportedProtocols) +// false +peerStore.protoBook.supports(peerId, supportedProtocols[0]) +// false +peerStore.protoBook.set(peerId, supportedProtocols) +peerStore.protoBook.supports(peerId, supportedProtocols) +// true +peerStore.protoBook.supports(peerId, supportedProtocols[0]) +// true +``` + +### peerStore.delete + +Delete the provided peer from every book. + +`peerStore.delete(peerId)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | `peerid` | peerId to remove | + +#### Returns + +| Type | Description | +|------|-------------| +| `boolean` | true if found and removed | + +#### Example + +```js +peerStore.delete(peerId) +// false +peerStore.addressBook.set(peerId, discoveredMultiaddrs) +peerStore.protoBook.set(peerId, supportedProtocols) +peerStore.delete(peerId) +// true +peerStore.delete(peerId2) +// false +peerStore.addressBook.set(peerId2, discoveredMultiaddrs) +peerStore.delete(peerId2) +// true +``` + +### peerStore.find + +Find the stored information of a given peer. + +`peerStore.find(peerId)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | `peerid` | peerId to find | + +#### Returns + +| Type | Description | +|------|-------------| +| `peerInfo` | Peer information of the provided peer | + +#### Example + +```js +peerStore.find(peerId) +// false +peerStore.addressBook.set(peerId, discoveredMultiaddrs) +peerStore.protoBook.set(peerId, supportedProtocols) +peerStore.find(peerId) +// { +// multiaddrInfos: [...], +// protocols: [...] +// } +``` + +### peerStore.peers + +Get all the stored information of every peer. + +`peerStore.peers()` + +#### Returns + +| Type | Description | +|------|-------------| +| `Map` | Peer information of every peer | + +TODO: change when `peer-info` is deprecated (breaking change) + +#### Example + +```js +for (peer of peerStore.peers().values()) { + // peerInfo instance +} +``` + ### pubsub.getSubscribers Gets a list of the peer-ids that are subscribed to one topic. diff --git a/src/dialer/index.js b/src/dialer/index.js index 7638ffe5..9d257cb5 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 01a6bc49..83135eeb 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 d8643611..75a68594 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) } @@ -241,6 +206,9 @@ class IdentifyService { protocols: Array.from(this._protocols.keys()) }) + // TODO: should we add to peerStore.addressBook.set() here? + // We can have an inbound connection from an unkwown peer + pipe( [message], lp.encode(), @@ -273,20 +241,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 a7ee76b6..db597cae 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/README.md b/src/peer-store/README.md index 3a3e9d4e..835c8c7e 100644 --- a/src/peer-store/README.md +++ b/src/peer-store/README.md @@ -1,8 +1,8 @@ # Peerstore -Libp2p's Peerstore is responsible for keeping an updated register with the relevant information of the known peers. It should gather environment changes and be able to take decisions and notice interested parties of relevant changes. The Peerstore comprises four main components: `addressBook`, `keyBook`, `protocolBook` and `metadataBook`. These book components have similar characteristics with the `Javascript Map` implementation. +Libp2p's Peerstore is responsible for keeping an updated register with the relevant information of the known peers. It should gather environment changes, be able to take decisions and notice interested parties of relevant changes. The Peerstore comprises four main components: `addressBook`, `keyBook`, `protocolBook` and `metadataBook`. These book components have similar characteristics with the `Javascript Map` implementation. -The PeerStore needs to manage the high level operations on its inner books. Moreover, the peerStore should be responsible for notifying interested parties of relevant events, through its Event Emitter. +The PeerStore manages the high level operations on its inner books. Moreover, the peerStore should be responsible for notifying interested parties of relevant events, through its Event Emitter. (Future considerations: Peerstore should manage a job runner to trigger books runners for data trimming or computations) @@ -14,13 +14,12 @@ In a libp2p node's life, it will discover peers through its discovery protocols. Taking into account a different scenario, a peer might perform/receive a dial request to/from a unkwown peer. In such a scenario, the `PeerStore` must store the peer's multiaddr once a connection is established. -(NOTE: this should be removed later) -(currently we silently put it to the peerStore, without emitting events, as this logic exists in the `onConnected` callback from the upgrader. This way, we are never emitting the `peer` event when inbound connections happen, or a unkwown peer is dialed. Should we differentiate this?) - After a connection is established with a peer, the Identify protocol will run automatically. A stream is created and peers exchange their information (Multiaddrs, running protocols and their public key). Once this information is obtained, it should be added to the PeerStore. In this specific case, as we are speaking to the source of truth, we should ensure the PeerStore is prioritizing these records. If the recorded `multiaddrs` or `protocols` have changed, interested parties must be informed via the `change:multiaddrs` or `change:protocols` events respectively. In the background, the Identify Service is also waiting for protocol change notifications of peers via the IdentifyPush protocol. Peers may leverage the `identify-push` message to communicate protocol changes to all connected peers, so that their PeerStore can be updated with the updated protocols. As the `identify-push` also sends complete and updated information, the data in the PeerStore can be replaced. +(To consider: Should we not replace until we get to multiaddr confidence? we might loose true information as we will talk with older nodes on the network.) + While it is currently not supported in js-libp2p, future iterations may also support the [IdentifyDelta protocol](https://github.com/libp2p/specs/pull/176). It is also possible to gather relevant information for peers from other protocols / subsystems. For instance, in `DHT` operations, nodes can exchange peer data as part of the `DHT` operation. In this case, we can learn additional information about a peer we already know. In this scenario the `PeerStore` should not replace the existing data it has, just add it. @@ -29,15 +28,17 @@ It is also possible to gather relevant information for peers from other protocol When the `PeerStore` data is updated, this information might be important for different parties. -Every time a peer needs to dial another peer, it is essential that it knows the multiaddrs used by the peer, in order to perform a successful dial to a peer. The same is true for pinging a peer. While the `AddressBook` is going to keep its data updated, it will also emit `change:multiaddrs` events so that subsystems/users interested in knowing these changes can be notifyied instead of pooling the `AddressBook`. +Every time a peer needs to dial another peer, it is essential that it knows the multiaddrs used by the peer, in order to perform a successful dial to it. The same is true for pinging a peer. While the `AddressBook` is going to keep its data updated, it will also emit `change:multiaddrs` events so that subsystems/users interested in knowing these changes can be notifyied instead of pooling the `AddressBook`. Everytime a peer starts/stops supporting a protocol, libp2p subsystems or users might need to act accordingly. `js-libp2p` registrar orchestrates known peers, established connections and protocol topologies. This way, once a protocol is supported for a peer, the topology of that protocol should be informed that a new peer may be used and the subsystem can decide if it should open a new stream with that peer or not. For these situations, the `ProtoBook` will emit `change:protocols` events whenever supported protocols of a peer change. ## PeerStore implementation -(Note: except for highlighting the APIs functionallity, they should be better formally described on `API.md` file) +The Peerstore wraps four main components: `addressBook`, `keyBook`, `protocolBook` and `metadataBook`. Moreover, it provides a high level API for those components, as well as data events. -#### API: +### API + +For the complete API documentation, you should check the [API.md](../../doc/API.md). Access to its underlying books: @@ -46,19 +47,27 @@ Access to its underlying books: High level operations: -- `peerStore.delete(peerId)` +- [`peerStore.delete(peerId)`](../../doc/API.md#peerstoredelete) Deletes the provided peer from every book. -- `peerStore.find(peerId)` +- [`peerStore.find(peerId)`](../../doc/API.md#peerstorefind) -TODO (Move to API.doc and reference) +Finds the stored information of a given peer. -- `peerStore.peers()` +- [`peerStore.peers()`](../../doc/API.md#peerstorepeers) -Get an array of all the peers, as well as their information. +Gets an array of all the peers, as well as their information. -## Address Book +### Events + +- `peer` - emitted when a new peer is added. +- `change:multiaadrs` - emitted when a known peer has a different set of multiaddrs. +- `change:protocols` - emitted when a known peer supports a different set of protocols. + +### Components API + +#### Address Book The `addressBook` keeps the known multiaddrs of a peer. The multiaddrs of each peer may change over time and the Address Book must account for this. @@ -77,24 +86,22 @@ A `peerId.toString()` identifier mapping to a `multiaddrInfo` object, which shou **Note:** except for multiaddr naming, the other properties are placeholders for now and might not be as described in the future milestones. - `addressBook.data` -- `addressBook.set()` -- `addressBook.get()` -- `addressBook.getMultiaddrsForPeer()` -- `addressBook.delete()` +- [`addressBook.delete()`](../../doc/API.md#peerstoreaddressbookdelete) +- [`addressBook.get()`](../../doc/API.md#peerstoreaddressbookget) +- [`addressBook.getMultiaddrsForPeer()`](../../doc/API.md#peerstoreaddressbookgetmultiaddrsforpeer) +- [`addressBook.set()`](../../doc/API.md#peerstoreaddressbookset) -It is important pointing out that the API methods which return arrays of data (`set`, `get`, `getMultiaddrsForPeer`) should return the `multiaddr` property of the `multiaddrInfo` and not the entire `multiaddrInfo` as the remaining data should be used internally. - -(Future considerations: Further API methods will probably be added in the context of multiaddr `ttl` and multiaddr confidence.) +(Future considerations: Further API methods will probably be added in the context of multiaddr validity and multiaddr confidence.) **Not Yet Implemented**: Multiaddr Confidence -## Key Book +#### Key Book The `keyBook` tracks the keys of the peers. **Not Yet Implemented** -## Protocol Book +#### Protocol Book The `protoBook` holds the identifiers of the protocols supported by each peer. The protocols supported by each peer are dynamic and will change over time. @@ -103,11 +110,11 @@ The `protoBook` holds the identifiers of the protocols supported by each peer. T A `peerId.toString()` identifier mapping to a `Set` of protocol identifier strings. - `protoBook.data` -- `protoBook.set()` -- `protoBook.get()` -- `protoBook.delete()` -- `protoBook.supports()` +- [`protoBook.delete()`](../../doc/API.md#peerstoreprotobookdelete) +- [`protoBook.get()`](../../doc/API.md#peerstoreprotobookget) +- [`protoBook.set()`](../../doc/API.md#peerstoreprotobookset) +- [`protoBook.supports()`](../../doc/API.md#peerstoreprotobooksupports) -## Metadata Book +#### Metadata Book **Not Yet Implemented** diff --git a/src/peer-store/address-book.js b/src/peer-store/address-book.js index 1ec7aaaf..c3c3ed25 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 af4725ed..02168d23 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 6e61b739..597b69e1 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) + * @param {boolean} [options.replace = true] * @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) + put (peerInfo, options) { + const multiaddrs = peerInfo.multiaddrs.toArray() + const protocols = Array.from(peerInfo.protocols || new Set()) + + this.addressBook.set(peerInfo.id, multiaddrs, options) + this.protoBook.set(peerInfo.id, protocols, options) + + const peer = this.find(peerInfo.id) + const pInfo = new PeerInfo(peerInfo.id) + + if (!peer) { + return pInfo } - let peer - // Already know the peer? - if (this.has(peerInfo.id)) { - peer = this.update(peerInfo) - } else { - peer = this.add(peerInfo) + peer.protocols.forEach((p) => pInfo.protocols.add(p)) + peer.multiaddrInfos.forEach((mi) => pInfo.multiaddrs.add(mi.multiaddr)) - // Emit the peer if silent = false - !options.silent && this.emit('peer', peerInfo) - } - return peer + return pInfo } + // TODO: Temporary adapter for modules using PeerStore + // This should be removed under a breaking change /** - * Add a new peer to the store. - * @param {PeerInfo} peerInfo - * @return {PeerInfo} - */ - add (peerInfo) { - if (!PeerInfo.isPeerInfo(peerInfo)) { - throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS) - } - - // 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 - } - - /** - * 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) - } - - this.emit('change:protocols', { - peerInfo: recorded, - protocols: Array.from(recorded.protocols) - }) - } - - // Add the public key if missing - if (!recorded.id.pubKey && peerInfo.id.pubKey) { - recorded.id.pubKey = peerInfo.id.pubKey - } - - return recorded - } - - /** - * 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) - return this.peers.get(peerId) + const pInfo = new PeerInfo(peerId) + peer.protocols.forEach((p) => pInfo.protocols.add(p)) + peer.multiaddrInfos.forEach((mi) => pInfo.multiaddrs.add(mi.multiaddr)) + + 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.remove(peerInfo.id.toB58String()) - this.add(peerInfo) - - // This should be cleaned up in PeerStore v2 - this.emit('change:multiaddrs', { - peerInfo, - multiaddrs: peerInfo.multiaddrs.toArray() - }) - - this.emit('change:protocols', { - peerInfo, - protocols: Array.from(peerInfo.protocols) - }) + 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} peer + * @param {PeerInfo} peerInfo * @returns {Array} */ - 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()}`) - }) + 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) || [] + // }) + } + + // 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 + // }) + } + } + + return peerInfos + } + + /** + * 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 + } + + /** + * Find the stored information of a given peer. + * @param {PeerId} peerId + * @returns {peerInfo} + */ + 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 7c859ce8..be7762e4 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 43fa235d..2aa6bcf8 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 25cb6d3f..ad239197 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 8520b285..0f91ebb2 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 c846e8ad..c87d8814 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 f6c716cb..6651b3de 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 8dde1c32..250e9bf0 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 ca32e023..e585233f 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 31fc38cd..f230ee19 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 8b96638e..140699eb 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,13 +24,14 @@ 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', () => { - describe('addressBook.set', () => { - let peerId - let ee, ab + let peerId - before(async () => { - peerId = await PeerId.create() - }) + before(async () => { + [peerId] = await peerUtils.createPeerId() + }) + + describe('addressBook.set', () => { + let ee, ab beforeEach(() => { ee = new EventEmitter() @@ -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 91628432..c4be9598 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') +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 proto1 = '/protocol1' +const proto2 = '/protocol2' +const proto3 = '/protocol3' describe('peer-store', () => { - let peerStore - - beforeEach(() => { - peerStore = new PeerStore() + let peerIds + before(async () => { + peerIds = await peerUtils.createPeerId({ + number: 3 + }) }) - it('should add a new peer and emit it when it does not exist', async () => { - const defer = pDefer() + describe('empty books', () => { + let peerStore - sinon.spy(peerStore, 'put') - sinon.spy(peerStore, 'add') - sinon.spy(peerStore, 'update') - - const [peerInfo] = await peerUtils.createPeerInfo() - - peerStore.on('peer', (peer) => { - expect(peer).to.exist() - defer.resolve() + beforeEach(() => { + peerStore = new PeerStore() }) - peerStore.put(peerInfo) - // Wait for peerStore to emit the peer - await defer.promise + it('has an empty map of peers', () => { + const peers = peerStore.peers + expect(peers.size).to.equal(0) + }) - expect(peerStore.put.callCount).to.equal(1) - expect(peerStore.add.callCount).to.equal(1) - expect(peerStore.update.callCount).to.equal(0) + it('returns false on trying to delete a non existant peerId', () => { + const deleted = peerStore.delete(peerIds[0]) + expect(deleted).to.equal(false) + }) + + it('returns undefined on trying to find a non existant peerId', () => { + const peerInfo = peerStore.find(peerIds[0]) + expect(peerInfo).to.not.exist() + }) }) - it('should update peer when it is already in the store', async () => { - const [peerInfo] = await peerUtils.createPeerInfo() + describe('previously populated books', () => { + let peerStore - // Put the peer in the store - peerStore.put(peerInfo) + beforeEach(() => { + peerStore = new PeerStore() - sinon.spy(peerStore, 'add') - sinon.spy(peerStore, 'update') + // Add peer0 with { addr1, addr2 } and { proto1 } + peerStore.addressBook.set(peerIds[0], [addr1, addr2]) + peerStore.protoBook.set(peerIds[0], proto1) - // 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') + // Add peer1 with { addr3 } and { proto2, proto3 } + peerStore.addressBook.set(peerIds[1], [addr3]) + peerStore.protoBook.set(peerIds[1], [proto2, proto3]) + + // Add peer2 { addr4 } + peerStore.addressBook.set(peerIds[2], [addr4]) }) - peerStore.put(peerInfo) + it('has peers', () => { + const peers = peerStore.peers - expect(peerStore.add.callCount).to.equal(0) - expect(peerStore.update.callCount).to.equal(1) - }) + 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 emit the "change:multiaddrs" event when a peer has new multiaddrs', async () => { - const defer = pDefer() - const [createdPeerInfo] = await peerUtils.createPeerInfo() + it('returns true on deleting a stored peer', () => { + const deleted = peerStore.delete(peerIds[0]) + expect(deleted).to.equal(true) - // Put the peer in the store - peerStore.put(createdPeerInfo) + const peers = peerStore.peers + expect(peers.size).to.equal(2) + expect(Array.from(peers.keys())).to.not.have.members([peerIds[0].toString()]) + }) - // When updating, "change:multiaddrs" event must not be emitted - peerStore.on('change:multiaddrs', ({ peerInfo, multiaddrs }) => { + 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) + + const peers = peerStore.peers + expect(peers.size).to.equal(2) + }) + + it('finds the stored information of a peer in all its books', () => { + const peerInfo = peerStore.find(peerIds[0]) 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() - }) - // If no protocols change, the event should not be emitted - peerStore.on('change:protocols', () => { - throw new Error('should not emit change:protocols') + expect(peerInfo.protocols).to.have.members([proto1]) + + const peerMultiaddrs = peerInfo.multiaddrInfos.map((mi) => mi.multiaddr) + expect(peerMultiaddrs).to.have.members([addr1, addr2]) }) - createdPeerInfo.multiaddrs.add(addr) - peerStore.put(createdPeerInfo) - - // Wait for peerStore to emit the event - await defer.promise - }) - - it('should emit the "change:protocols" event when a peer has new protocols', async () => { - const defer = pDefer() - const [createdPeerInfo] = await peerUtils.createPeerInfo() - - // Put the peer in the store - peerStore.put(createdPeerInfo) - - // 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 }) => { + 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.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() + expect(peerInfo.protocols.length).to.eql(0) + + const peerMultiaddrs = peerInfo.multiaddrInfos.map((mi) => mi.multiaddr) + expect(peerMultiaddrs).to.have.members([addr4]) }) - - createdPeerInfo.protocols.add('/new-protocol/1.0.0') - peerStore.put(createdPeerInfo) - - // Wait for peerStore to emit the event - await defer.promise - }) - - it('should be able to retrieve a peer from store through its b58str id', async () => { - const [peerInfo] = await peerUtils.createPeerInfo() - const id = peerInfo.id - - let retrievedPeer = peerStore.get(id) - expect(retrievedPeer).to.not.exist() - - // Put the peer in the store - peerStore.put(peerInfo) - - 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 - - let removed = peerStore.remove(id) - expect(removed).to.eql(false) - - // Put the peer in the store - peerStore.put(peerInfo) - expect(peerStore.peers.size).to.equal(1) - - removed = peerStore.remove(id) - expect(removed).to.eql(true) - expect(peerStore.peers.size).to.equal(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) }) }) - -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 cb8d6bbc..5c9fe5b9 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,13 +19,14 @@ const { const arraysAreEqual = (a, b) => a.length === b.length && a.sort().every((item, index) => b[index] === item) describe('protoBook', () => { - describe('protoBook.set', () => { - let peerId - let ee, pb + let peerId - before(async () => { - peerId = await PeerId.create() - }) + before(async () => { + [peerId] = await peerUtils.createPeerId() + }) + + describe('protoBook.set', () => { + let ee, pb beforeEach(() => { ee = new EventEmitter() @@ -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 056c4b49..a8c17c93 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)