diff --git a/doc/API.md b/doc/API.md index 7c74d82dc3..883c67f5d5 100644 --- a/doc/API.md +++ b/doc/API.md @@ -26,6 +26,9 @@ * [`peerStore.addressBook.get`](#peerstoreaddressbookget) * [`peerStore.addressBook.getMultiaddrsForPeer`](#peerstoreaddressbookgetmultiaddrsforpeer) * [`peerStore.addressBook.set`](#peerstoreaddressbookset) + * [`peerStore.keyBook.delete`](#peerstorekeybookdelete) + * [`peerStore.keyBook.get`](#peerstorekeybookget) + * [`peerStore.keyBook.set`](#peerstorekeybookset) * [`peerStore.protoBook.add`](#peerstoreprotobookadd) * [`peerStore.protoBook.delete`](#peerstoreprotobookdelete) * [`peerStore.protoBook.get`](#peerstoreprotobookget) @@ -811,6 +814,89 @@ Add known `protocols` of a given peer. peerStore.protoBook.add(peerId, protocols) ``` + +### peerStore.keyBook.delete + +Delete the provided peer from the book. + +`peerStore.keyBook.delete(peerId)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | [`PeerId`][peer-id] | peerId to remove | + +#### Returns + +| Type | Description | +|------|-------------| +| `boolean` | true if found and removed | + +#### Example + +```js +peerStore.keyBook.delete(peerId) +// false +peerStore.keyBook.set(peerId, publicKey) +peerStore.keyBook.delete(peerId) +// true +``` + +### peerStore.keyBook.get + +Get the known `PublicKey` of a provided peer. + +`peerStore.keyBook.get(peerId)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | [`PeerId`][peer-id] | peerId to get | + +#### Returns + +| Type | Description | +|------|-------------| +| `RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey` | Peer PublicKey | + +#### Example + +```js +peerStore.keyBook.get(peerId) +// undefined +peerStore.keyBook.set(peerId, publicKey) +peerStore.keyBook.get(peerId) +// PublicKey +``` + +### peerStore.keyBook.set + +Set known `peerId`. This can include its Public Key. + +`peerStore.keyBook.set(peerId, publicKey)` + +#### Parameters + +| Name | Type | Description | +|------|------|-------------| +| peerId | [`PeerId`][peer-id] | peerId to set | +| publicKey | [`RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey`][keys] | peer's public key | + +#### Returns + +| Type | Description | +|------|-------------| +| `KeyBook` | Returns the Key Book component | + +#### Example + +```js +const publicKey = peerId.pubKey +peerStore.keyBook.set(peerId, publicKey) +``` + ### peerStore.protoBook.delete Delete the provided peer from the book. @@ -1334,3 +1420,4 @@ This event will be triggered anytime we are disconnected from another peer, rega [connection]: https://github.com/libp2p/js-interfaces/tree/master/src/connection [multiaddr]: https://github.com/multiformats/js-multiaddr [peer-id]: https://github.com/libp2p/js-peer-id +[keys]: https://github.com/libp2p/js-libp2p-crypto/tree/master/src/keys \ No newline at end of file diff --git a/package.json b/package.json index d29f1b3192..1335bdc286 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "aegir": "^21.9.0", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", + "chai-bytes": "^0.1.2", "cids": "^0.8.0", "delay": "^4.3.0", "dirty-chai": "^2.0.1", diff --git a/src/connection-manager/index.js b/src/connection-manager/index.js index 8df9521b1e..cc64468d1a 100644 --- a/src/connection-manager/index.js +++ b/src/connection-manager/index.js @@ -171,18 +171,22 @@ class ConnectionManager extends EventEmitter { * @param {Connection} connection */ onConnect (connection) { - const peerId = connection.remotePeer.toB58String() - const storedConn = this.connections.get(peerId) + const peerId = connection.remotePeer + const peerIdStr = peerId.toB58String() + const storedConn = this.connections.get(peerIdStr) if (storedConn) { storedConn.push(connection) } else { - this.connections.set(peerId, [connection]) + this.connections.set(peerIdStr, [connection]) this.emit('peer:connect', connection) } - if (!this._peerValues.has(peerId)) { - this._peerValues.set(peerId, this._options.defaultPeerValue) + this._libp2p.peerStore.addressBook.add(peerId, [connection.remoteAddr]) + this._libp2p.peerStore.keyBook.set(peerId, peerId.pubKey) + + if (!this._peerValues.has(peerIdStr)) { + this._peerValues.set(peerIdStr, this._options.defaultPeerValue) } this._checkLimit('maxConnections', this.size) diff --git a/src/peer-store/README.md b/src/peer-store/README.md index 2b4dd35ed9..3c1750571d 100644 --- a/src/peer-store/README.md +++ b/src/peer-store/README.md @@ -10,7 +10,9 @@ Several libp2p subsystems will perform operations, which will gather relevant in In a libp2p node's life, it will discover peers through its discovery protocols. In a typical discovery protocol, addresses of the peer are discovered along with its peer id. Once this happens, the PeerStore should collect this information for future (or immediate) usage by other subsystems. When the information is stored, the PeerStore should inform interested parties of the peer discovered (`peer` event). -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. +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. + +When a connection is being upgraded, more precisely after its encryption, or even in a discovery protocol, a libp2p node can get to know other parties public keys. In this scenario, libp2p will add the peer's public key to its `KeyBook`. 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. @@ -42,7 +44,7 @@ The `addressBook` keeps the known multiaddrs of a peer. The multiaddrs of each p `Map` -A `peerId.toString()` identifier mapping to a `Address` object, which should have the following structure: +A `peerId.toB58String()` identifier mapping to a `Address` object, which should have the following structure: ```js { @@ -52,9 +54,11 @@ A `peerId.toString()` identifier mapping to a `Address` object, which should hav #### Key Book -The `keyBook` tracks the keys of the peers. +The `keyBook` tracks the public keys of the peers by keeping their [`PeerId`][peer-id]. -**Not Yet Implemented** +`Map>` -A `peerId.toString()` identifier mapping to a `Set` of protocol identifier strings. +A `peerId.toB58String()` identifier mapping to a `Set` of protocol identifier strings. #### Metadata Book @@ -74,8 +78,9 @@ For the complete API documentation, you should check the [API.md](../../doc/API. Access to its underlying books: -- `peerStore.protoBook.*` - `peerStore.addressBook.*` +- `peerStore.keyBook.*` +- `peerStore.protoBook.*` ### Events @@ -107,8 +112,6 @@ All the known peer protocols are stored with a key pattern as follows: **KeyBook** -_NOT_YET_IMPLEMENTED_ - All public keys are stored under the following pattern: ` /peers/keys/` @@ -127,3 +130,5 @@ Metadata is stored under the following key pattern: - Further API methods will probably need to be added in the context of multiaddr validity and confidence. - When improving libp2p configuration for specific runtimes, we should take into account the PeerStore recommended datastore. - When improving libp2p configuration, we should think about a possible way of allowing the configuration of Bootstrap to be influenced by the persisted peers, as a way to decrease the load on Bootstrap nodes. + +[peer-id]: https://github.com/libp2p/js-peer-id diff --git a/src/peer-store/book.js b/src/peer-store/book.js index 72c70ff417..34d77e1da6 100644 --- a/src/peer-store/book.js +++ b/src/peer-store/book.js @@ -47,7 +47,7 @@ class Book { * Set data into the datastructure, persistence and emit it using the provided transformers. * @private * @param {PeerId} peerId peerId of the data to store - * @param {Array<*>} data data to store. + * @param {*} data data to store. * @param {Object} [options] storing options. * @param {boolean} [options.emit = true] emit the provided data. * @return {void} @@ -57,22 +57,22 @@ class Book { // Store data in memory this.data.set(b58key, data) - this._setPeerId(peerId) // Emit event - emit && this._ps.emit(this.eventName, { - peerId, - [this.eventProperty]: this.eventTransformer(data) - }) + emit && this._emit(peerId, data) } /** - * Add known data of a provided peer. + * Emit data. + * @private * @param {PeerId} peerId - * @param {Array|Data} data + * @param {*} data */ - add (peerId, data) { - throw errcode(new Error('set must be implemented by the subclass'), 'ERR_NOT_IMPLEMENTED') + _emit (peerId, data) { + this._ps.emit(this.eventName, { + peerId, + [this.eventProperty]: this.eventTransformer(data) + }) } /** @@ -104,24 +104,10 @@ class Book { return false } - this._ps.emit(this.eventName, { - peerId, - [this.eventProperty]: [] - }) + this._emit(peerId, []) return true } - - /** - * Set PeerId into peerStore datastructure. - * @private - * @param {PeerId} peerId - */ - _setPeerId (peerId) { - if (!this._ps.peerIds.get(peerId)) { - this._ps.peerIds.set(peerId.toB58String(), peerId) - } - } } module.exports = Book diff --git a/src/peer-store/index.js b/src/peer-store/index.js index a88b860009..d19b2de8c3 100644 --- a/src/peer-store/index.js +++ b/src/peer-store/index.js @@ -9,6 +9,7 @@ const { EventEmitter } = require('events') const PeerId = require('peer-id') const AddressBook = require('./address-book') +const KeyBook = require('./key-book') const ProtoBook = require('./proto-book') const { @@ -42,16 +43,14 @@ class PeerStore extends EventEmitter { this.addressBook = new AddressBook(this) /** - * ProtoBook containing a map of peerIdStr to supported protocols. + * KeyBook containing a map of peerIdStr to their PeerId with public keys. */ - this.protoBook = new ProtoBook(this) + this.keyBook = new KeyBook(this) /** - * TODO: this should only exist until we have the key-book - * Map known peers to their peer-id. - * @type {Map} + * ProtoBook containing a map of peerIdStr to supported protocols. */ - this.peerIds = new Map() + this.protoBook = new ProtoBook(this) } /** @@ -73,7 +72,7 @@ class PeerStore extends EventEmitter { // AddressBook for (const [idStr, addresses] of this.addressBook.data.entries()) { - const id = PeerId.createFromCID(idStr) + const id = this.keyBook.data.get(idStr) || PeerId.createFromCID(idStr) peersData.set(idStr, { id, addresses, @@ -84,10 +83,11 @@ class PeerStore extends EventEmitter { // ProtoBook for (const [idStr, protocols] of this.protoBook.data.entries()) { const pData = peersData.get(idStr) + const id = this.keyBook.data.get(idStr) || PeerId.createFromCID(idStr) if (!pData) { peersData.set(idStr, { - id: PeerId.createFromCID(idStr), + id, addresses: [], protocols: Array.from(protocols) }) @@ -104,8 +104,10 @@ class PeerStore extends EventEmitter { */ delete (peerId) { const addressesDeleted = this.addressBook.delete(peerId) + const keyDeleted = this.keyBook.delete(peerId) const protocolsDeleted = this.protoBook.delete(peerId) - return addressesDeleted || protocolsDeleted + + return addressesDeleted || keyDeleted || protocolsDeleted } /** @@ -118,7 +120,7 @@ class PeerStore extends EventEmitter { throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS) } - const id = this.peerIds.get(peerId.toB58String()) + const id = this.keyBook.data.get(peerId.toB58String()) const addresses = this.addressBook.get(peerId) const protocols = this.protoBook.get(peerId) diff --git a/src/peer-store/key-book.js b/src/peer-store/key-book.js new file mode 100644 index 0000000000..a5b26edce3 --- /dev/null +++ b/src/peer-store/key-book.js @@ -0,0 +1,85 @@ +'use strict' + +const errcode = require('err-code') +const debug = require('debug') +const log = debug('libp2p:peer-store:key-book') +log.error = debug('libp2p:peer-store:key-book:error') + +const PeerId = require('peer-id') + +const Book = require('./book') + +const { + codes: { ERR_INVALID_PARAMETERS } +} = require('../errors') + +/** + * The KeyBook is responsible for keeping the known public keys of a peer. + */ +class KeyBook extends Book { + /** + * @constructor + * @param {PeerStore} peerStore + */ + constructor (peerStore) { + super({ + peerStore, + eventName: 'change:pubkey', + eventProperty: 'pubkey', + eventTransformer: (data) => data.pubKey + }) + + /** + * Map known peers to their known Public Key. + * @type {Map} + */ + this.data = new Map() + } + + /** + * Set the Peer public key. + * @override + * @param {PeerId} peerId + * @param {RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey} publicKey + * @return {KeyBook} + */ + set (peerId, publicKey) { + 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) + } + + const id = peerId.toB58String() + const recPeerId = this.data.get(id) + + // If no record available, and this is valid + if (!recPeerId && publicKey) { + // This might be unecessary, but we want to store the PeerId + // to avoid an async operation when reconstructing the PeerId + peerId.pubKey = publicKey + + this._setData(peerId, peerId) + log(`stored provided public key for ${id}`) + } + + return this + } + + /** + * Get Public key of the given PeerId, if stored. + * @override + * @param {PeerId} peerId + * @return {RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey} + */ + get (peerId) { + if (!PeerId.isPeerId(peerId)) { + throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS) + } + + const rec = this.data.get(peerId.toB58String()) + + return rec ? rec.pubKey : undefined + } +} + +module.exports = KeyBook diff --git a/src/peer-store/persistent/consts.js b/src/peer-store/persistent/consts.js index 86b0ec61e1..9679cc5728 100644 --- a/src/peer-store/persistent/consts.js +++ b/src/peer-store/persistent/consts.js @@ -5,5 +5,8 @@ module.exports.NAMESPACE_COMMON = '/peers/' // /peers/protos/ module.exports.NAMESPACE_ADDRESS = '/peers/addrs/' +// /peers/keys/ +module.exports.NAMESPACE_KEYS = '/peers/keys/' + // /peers/addrs/ module.exports.NAMESPACE_PROTOCOL = '/peers/protos/' diff --git a/src/peer-store/persistent/index.js b/src/peer-store/persistent/index.js index 66572b7bb0..366e9bc48f 100644 --- a/src/peer-store/persistent/index.js +++ b/src/peer-store/persistent/index.js @@ -13,6 +13,7 @@ const PeerStore = require('..') const { NAMESPACE_ADDRESS, NAMESPACE_COMMON, + NAMESPACE_KEYS, NAMESPACE_PROTOCOL } = require('./consts') @@ -56,10 +57,11 @@ class PersistentPeerStore extends PeerStore { // Handlers for dirty peers this.on('change:protocols', this._addDirtyPeer) this.on('change:multiaddrs', this._addDirtyPeer) + this.on('change:pubkey', this._addDirtyPeer) // Load data for await (const entry of this._datastore.query({ prefix: NAMESPACE_COMMON })) { - this._processDatastoreEntry(entry) + await this._processDatastoreEntry(entry) } log('PeerStore started') @@ -109,12 +111,15 @@ class PersistentPeerStore extends PeerStore { log('create batch commit') const batch = this._datastore.batch() for (const peerIdStr of commitPeers) { - // PeerId (replace by keyBook) - const peerId = this.peerIds.get(peerIdStr) + // PeerId + const peerId = this.keyBook.data.get(peerIdStr) || PeerId.createFromCID(peerIdStr) // Address Book this._batchAddressBook(peerId, batch) + // Key Book + this._batchKeyBook(peerId, batch) + // Proto Book this._batchProtoBook(peerId, batch) } @@ -154,6 +159,31 @@ class PersistentPeerStore extends PeerStore { } } + /** + * Add Key book data of the peer to the batch. + * @private + * @param {PeerId} peerId + * @param {Object} batch + */ + _batchKeyBook (peerId, batch) { + const b32key = peerId.toString() + const key = new Key(`${NAMESPACE_KEYS}${b32key}`) + + try { + // Deleted from the book + if (!peerId.pubKey) { + batch.delete(key) + return + } + + const encodedData = peerId.marshalPubKey() + + batch.put(key, encodedData) + } catch (err) { + log.error(err) + } + } + /** * Add proto book data of the peer to the batch. * @private @@ -187,8 +217,9 @@ class PersistentPeerStore extends PeerStore { * @param {Object} params * @param {Key} params.key datastore key * @param {Buffer} params.value datastore value stored + * @return {Promise} */ - _processDatastoreEntry ({ key, value }) { + async _processDatastoreEntry ({ key, value }) { try { const keyParts = key.toString().split('/') const peerId = PeerId.createFromCID(keyParts[3]) @@ -205,6 +236,14 @@ class PersistentPeerStore extends PeerStore { })), { emit: false }) break + case 'keys': + decoded = await PeerId.createFromPubKey(value) + + this.keyBook._setData( + decoded, + decoded, + { emit: false }) + break case 'protos': decoded = Protocols.decode(value) diff --git a/test/identify/index.spec.js b/test/identify/index.spec.js index 9ca892ff05..abcbbeb34e 100644 --- a/test/identify/index.spec.js +++ b/test/identify/index.spec.js @@ -242,8 +242,8 @@ describe('Identify', () => { expect(connection).to.exist() // Wait for peer store to be updated - // Dialer._createDialTarget (add), Identify (replace) - await pWaitFor(() => peerStoreSpySet.callCount === 1 && peerStoreSpyAdd.callCount === 1) + // Dialer._createDialTarget (add), Connected (add), Identify (replace) + await pWaitFor(() => peerStoreSpySet.callCount === 1 && peerStoreSpyAdd.callCount === 2) expect(libp2p.identifyService.identify.callCount).to.equal(1) // The connection should have no open streams diff --git a/test/peer-store/key-book.spec.js b/test/peer-store/key-book.spec.js new file mode 100644 index 0000000000..22b708fbf2 --- /dev/null +++ b/test/peer-store/key-book.spec.js @@ -0,0 +1,64 @@ +'use strict' +/* eslint-env mocha */ + +const chai = require('chai') +chai.use(require('dirty-chai')) +chai.use(require('chai-bytes')) +const { expect } = chai +const sinon = require('sinon') + +const PeerStore = require('../../src/peer-store') + +const peerUtils = require('../utils/creators/peer') +const { + codes: { ERR_INVALID_PARAMETERS } +} = require('../../src/errors') + +describe('keyBook', () => { + let peerId, peerStore, kb + + beforeEach(async () => { + [peerId] = await peerUtils.createPeerId() + peerStore = new PeerStore() + kb = peerStore.keyBook + }) + + it('throws invalid parameters error if invalid PeerId is provided in set', () => { + try { + kb.set('invalid peerId') + } catch (err) { + expect(err.code).to.equal(ERR_INVALID_PARAMETERS) + return + } + throw new Error('invalid peerId should throw error') + }) + + it('throws invalid parameters error if invalid PeerId is provided in get', () => { + try { + kb.get('invalid peerId') + } catch (err) { + expect(err.code).to.equal(ERR_INVALID_PARAMETERS) + return + } + throw new Error('invalid peerId should throw error') + }) + + it('stores the peerId in the book and returns the public key', () => { + // Set PeerId + kb.set(peerId, peerId.pubKey) + + // Get public key + const pubKey = kb.get(peerId) + expect(peerId.pubKey.bytes).to.equalBytes(pubKey.bytes) + }) + + it('should not store if already stored', () => { + const spy = sinon.spy(kb, '_setData') + + // Set PeerId + kb.set(peerId, peerId.pubKey) + kb.set(peerId, peerId.pubKey) + + expect(spy).to.have.property('callCount', 1) + }) +}) diff --git a/test/peer-store/peer-store.node.js b/test/peer-store/peer-store.node.js new file mode 100644 index 0000000000..c6b34eb541 --- /dev/null +++ b/test/peer-store/peer-store.node.js @@ -0,0 +1,53 @@ +'use strict' +/* eslint-env mocha */ + +const chai = require('chai') +chai.use(require('dirty-chai')) +chai.use(require('chai-bytes')) +const { expect } = chai +const sinon = require('sinon') + +const baseOptions = require('../utils/base-options') +const peerUtils = require('../utils/creators/peer') + +describe('libp2p.peerStore', () => { + let libp2p, remoteLibp2p + + beforeEach(async () => { + [libp2p, remoteLibp2p] = await peerUtils.createPeer({ + number: 2, + populateAddressBooks: false, + config: { + ...baseOptions + } + }) + }) + + it('adds peer address to AddressBook and keys to the keybook when establishing connection', async () => { + const idStr = libp2p.peerId.toB58String() + const remoteIdStr = remoteLibp2p.peerId.toB58String() + + const spyAddressBook = sinon.spy(libp2p.peerStore.addressBook, 'add') + const spyKeyBook = sinon.spy(libp2p.peerStore.keyBook, 'set') + + const remoteMultiaddr = `${remoteLibp2p.multiaddrs[0]}/p2p/${remoteIdStr}` + const conn = await libp2p.dial(remoteMultiaddr) + + expect(conn).to.exist() + expect(spyAddressBook).to.have.property('called', true) + expect(spyKeyBook).to.have.property('called', true) + + const localPeers = libp2p.peerStore.peers + expect(localPeers.size).to.equal(1) + + // TODO: needs https://github.com/NodeFactoryIo/js-libp2p-noise/issues/58 + // const publicKeyInLocalPeer = localPeers.get(remoteIdStr).id.pubKey + // expect(publicKeyInLocalPeer.bytes).to.equalBytes(remoteLibp2p.peerId.pubKey.bytes) + + const remotePeers = remoteLibp2p.peerStore.peers + expect(remotePeers.size).to.equal(1) + const publicKeyInRemotePeer = remotePeers.get(idStr).id.pubKey + expect(publicKeyInRemotePeer).to.exist() + expect(publicKeyInRemotePeer.bytes).to.equalBytes(libp2p.peerId.pubKey.bytes) + }) +}) diff --git a/test/peer-store/peer-store.spec.js b/test/peer-store/peer-store.spec.js index c4f9fbed39..1884ea4dc6 100644 --- a/test/peer-store/peer-store.spec.js +++ b/test/peer-store/peer-store.spec.js @@ -48,6 +48,13 @@ describe('peer-store', () => { const peer = peerStore.get(peerIds[0]) expect(peer).to.not.exist() }) + + it('sets the peer\'s public key to the KeyBook', () => { + peerStore.keyBook.set(peerIds[0], peerIds[0].pubKey) + + const pubKey = peerStore.keyBook.get(peerIds[0]) + expect(pubKey).to.exist() + }) }) describe('previously populated books', () => { @@ -108,6 +115,8 @@ describe('peer-store', () => { const peerMultiaddrs = peer.addresses.map((mi) => mi.multiaddr) expect(peerMultiaddrs).to.have.members([addr1, addr2]) + + expect(peer.id).to.exist() }) it('gets the stored information of a peer that is not present in all its books', () => { diff --git a/test/peer-store/persisted-peer-store.spec.js b/test/peer-store/persisted-peer-store.spec.js index 9f8a2f5146..a8b0fea1ba 100644 --- a/test/peer-store/persisted-peer-store.spec.js +++ b/test/peer-store/persisted-peer-store.spec.js @@ -68,16 +68,16 @@ describe('Persisted PeerStore', () => { // AddressBook peerStore.addressBook.set(peer, multiaddrs) - expect(spyDirty).to.have.property('callCount', 1) + expect(spyDirty).to.have.property('callCount', 1) // Address expect(spyDs).to.have.property('callCount', 1) // ProtoBook peerStore.protoBook.set(peer, protocols) - expect(spyDirty).to.have.property('callCount', 2) + expect(spyDirty).to.have.property('callCount', 2) // Protocol expect(spyDs).to.have.property('callCount', 2) - // Should have two peer records stored in the datastore + // Should have three peer records stored in the datastore const queryParams = { prefix: '/peers/' } @@ -110,15 +110,19 @@ describe('Persisted PeerStore', () => { peerStore.addressBook.set(peers[0], [multiaddrs[0]]) peerStore.addressBook.set(peers[1], [multiaddrs[1]]) + // KeyBook + peerStore.keyBook.set(peers[0], peers[0].pubKey) + peerStore.keyBook.set(peers[1], peers[1].pubKey) + // ProtoBook peerStore.protoBook.set(peers[0], protocols) peerStore.protoBook.set(peers[1], protocols) - expect(spyDs).to.have.property('callCount', 4) + expect(spyDs).to.have.property('callCount', 6) // 2 AddressBook + 2 KeyBook + 2 ProtoBook expect(peerStore.peers.size).to.equal(2) await peerStore.stop() - peerStore.peerIds.clear() + peerStore.keyBook.data.clear() peerStore.addressBook.data.clear() peerStore.protoBook.data.clear() @@ -127,11 +131,12 @@ describe('Persisted PeerStore', () => { await peerStore.start() - expect(spy).to.have.property('callCount', 4) // 4 datastore entries - expect(spyDs).to.have.property('callCount', 4) // 4 previous operations + expect(spy).to.have.property('callCount', 6) + expect(spyDs).to.have.property('callCount', 6) expect(peerStore.peers.size).to.equal(2) expect(peerStore.addressBook.data.size).to.equal(2) + expect(peerStore.keyBook.data.size).to.equal(2) expect(peerStore.protoBook.data.size).to.equal(2) }) @@ -149,6 +154,7 @@ describe('Persisted PeerStore', () => { const spyDs = sinon.spy(datastore, 'batch') const spyAddressBook = sinon.spy(peerStore.addressBook, 'delete') + const spyKeyBook = sinon.spy(peerStore.keyBook, 'delete') const spyProtoBook = sinon.spy(peerStore.protoBook, 'delete') // Delete from PeerStore @@ -156,6 +162,7 @@ describe('Persisted PeerStore', () => { await peerStore.stop() expect(spyAddressBook).to.have.property('callCount', 1) + expect(spyKeyBook).to.have.property('callCount', 1) expect(spyProtoBook).to.have.property('callCount', 1) expect(spyDs).to.have.property('callCount', 2) @@ -199,7 +206,7 @@ describe('Persisted PeerStore', () => { // Remove data from the same Peer peerStore.addressBook.delete(peers[0]) - expect(spyDirty).to.have.property('callCount', 3) + expect(spyDirty).to.have.property('callCount', 3) // 2 AddrBook ops, 1 ProtoBook op expect(peerStore._dirtyPeers.size).to.equal(1) expect(spyDs).to.have.property('callCount', 0) @@ -215,7 +222,6 @@ describe('Persisted PeerStore', () => { expect(spyDirty).to.have.property('callCount', 4) expect(spyDs).to.have.property('callCount', 1) - expect(peerStore._dirtyPeers.size).to.equal(0) // Reset // Should have two peer records stored in the datastore let count = 0 @@ -239,7 +245,7 @@ describe('Persisted PeerStore', () => { peerStore.protoBook.set(peer, protocols) expect(spyDs).to.have.property('callCount', 0) - expect(spyDirty).to.have.property('callCount', 1) + expect(spyDirty).to.have.property('callCount', 1) // ProtoBook expect(peerStore._dirtyPeers.size).to.equal(1) const queryParams = {