chore: merge 0.28x updates

This commit is contained in:
Vasco Santos 2020-05-07 17:45:22 +02:00
commit c0bbda24c2
14 changed files with 409 additions and 65 deletions

View File

@ -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)
@ -870,6 +873,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.
@ -1393,3 +1479,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

View File

@ -83,6 +83,7 @@
"aegir": "^21.9.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chai-bytes": "^0.1.2",
"chai-string": "^1.5.0",
"cids": "^0.8.0",
"datastore-fs": "^1.0.0",

View File

@ -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)

View File

@ -12,6 +12,8 @@ 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.
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.
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.
@ -42,7 +44,7 @@ The `addressBook` keeps the known multiaddrs of a peer. The multiaddrs of each p
`Map<string, Address>`
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<string, PeerId`
A `peerId.toB58String()` identifier mapping to a `PeerId` of the peer. This instance contains the peer public key.
#### Protocol Book
@ -62,7 +66,7 @@ The `protoBook` holds the identifiers of the protocols supported by each peer. T
`Map<string, Set<string>>`
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/<b32 peer id no padding>`
@ -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

View File

@ -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} 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

View File

@ -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 {
@ -41,17 +42,15 @@ class PeerStore extends EventEmitter {
*/
this.addressBook = new AddressBook(this)
/**
* KeyBook containing a map of peerIdStr to their PeerId with public keys.
*/
this.keyBook = new KeyBook(this)
/**
* ProtoBook containing a map of peerIdStr to supported protocols.
*/
this.protoBook = new ProtoBook(this)
/**
* TODO: this should only exist until we have the key-book
* Map known peers to their peer-id.
* @type {Map<string, Array<PeerId>}
*/
this.peerIds = new Map()
}
/**
@ -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)

View File

@ -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<string, PeerId>}
*/
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

View File

@ -5,5 +5,8 @@ module.exports.NAMESPACE_COMMON = '/peers/'
// /peers/protos/<b32 peer id no padding>
module.exports.NAMESPACE_ADDRESS = '/peers/addrs/'
// /peers/keys/<b32 peer id no padding>
module.exports.NAMESPACE_KEYS = '/peers/keys/'
// /peers/addrs/<b32 peer id no padding>
module.exports.NAMESPACE_PROTOCOL = '/peers/protos/'

View File

@ -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<void>}
*/
_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)

View File

@ -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

View File

@ -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)
})
})

View File

@ -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)
})
})

View File

@ -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', () => {

View File

@ -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 = {