feat: async peerstore backed by datastores (#1058)

We have a peerstore that keeps all data for all observed peers in memory with no eviction.

This is fine when you don't discover many peers but when using the DHT you encounter a significant number of peers so our peer storage grows and grows over time.

We have a persistent peer store, but it just periodically writes peers into the datastore to be read at startup, still keeping them in memory.

It also means a restart doesn't give you any temporary reprieve from the memory leak as the previously observed peer data is read into memory at startup.

This change refactors the peerstore to use a datastore by default, reading and writing peer info as it arrives.  It can be configured with a MemoryDatastore if desired.

It was necessary to change the peerstore and *book interfaces to be asynchronous since the datastore api is asynchronous.

BREAKING CHANGE: `libp2p.handle`, `libp2p.registrar.register` and the peerstore methods have become async
This commit is contained in:
Alex Potsides
2022-01-20 12:03:35 +00:00
committed by GitHub
parent 0a4dc54d08
commit 978eb3676f
94 changed files with 3263 additions and 4039 deletions

View File

@ -10,6 +10,10 @@ const { baseOptions } = require('./utils')
describe('Protocol prefix is configurable', () => {
let libp2p
afterEach(async () => {
libp2p && await libp2p.stop()
})
it('protocolPrefix is provided', async () => {
const testProtocol = 'test-protocol'
libp2p = await create(mergeOptions(baseOptions, {
@ -17,31 +21,27 @@ describe('Protocol prefix is configurable', () => {
protocolPrefix: testProtocol
}
}))
await libp2p.start()
const protocols = libp2p.peerStore.protoBook.get(libp2p.peerId);
[
const protocols = await libp2p.peerStore.protoBook.get(libp2p.peerId)
expect(protocols).to.include.members([
'/libp2p/circuit/relay/0.1.0',
`/${testProtocol}/id/1.0.0`,
`/${testProtocol}/id/push/1.0.0`,
`/${testProtocol}/ping/1.0.0`
].forEach((i, idx) => {
expect(protocols[idx]).equals(i)
})
await libp2p.stop()
])
})
it('protocolPrefix is not provided', async () => {
libp2p = await create(baseOptions)
await libp2p.start()
const protocols = libp2p.peerStore.protoBook.get(libp2p.peerId);
[
const protocols = await libp2p.peerStore.protoBook.get(libp2p.peerId)
expect(protocols).to.include.members([
'/libp2p/circuit/relay/0.1.0',
'/ipfs/id/1.0.0',
'/ipfs/id/push/1.0.0',
'/ipfs/ping/1.0.0'
].forEach((i, idx) => {
expect(protocols[idx]).equals(i)
})
await libp2p.stop()
])
})
})

View File

@ -71,7 +71,7 @@ describe('Connection Manager', () => {
sinon.spy(libp2p.connectionManager, 'emit')
sinon.spy(remoteLibp2p.connectionManager, 'emit')
libp2p.peerStore.addressBook.set(remoteLibp2p.peerId, remoteLibp2p.multiaddrs)
await libp2p.peerStore.addressBook.set(remoteLibp2p.peerId, remoteLibp2p.multiaddrs)
await libp2p.dial(remoteLibp2p.peerId)
// check connect event
@ -219,9 +219,9 @@ describe('libp2p.connections', () => {
})
// Populate PeerStore before starting
libp2p.peerStore.addressBook.set(nodes[0].peerId, nodes[0].multiaddrs)
libp2p.peerStore.addressBook.set(nodes[1].peerId, nodes[1].multiaddrs)
libp2p.peerStore.protoBook.set(nodes[1].peerId, ['/protocol-min-conns'])
await libp2p.peerStore.addressBook.set(nodes[0].peerId, nodes[0].multiaddrs)
await libp2p.peerStore.addressBook.set(nodes[1].peerId, nodes[1].multiaddrs)
await libp2p.peerStore.protoBook.set(nodes[1].peerId, ['/protocol-min-conns'])
await libp2p.start()

View File

@ -77,7 +77,7 @@ describe('Connection Manager', () => {
const value = Math.random()
spies.set(value, spy)
libp2p.connectionManager.setPeerValue(connection.remotePeer, value)
libp2p.connectionManager.onConnect(connection)
await libp2p.connectionManager.onConnect(connection)
}))
// get the lowest value
@ -109,8 +109,8 @@ describe('Connection Manager', () => {
const spy = sinon.spy()
await Promise.all([...new Array(max + 1)].map(async () => {
const connection = await mockConnection()
sinon.stub(connection, 'close').callsFake(() => spy()) // eslint-disable-line
libp2p.connectionManager.onConnect(connection)
sinon.stub(connection, 'close').callsFake(async () => spy()) // eslint-disable-line
await libp2p.connectionManager.onConnect(connection)
}))
expect(libp2p.connectionManager._maybeDisconnectOne).to.have.property('callCount', 1)

View File

@ -291,11 +291,11 @@ describe('content-routing', () => {
yield result
})
expect(node.peerStore.addressBook.get(providerPeerId)).to.not.be.ok()
expect(await node.peerStore.has(providerPeerId)).to.not.be.ok()
await drain(node.contentRouting.findProviders('a cid'))
expect(node.peerStore.addressBook.get(providerPeerId)).to.deep.include({
expect(await node.peerStore.addressBook.get(providerPeerId)).to.deep.include({
isCertified: false,
multiaddr: result.multiaddrs[0]
})
@ -377,7 +377,7 @@ describe('content-routing', () => {
await drain(node.contentRouting.findProviders('a cid'))
expect(node.peerStore.addressBook.get(providerPeerId)).to.deep.include({
expect(await node.peerStore.addressBook.get(providerPeerId)).to.deep.include({
isCertified: false,
multiaddr: result1.multiaddrs[0]
}).and.to.deep.include({

View File

@ -45,8 +45,8 @@ describe('DHT subsystem operates correctly', () => {
remoteLibp2p.start()
])
libp2p.peerStore.addressBook.set(remotePeerId, [remoteListenAddr])
remAddr = libp2p.peerStore.addressBook.getMultiaddrsForPeer(remotePeerId)[0]
await libp2p.peerStore.addressBook.set(remotePeerId, [remoteListenAddr]);
[remAddr] = await libp2p.peerStore.addressBook.getMultiaddrsForPeer(remotePeerId)
})
afterEach(() => Promise.all([
@ -106,8 +106,8 @@ describe('DHT subsystem operates correctly', () => {
await libp2p.start()
await remoteLibp2p.start()
libp2p.peerStore.addressBook.set(remotePeerId, [remoteListenAddr])
remAddr = libp2p.peerStore.addressBook.getMultiaddrsForPeer(remotePeerId)[0]
await libp2p.peerStore.addressBook.set(remotePeerId, [remoteListenAddr])
remAddr = (await libp2p.peerStore.addressBook.getMultiaddrsForPeer(remotePeerId))[0]
})
afterEach(() => Promise.all([

View File

@ -18,7 +18,7 @@ const AggregateError = require('aggregate-error')
const { Connection } = require('libp2p-interfaces/src/connection')
const { AbortError } = require('libp2p-interfaces/src/transport/errors')
const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string')
const { MemoryDatastore } = require('datastore-core/memory')
const Libp2p = require('../../src')
const Dialer = require('../../src/dialer')
const AddressManager = require('../../src/address-manager')
@ -48,7 +48,10 @@ describe('Dialing (direct, TCP)', () => {
PeerId.createFromJSON(Peers[1])
])
peerStore = new PeerStore({ peerId: remotePeerId })
peerStore = new PeerStore({
peerId: remotePeerId,
datastore: new MemoryDatastore()
})
remoteTM = new TransportManager({
libp2p: {
addressManager: new AddressManager(remotePeerId, { listen: [listenAddr] }),
@ -62,7 +65,10 @@ describe('Dialing (direct, TCP)', () => {
localTM = new TransportManager({
libp2p: {
peerId: localPeerId,
peerStore: new PeerStore({ peerId: localPeerId })
peerStore: new PeerStore({
peerId: localPeerId,
datastore: new MemoryDatastore()
})
},
upgrader: mockUpgrader
})
@ -113,7 +119,10 @@ describe('Dialing (direct, TCP)', () => {
it('should be able to connect to a given peer id', async () => {
const peerId = await PeerId.createFromJSON(Peers[0])
const peerStore = new PeerStore({ peerId })
const peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
const dialer = new Dialer({
transportManager: localTM,
peerStore
@ -249,7 +258,7 @@ describe('Dialing (direct, TCP)', () => {
connEncryption: [Crypto]
}
})
remoteLibp2p.handle('/echo/1.0.0', ({ stream }) => pipe(stream, stream))
await remoteLibp2p.handle('/echo/1.0.0', ({ stream }) => pipe(stream, stream))
await remoteLibp2p.start()
remoteAddr = remoteLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${remotePeerId.toB58String()}`)
@ -339,12 +348,12 @@ describe('Dialing (direct, TCP)', () => {
})
// register some stream handlers to simulate several protocols
libp2p.handle('/stream-count/1', ({ stream }) => pipe(stream, stream))
libp2p.handle('/stream-count/2', ({ stream }) => pipe(stream, stream))
remoteLibp2p.handle('/stream-count/3', ({ stream }) => pipe(stream, stream))
remoteLibp2p.handle('/stream-count/4', ({ stream }) => pipe(stream, stream))
await libp2p.handle('/stream-count/1', ({ stream }) => pipe(stream, stream))
await libp2p.handle('/stream-count/2', ({ stream }) => pipe(stream, stream))
await remoteLibp2p.handle('/stream-count/3', ({ stream }) => pipe(stream, stream))
await remoteLibp2p.handle('/stream-count/4', ({ stream }) => pipe(stream, stream))
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.multiaddrs)
await libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.multiaddrs)
const connection = await libp2p.dial(remotePeerId)
// Create local to remote streams
@ -363,8 +372,8 @@ describe('Dialing (direct, TCP)', () => {
// Verify stream count
const remoteConn = remoteLibp2p.connectionManager.get(libp2p.peerId)
expect(connection.streams).to.have.length(5)
expect(remoteConn.streams).to.have.length(5)
expect(connection.streams).to.have.length(6)
expect(remoteConn.streams).to.have.length(6)
// Close the connection and verify all streams have been closed
await connection.close()

View File

@ -13,7 +13,7 @@ const { NOISE: Crypto } = require('@chainsafe/libp2p-noise')
const { Multiaddr } = require('multiaddr')
const AggregateError = require('aggregate-error')
const { AbortError } = require('libp2p-interfaces/src/transport/errors')
const { MemoryDatastore } = require('datastore-core/memory')
const { codes: ErrorCodes } = require('../../src/errors')
const Constants = require('../../src/constants')
const Dialer = require('../../src/dialer')
@ -36,7 +36,10 @@ describe('Dialing (direct, WebSockets)', () => {
before(async () => {
[peerId] = await createPeerId()
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
localTM = new TransportManager({
libp2p: {},
upgrader: mockUpgrader,
@ -215,7 +218,7 @@ describe('Dialing (direct, WebSockets)', () => {
})
// Inject data in the AddressBook
peerStore.addressBook.add(peerId, peerMultiaddrs)
await peerStore.addressBook.add(peerId, peerMultiaddrs)
// Perform 3 multiaddr dials
await dialer.connectToPeer(peerId)

View File

@ -10,7 +10,6 @@ const duplexPair = require('it-pair/duplex')
const { Multiaddr } = require('multiaddr')
const pWaitFor = require('p-wait-for')
const { toString: unit8ArrayToString } = require('uint8arrays/to-string')
const { codes: Errors } = require('../../src/errors')
const IdentifyService = require('../../src/identify')
const multicodecs = IdentifyService.multicodecs
@ -22,7 +21,7 @@ const baseOptions = require('../utils/base-options.browser')
const { updateSelfPeerRecord } = require('../../src/record/utils')
const pkg = require('../../package.json')
const AddressManager = require('../../src/address-manager')
const { MemoryDatastore } = require('datastore-core/memory')
const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser')
const remoteAddr = MULTIADDRS_WEBSOCKETS[0]
const listenMaddrs = [new Multiaddr('/ip4/127.0.0.1/tcp/15002/ws')]
@ -38,10 +37,16 @@ describe('Identify', () => {
PeerId.createFromJSON(Peers[1])
]))
localPeerStore = new PeerStore({ peerId: localPeer })
localPeerStore = new PeerStore({
peerId: localPeer,
datastore: new MemoryDatastore()
})
localPeerStore.protoBook.set(localPeer, protocols)
remotePeerStore = new PeerStore({ peerId: remotePeer })
remotePeerStore = new PeerStore({
peerId: remotePeer,
datastore: new MemoryDatastore()
})
remotePeerStore.protoBook.set(remotePeer, protocols)
localAddressManager = new AddressManager(localPeer)
@ -103,7 +108,7 @@ describe('Identify', () => {
expect(localIdentify.peerStore.protoBook.set.callCount).to.equal(1)
// Validate the remote peer gets updated in the peer store
const addresses = localIdentify.peerStore.addressBook.get(remotePeer)
const addresses = await localIdentify.peerStore.addressBook.get(remotePeer)
expect(addresses).to.exist()
expect(addresses).have.lengthOf(listenMaddrs.length)
expect(addresses.map((a) => a.multiaddr)[0].equals(listenMaddrs[0]))
@ -149,7 +154,7 @@ describe('Identify', () => {
sinon.spy(localIdentify.peerStore.addressBook, 'set')
sinon.spy(localIdentify.peerStore.protoBook, 'set')
sinon.spy(localIdentify.peerStore.metadataBook, 'set')
sinon.spy(localIdentify.peerStore.metadataBook, 'setValue')
// Run identify
await Promise.all([
@ -164,7 +169,7 @@ describe('Identify', () => {
expect(localIdentify.peerStore.addressBook.set.callCount).to.equal(1)
expect(localIdentify.peerStore.protoBook.set.callCount).to.equal(1)
const metadataArgs = localIdentify.peerStore.metadataBook.set.firstCall.args
const metadataArgs = localIdentify.peerStore.metadataBook.setValue.firstCall.args
expect(metadataArgs[0].id.bytes).to.equal(remotePeer.bytes)
expect(metadataArgs[1]).to.equal('AgentVersion')
expect(unit8ArrayToString(metadataArgs[2])).to.equal(agentVersion)
@ -221,13 +226,16 @@ describe('Identify', () => {
.and.to.have.property('code', Errors.ERR_INVALID_PEER)
})
it('should store host data and protocol version into metadataBook', () => {
it('should store host data and protocol version into metadataBook', async () => {
const agentVersion = 'js-project/1.0.0'
const peerStore = new PeerStore({ peerId: localPeer })
const peerStore = new PeerStore({
peerId: localPeer,
datastore: new MemoryDatastore()
})
sinon.spy(peerStore.metadataBook, 'set')
sinon.spy(peerStore.metadataBook, 'setValue')
new IdentifyService({ // eslint-disable-line no-new
const service = new IdentifyService({ // eslint-disable-line no-new
libp2p: {
peerId: localPeer,
connectionManager: new EventEmitter(),
@ -243,23 +251,30 @@ describe('Identify', () => {
protocols
})
expect(peerStore.metadataBook.set.callCount).to.eql(2)
await service.start()
const storedAgentVersion = peerStore.metadataBook.getValue(localPeer, 'AgentVersion')
const storedProtocolVersion = peerStore.metadataBook.getValue(localPeer, 'ProtocolVersion')
expect(peerStore.metadataBook.setValue.callCount).to.eql(2)
const storedAgentVersion = await peerStore.metadataBook.getValue(localPeer, 'AgentVersion')
const storedProtocolVersion = await peerStore.metadataBook.getValue(localPeer, 'ProtocolVersion')
expect(agentVersion).to.eql(unit8ArrayToString(storedAgentVersion))
expect(storedProtocolVersion).to.exist()
await service.stop()
})
describe('push', () => {
it('should be able to push identify updates to another peer', async () => {
const storedProtocols = [multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0']
const storedProtocols = [multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0'].sort()
const connectionManager = new EventEmitter()
connectionManager.getConnection = () => { }
const localPeerStore = new PeerStore({ peerId: localPeer })
localPeerStore.protoBook.set(localPeer, storedProtocols)
const localPeerStore = new PeerStore({
peerId: localPeer,
datastore: new MemoryDatastore()
})
await localPeerStore.protoBook.set(localPeer, storedProtocols)
const localIdentify = new IdentifyService({
libp2p: {
@ -273,8 +288,11 @@ describe('Identify', () => {
}
})
const remotePeerStore = new PeerStore({ peerId: remotePeer })
remotePeerStore.protoBook.set(remotePeer, storedProtocols)
const remotePeerStore = new PeerStore({
peerId: remotePeer,
datastore: new MemoryDatastore()
})
await remotePeerStore.protoBook.set(remotePeer, storedProtocols)
const remoteIdentify = new IdentifyService({
libp2p: {
@ -316,7 +334,7 @@ describe('Identify', () => {
expect(remoteIdentify.peerStore.addressBook.consumePeerRecord.callCount).to.equal(2)
expect(remoteIdentify.peerStore.protoBook.set.callCount).to.equal(1)
const addresses = localIdentify.peerStore.addressBook.get(localPeer)
const addresses = await localIdentify.peerStore.addressBook.get(localPeer)
expect(addresses).to.exist()
expect(addresses).have.lengthOf(listenMaddrs.length)
expect(addresses.map((a) => a.multiaddr)).to.eql(listenMaddrs)
@ -328,12 +346,15 @@ describe('Identify', () => {
// LEGACY
it('should be able to push identify updates to another peer with no certified peer records support', async () => {
const storedProtocols = [multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0']
const storedProtocols = [multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0'].sort()
const connectionManager = new EventEmitter()
connectionManager.getConnection = () => { }
const localPeerStore = new PeerStore({ peerId: localPeer })
localPeerStore.protoBook.set(localPeer, storedProtocols)
const localPeerStore = new PeerStore({
peerId: localPeer,
datastore: new MemoryDatastore()
})
await localPeerStore.protoBook.set(localPeer, storedProtocols)
const localIdentify = new IdentifyService({
libp2p: {
@ -347,14 +368,17 @@ describe('Identify', () => {
}
})
const remotePeerStore = new PeerStore({ peerId: remotePeer })
const remotePeerStore = new PeerStore({
peerId: remotePeer,
datastore: new MemoryDatastore()
})
remotePeerStore.protoBook.set(remotePeer, storedProtocols)
const remoteIdentify = new IdentifyService({
libp2p: {
peerId: remotePeer,
connectionManager,
peerStore: new PeerStore({ peerId: remotePeer }),
peerStore: remotePeerStore,
multiaddrs: [],
_options: { host: {} },
_config: { protocolPrefix: 'ipfs' },
@ -492,11 +516,15 @@ describe('Identify', () => {
await libp2p.identifyService.identify.firstCall.returnValue
sinon.stub(libp2p, 'isStarted').returns(true)
libp2p.handle('/echo/2.0.0', () => {})
libp2p.unhandle('/echo/2.0.0')
await libp2p.handle('/echo/2.0.0', () => {})
await libp2p.unhandle('/echo/2.0.0')
// the protocol change event listener in the identity service is async
await pWaitFor(() => libp2p.identifyService.push.callCount === 2)
// Verify the remote peer is notified of both changes
expect(libp2p.identifyService.push.callCount).to.equal(2)
for (const call of libp2p.identifyService.push.getCalls()) {
const [connections] = call.args
expect(connections.length).to.equal(1)
@ -509,7 +537,7 @@ describe('Identify', () => {
await pWaitFor(() => connection.streams.length === 0)
})
it('should store host data and protocol version into metadataBook', () => {
it('should store host data and protocol version into metadataBook', async () => {
const agentVersion = 'js-project/1.0.0'
libp2p = new Libp2p({
@ -519,9 +547,10 @@ describe('Identify', () => {
agentVersion
}
})
await libp2p.start()
const storedAgentVersion = libp2p.peerStore.metadataBook.getValue(localPeer, 'AgentVersion')
const storedProtocolVersion = libp2p.peerStore.metadataBook.getValue(localPeer, 'ProtocolVersion')
const storedAgentVersion = await libp2p.peerStore.metadataBook.getValue(localPeer, 'AgentVersion')
const storedProtocolVersion = await libp2p.peerStore.metadataBook.getValue(localPeer, 'ProtocolVersion')
expect(agentVersion).to.eql(unit8ArrayToString(storedAgentVersion))
expect(storedProtocolVersion).to.exist()
@ -545,7 +574,10 @@ describe('Identify', () => {
await libp2p.identifyService.identify.firstCall.returnValue
sinon.stub(libp2p, 'isStarted').returns(true)
libp2p.peerStore.addressBook.add(libp2p.peerId, [new Multiaddr('/ip4/180.0.0.1/tcp/15001/ws')])
await libp2p.peerStore.addressBook.add(libp2p.peerId, [new Multiaddr('/ip4/180.0.0.1/tcp/15001/ws')])
// the protocol change event listener in the identity service is async
await pWaitFor(() => libp2p.identifyService.push.callCount === 1)
// Verify the remote peer is notified of change
expect(libp2p.identifyService.push.callCount).to.equal(1)

View File

@ -266,10 +266,10 @@ describe('Metrics', () => {
const metric = 'some-metric'
const value = 1
metrics.updateComponentMetric(component, metric, value)
metrics.updateComponentMetric({ component, metric, value })
expect(metrics.getComponentMetrics()).to.have.lengthOf(1)
expect(metrics.getComponentMetrics().get(component)).to.have.lengthOf(1)
expect(metrics.getComponentMetrics().get(component).get(metric)).to.equal(value)
expect(metrics.getComponentMetrics().get('libp2p').get(component)).to.have.lengthOf(1)
expect(metrics.getComponentMetrics().get('libp2p').get(component).get(metric)).to.equal(value)
})
})

View File

@ -658,10 +658,8 @@ describe('peer-routing', () => {
await node.start()
await delay(300)
expect(node._dht.getClosestPeers.callCount).to.eql(1)
await delay(500)
expect(node._dht.getClosestPeers.callCount).to.eql(2)
// should run more than once
await pWaitFor(() => node._dht.getClosestPeers.callCount === 2)
})
})
})

View File

@ -9,7 +9,7 @@ const arrayEquals = require('libp2p-utils/src/array-equals')
const addressSort = require('libp2p-utils/src/address-sort')
const PeerId = require('peer-id')
const pDefer = require('p-defer')
const { MemoryDatastore } = require('datastore-core/memory')
const PeerStore = require('../../src/peer-store')
const Envelope = require('../../src/record/envelope')
const PeerRecord = require('../../src/record/peer-record')
@ -19,6 +19,11 @@ const {
codes: { ERR_INVALID_PARAMETERS }
} = require('../../src/errors')
/**
* @typedef {import('../../src/peer-store/types').PeerStore} PeerStore
* @typedef {import('../../src/peer-store/types').AddressBook} AddressBook
*/
const addr1 = new Multiaddr('/ip4/127.0.0.1/tcp/8000')
const addr2 = new Multiaddr('/ip4/20.0.0.1/tcp/8001')
const addr3 = new Multiaddr('/ip4/127.0.0.1/tcp/8002')
@ -31,10 +36,16 @@ describe('addressBook', () => {
})
describe('addressBook.set', () => {
let peerStore, ab
/** @type {PeerStore} */
let peerStore
/** @type {AddressBook} */
let ab
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
ab = peerStore.addressBook
})
@ -42,9 +53,9 @@ describe('addressBook', () => {
peerStore.removeAllListeners()
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
it('throws invalid parameters error if invalid PeerId is provided', async () => {
try {
ab.set('invalid peerId')
await ab.set('invalid peerId')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -52,9 +63,9 @@ describe('addressBook', () => {
throw new Error('invalid peerId should throw error')
})
it('throwns invalid parameters error if no addresses provided', () => {
it('throws invalid parameters error if no addresses provided', async () => {
try {
ab.set(peerId)
await ab.set(peerId)
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -62,9 +73,9 @@ describe('addressBook', () => {
throw new Error('no addresses should throw error')
})
it('throwns invalid parameters error if invalid multiaddrs are provided', () => {
it('throws invalid parameters error if invalid multiaddrs are provided', async () => {
try {
ab.set(peerId, ['invalid multiaddr'])
await ab.set(peerId, ['invalid multiaddr'])
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -72,7 +83,7 @@ describe('addressBook', () => {
throw new Error('invalid multiaddrs should throw error')
})
it('replaces the stored content by default and emit change event', () => {
it('replaces the stored content by default and emit change event', async () => {
const defer = pDefer()
const supportedMultiaddrs = [addr1, addr2]
@ -82,8 +93,8 @@ describe('addressBook', () => {
defer.resolve()
})
ab.set(peerId, supportedMultiaddrs)
const addresses = ab.get(peerId)
await ab.set(peerId, supportedMultiaddrs)
const addresses = await ab.get(peerId)
const multiaddrs = addresses.map((mi) => mi.multiaddr)
expect(multiaddrs).to.have.deep.members(supportedMultiaddrs)
@ -105,11 +116,11 @@ describe('addressBook', () => {
})
// set 1
ab.set(peerId, supportedMultiaddrsA)
await ab.set(peerId, supportedMultiaddrsA)
// set 2 (same content)
ab.set(peerId, supportedMultiaddrsB)
const addresses = ab.get(peerId)
await ab.set(peerId, supportedMultiaddrsB)
const addresses = await ab.get(peerId)
const multiaddrs = addresses.map((mi) => mi.multiaddr)
expect(multiaddrs).to.have.deep.members(supportedMultiaddrsB)
@ -130,10 +141,10 @@ describe('addressBook', () => {
})
// set 1
ab.set(peerId, supportedMultiaddrs)
await ab.set(peerId, supportedMultiaddrs)
// set 2 (same content)
ab.set(peerId, supportedMultiaddrs)
await ab.set(peerId, supportedMultiaddrs)
// Wait 50ms for incorrect second event
setTimeout(() => {
@ -145,10 +156,16 @@ describe('addressBook', () => {
})
describe('addressBook.add', () => {
let peerStore, ab
/** @type {PeerStore} */
let peerStore
/** @type {AddressBook} */
let ab
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
ab = peerStore.addressBook
})
@ -156,9 +173,9 @@ describe('addressBook', () => {
peerStore.removeAllListeners()
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
it('throws invalid parameters error if invalid PeerId is provided', async () => {
try {
ab.add('invalid peerId')
await ab.add('invalid peerId')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -166,9 +183,9 @@ describe('addressBook', () => {
throw new Error('invalid peerId should throw error')
})
it('throwns invalid parameters error if no addresses provided', () => {
it('throws invalid parameters error if no addresses provided', async () => {
try {
ab.add(peerId)
await ab.add(peerId)
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -176,9 +193,9 @@ describe('addressBook', () => {
throw new Error('no addresses provided should throw error')
})
it('throwns invalid parameters error if invalid multiaddrs are provided', () => {
it('throws invalid parameters error if invalid multiaddrs are provided', async () => {
try {
ab.add(peerId, ['invalid multiaddr'])
await ab.add(peerId, ['invalid multiaddr'])
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -193,7 +210,7 @@ describe('addressBook', () => {
defer.reject()
})
ab.add(peerId, [])
await ab.add(peerId, [])
// Wait 50ms for incorrect second event
setTimeout(() => {
@ -203,7 +220,7 @@ describe('addressBook', () => {
await defer.promise
})
it('adds the new content and emits change event', () => {
it('adds the new content and emits change event', async () => {
const defer = pDefer()
const supportedMultiaddrsA = [addr1, addr2]
@ -219,14 +236,14 @@ describe('addressBook', () => {
})
// Replace
ab.set(peerId, supportedMultiaddrsA)
let addresses = ab.get(peerId)
await ab.set(peerId, supportedMultiaddrsA)
let addresses = await ab.get(peerId)
let multiaddrs = addresses.map((mi) => mi.multiaddr)
expect(multiaddrs).to.have.deep.members(supportedMultiaddrsA)
// Add
ab.add(peerId, supportedMultiaddrsB)
addresses = ab.get(peerId)
await ab.add(peerId, supportedMultiaddrsB)
addresses = await ab.get(peerId)
multiaddrs = addresses.map((mi) => mi.multiaddr)
expect(multiaddrs).to.have.deep.members(finalMultiaddrs)
@ -249,11 +266,11 @@ describe('addressBook', () => {
})
// set 1
ab.set(peerId, supportedMultiaddrsA)
await ab.set(peerId, supportedMultiaddrsA)
// set 2 (content already existing)
ab.add(peerId, supportedMultiaddrsB)
const addresses = ab.get(peerId)
await ab.add(peerId, supportedMultiaddrsB)
const addresses = await ab.get(peerId)
const multiaddrs = addresses.map((mi) => mi.multiaddr)
expect(multiaddrs).to.have.deep.members(finalMultiaddrs)
@ -275,10 +292,10 @@ describe('addressBook', () => {
})
// set 1
ab.set(peerId, supportedMultiaddrsA)
await ab.set(peerId, supportedMultiaddrsA)
// set 2 (content already existing)
ab.add(peerId, supportedMultiaddrsB)
await ab.add(peerId, supportedMultiaddrsB)
// Wait 50ms for incorrect second event
setTimeout(() => {
@ -288,26 +305,32 @@ describe('addressBook', () => {
await defer.promise
})
it('does not add replicated content', () => {
it('does not add replicated content', async () => {
// set 1
ab.set(peerId, [addr1, addr1])
await ab.set(peerId, [addr1, addr1])
const addresses = ab.get(peerId)
const addresses = await ab.get(peerId)
expect(addresses).to.have.lengthOf(1)
})
})
describe('addressBook.get', () => {
let peerStore, ab
/** @type {PeerStore} */
let peerStore
/** @type {AddressBook} */
let ab
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
ab = peerStore.addressBook
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
it('throws invalid parameters error if invalid PeerId is provided', async () => {
try {
ab.get('invalid peerId')
await ab.get('invalid peerId')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -315,34 +338,40 @@ describe('addressBook', () => {
throw new Error('invalid peerId should throw error')
})
it('returns undefined if no multiaddrs are known for the provided peer', () => {
const addresses = ab.get(peerId)
it('returns empty if no multiaddrs are known for the provided peer', async () => {
const addresses = await ab.get(peerId)
expect(addresses).to.not.exist()
expect(addresses).to.be.empty()
})
it('returns the multiaddrs stored', () => {
it('returns the multiaddrs stored', async () => {
const supportedMultiaddrs = [addr1, addr2]
ab.set(peerId, supportedMultiaddrs)
await ab.set(peerId, supportedMultiaddrs)
const addresses = ab.get(peerId)
const addresses = await ab.get(peerId)
const multiaddrs = addresses.map((mi) => mi.multiaddr)
expect(multiaddrs).to.have.deep.members(supportedMultiaddrs)
})
})
describe('addressBook.getMultiaddrsForPeer', () => {
let peerStore, ab
/** @type {PeerStore} */
let peerStore
/** @type {AddressBook} */
let ab
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
ab = peerStore.addressBook
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
it('throws invalid parameters error if invalid PeerId is provided', async () => {
try {
ab.getMultiaddrsForPeer('invalid peerId')
await ab.getMultiaddrsForPeer('invalid peerId')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -350,28 +379,28 @@ describe('addressBook', () => {
throw new Error('invalid peerId should throw error')
})
it('returns undefined if no multiaddrs are known for the provided peer', () => {
const addresses = ab.getMultiaddrsForPeer(peerId)
it('returns empty if no multiaddrs are known for the provided peer', async () => {
const addresses = await ab.getMultiaddrsForPeer(peerId)
expect(addresses).to.not.exist()
expect(addresses).to.be.empty()
})
it('returns the multiaddrs stored', () => {
it('returns the multiaddrs stored', async () => {
const supportedMultiaddrs = [addr1, addr2]
ab.set(peerId, supportedMultiaddrs)
await ab.set(peerId, supportedMultiaddrs)
const multiaddrs = ab.getMultiaddrsForPeer(peerId)
const multiaddrs = await ab.getMultiaddrsForPeer(peerId)
multiaddrs.forEach((m) => {
expect(m.getPeerId()).to.equal(peerId.toB58String())
})
})
it('can sort multiaddrs providing a sorter', () => {
it('can sort multiaddrs providing a sorter', async () => {
const supportedMultiaddrs = [addr1, addr2]
ab.set(peerId, supportedMultiaddrs)
await ab.set(peerId, supportedMultiaddrs)
const multiaddrs = ab.getMultiaddrsForPeer(peerId, addressSort.publicAddressesFirst)
const multiaddrs = await ab.getMultiaddrsForPeer(peerId, addressSort.publicAddressesFirst)
const sortedAddresses = addressSort.publicAddressesFirst(supportedMultiaddrs.map((m) => ({ multiaddr: m })))
multiaddrs.forEach((m, index) => {
@ -381,16 +410,22 @@ describe('addressBook', () => {
})
describe('addressBook.delete', () => {
let peerStore, ab
/** @type {PeerStore} */
let peerStore
/** @type {AddressBook} */
let ab
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
ab = peerStore.addressBook
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
it('throws invalid parameters error if invalid PeerId is provided', async () => {
try {
ab.delete('invalid peerId')
await ab.delete('invalid peerId')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -398,16 +433,14 @@ describe('addressBook', () => {
throw new Error('invalid peerId should throw error')
})
it('returns false if no records exist for the peer and no event is emitted', () => {
it('does not emit an event if no records exist for the peer', async () => {
const defer = pDefer()
peerStore.on('change:multiaddrs', () => {
defer.reject()
})
const deleted = ab.delete(peerId)
expect(deleted).to.equal(false)
await ab.delete(peerId)
// Wait 50ms for incorrect invalid event
setTimeout(() => {
@ -417,11 +450,11 @@ describe('addressBook', () => {
return defer.promise
})
it('returns true if the record exists and an event is emitted', () => {
it('emits an event if the record exists', async () => {
const defer = pDefer()
const supportedMultiaddrs = [addr1, addr2]
ab.set(peerId, supportedMultiaddrs)
await ab.set(peerId, supportedMultiaddrs)
// Listen after set
peerStore.on('change:multiaddrs', ({ multiaddrs }) => {
@ -429,20 +462,24 @@ describe('addressBook', () => {
defer.resolve()
})
const deleted = ab.delete(peerId)
expect(deleted).to.equal(true)
await ab.delete(peerId)
return defer.promise
})
})
describe('certified records', () => {
let peerStore, ab
/** @type {PeerStore} */
let peerStore
/** @type {AddressBook} */
let ab
describe('consumes a valid peer record and stores its data', () => {
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
ab = peerStore.addressBook
})
@ -455,15 +492,11 @@ describe('addressBook', () => {
const envelope = await Envelope.seal(peerRecord, peerId)
// consume peer record
const consumed = ab.consumePeerRecord(envelope)
const consumed = await ab.consumePeerRecord(envelope)
expect(consumed).to.eql(true)
// Validate stored envelope
const storedEnvelope = await ab.getPeerRecord(peerId)
expect(envelope.equals(storedEnvelope)).to.eql(true)
// Validate AddressBook addresses
const addrs = ab.get(peerId)
const addrs = await ab.get(peerId)
expect(addrs).to.exist()
expect(addrs).to.have.lengthOf(multiaddrs.length)
addrs.forEach((addr, index) => {
@ -488,7 +521,7 @@ describe('addressBook', () => {
})
// consume peer record
const consumed = ab.consumePeerRecord(envelope)
const consumed = await ab.consumePeerRecord(envelope)
expect(consumed).to.eql(true)
return defer.promise
@ -499,10 +532,10 @@ describe('addressBook', () => {
const multiaddrs = [addr1, addr2]
// Set addressBook data
ab.set(peerId, multiaddrs)
await ab.set(peerId, multiaddrs)
// Validate data exists, but not certified
let addrs = ab.get(peerId)
let addrs = await ab.get(peerId)
expect(addrs).to.exist()
expect(addrs).to.have.lengthOf(multiaddrs.length)
@ -525,14 +558,14 @@ describe('addressBook', () => {
})
// consume peer record
const consumed = ab.consumePeerRecord(envelope)
const consumed = await ab.consumePeerRecord(envelope)
expect(consumed).to.eql(true)
// Wait event
await defer.promise
// Validate data exists and certified
addrs = ab.get(peerId)
addrs = await ab.get(peerId)
expect(addrs).to.exist()
expect(addrs).to.have.lengthOf(multiaddrs.length)
addrs.forEach((addr, index) => {
@ -546,10 +579,10 @@ describe('addressBook', () => {
const multiaddrs = [addr1, addr2]
// Set addressBook data
ab.set(peerId, [addr1])
await ab.set(peerId, [addr1])
// Validate data exists, but not certified
let addrs = ab.get(peerId)
let addrs = await ab.get(peerId)
expect(addrs).to.exist()
expect(addrs).to.have.lengthOf(1)
expect(addrs[0].isCertified).to.eql(false)
@ -569,14 +602,14 @@ describe('addressBook', () => {
})
// consume peer record
const consumed = ab.consumePeerRecord(envelope)
const consumed = await ab.consumePeerRecord(envelope)
expect(consumed).to.eql(true)
// Wait event
await defer.promise
// Validate data exists and certified
addrs = ab.get(peerId)
addrs = await ab.get(peerId)
expect(addrs).to.exist()
expect(addrs).to.have.lengthOf(multiaddrs.length)
addrs.forEach((addr, index) => {
@ -591,10 +624,10 @@ describe('addressBook', () => {
const multiaddrsCertified = [addr1, addr2]
// Set addressBook data
ab.set(peerId, multiaddrsUncertified)
await ab.set(peerId, multiaddrsUncertified)
// Validate data exists, but not certified
let addrs = ab.get(peerId)
let addrs = await ab.get(peerId)
expect(addrs).to.exist()
expect(addrs).to.have.lengthOf(multiaddrsUncertified.length)
addrs.forEach((addr, index) => {
@ -616,14 +649,14 @@ describe('addressBook', () => {
})
// consume peer record
const consumed = ab.consumePeerRecord(envelope)
const consumed = await ab.consumePeerRecord(envelope)
expect(consumed).to.eql(true)
// Wait event
await defer.promise
// Validate data exists and certified
addrs = ab.get(peerId)
addrs = await ab.get(peerId)
expect(addrs).to.exist()
expect(addrs).to.have.lengthOf(multiaddrsCertified.length)
addrs.forEach((addr, index) => {
@ -635,16 +668,19 @@ describe('addressBook', () => {
describe('fails to consume invalid peer records', () => {
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
ab = peerStore.addressBook
})
it('invalid peer record', () => {
it('invalid peer record', async () => {
const invalidEnvelope = {
payload: Buffer.from('invalid-peerRecord')
}
const consumed = ab.consumePeerRecord(invalidEnvelope)
const consumed = await ab.consumePeerRecord(invalidEnvelope)
expect(consumed).to.eql(false)
})
@ -659,7 +695,7 @@ describe('addressBook', () => {
})
const envelope = await Envelope.seal(peerRecord, peerId)
const consumed = ab.consumePeerRecord(envelope)
const consumed = await ab.consumePeerRecord(envelope)
expect(consumed).to.eql(false)
})
@ -679,10 +715,10 @@ describe('addressBook', () => {
const envelope2 = await Envelope.seal(peerRecord2, peerId)
// Consume envelope1 (bigger seqNumber)
let consumed = ab.consumePeerRecord(envelope1)
let consumed = await ab.consumePeerRecord(envelope1)
expect(consumed).to.eql(true)
consumed = ab.consumePeerRecord(envelope2)
consumed = await ab.consumePeerRecord(envelope2)
expect(consumed).to.eql(false)
})
@ -693,7 +729,7 @@ describe('addressBook', () => {
})
const envelope = await Envelope.seal(peerRecord, peerId)
const consumed = ab.consumePeerRecord(envelope)
const consumed = await ab.consumePeerRecord(envelope)
expect(consumed).to.eql(false)
})
})

View File

@ -3,26 +3,43 @@
const { expect } = require('aegir/utils/chai')
const sinon = require('sinon')
const { MemoryDatastore } = require('datastore-core/memory')
const PeerStore = require('../../src/peer-store')
const pDefer = require('p-defer')
const peerUtils = require('../utils/creators/peer')
const {
codes: { ERR_INVALID_PARAMETERS }
} = require('../../src/errors')
/**
* @typedef {import('../../src/peer-store/types').PeerStore} PeerStore
* @typedef {import('../../src/peer-store/types').KeyBook} KeyBook
* @typedef {import('peer-id')} PeerId
*/
describe('keyBook', () => {
let peerId, peerStore, kb
/** @type {PeerId} */
let peerId
/** @type {PeerStore} */
let peerStore
/** @type {KeyBook} */
let kb
/** @type {MemoryDatastore} */
let datastore
beforeEach(async () => {
[peerId] = await peerUtils.createPeerId()
peerStore = new PeerStore({ peerId })
datastore = new MemoryDatastore()
peerStore = new PeerStore({
peerId,
datastore
})
kb = peerStore.keyBook
})
it('throws invalid parameters error if invalid PeerId is provided in set', () => {
it('throws invalid parameters error if invalid PeerId is provided in set', async () => {
try {
kb.set('invalid peerId')
await kb.set('invalid peerId')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -30,9 +47,9 @@ describe('keyBook', () => {
throw new Error('invalid peerId should throw error')
})
it('throws invalid parameters error if invalid PeerId is provided in get', () => {
it('throws invalid parameters error if invalid PeerId is provided in get', async () => {
try {
kb.get('invalid peerId')
await kb.get('invalid peerId')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -40,22 +57,58 @@ describe('keyBook', () => {
throw new Error('invalid peerId should throw error')
})
it('stores the peerId in the book and returns the public key', () => {
it('stores the peerId in the book and returns the public key', async () => {
// Set PeerId
kb.set(peerId, peerId.pubKey)
await kb.set(peerId, peerId.pubKey)
// Get public key
const pubKey = kb.get(peerId)
const pubKey = await kb.get(peerId)
expect(peerId.pubKey.bytes).to.equalBytes(pubKey.bytes)
})
it('should not store if already stored', () => {
const spy = sinon.spy(kb, '_setData')
it('should not store if already stored', async () => {
const spy = sinon.spy(datastore, 'put')
// Set PeerId
kb.set(peerId, peerId.pubKey)
kb.set(peerId, peerId.pubKey)
await kb.set(peerId, peerId.pubKey)
await kb.set(peerId, peerId.pubKey)
expect(spy).to.have.property('callCount', 1)
})
it('should emit an event when setting a key', async () => {
const defer = pDefer()
peerStore.on('change:pubkey', ({ peerId: id, pubKey }) => {
expect(id.toB58String()).to.equal(peerId.toB58String())
expect(pubKey.bytes).to.equalBytes(peerId.pubKey.bytes)
defer.resolve()
})
// Set PeerId
await kb.set(peerId, peerId.pubKey)
await defer.promise
})
it('should not set when key does not match', async () => {
const [edKey] = await peerUtils.createPeerId({ fixture: false, opts: { keyType: 'Ed25519' } })
// Set PeerId
await expect(kb.set(edKey, peerId.pubKey)).to.eventually.be.rejectedWith(/bytes do not match/)
})
it('should emit an event when deleting a key', async () => {
const defer = pDefer()
await kb.set(peerId, peerId.pubKey)
peerStore.on('change:pubkey', ({ peerId: id, pubKey }) => {
expect(id.toB58String()).to.equal(peerId.toB58String())
expect(pubKey).to.be.undefined()
defer.resolve()
})
await kb.delete(peerId)
await defer.promise
})
})

View File

@ -3,7 +3,7 @@
const { expect } = require('aegir/utils/chai')
const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string')
const { MemoryDatastore } = require('datastore-core/memory')
const pDefer = require('p-defer')
const PeerStore = require('../../src/peer-store')
@ -12,7 +12,14 @@ const {
codes: { ERR_INVALID_PARAMETERS }
} = require('../../src/errors')
/**
* @typedef {import('../../src/peer-store/types').PeerStore} PeerStore
* @typedef {import('../../src/peer-store/types').MetadataBook} MetadataBook
* @typedef {import('peer-id')} PeerId
*/
describe('metadataBook', () => {
/** @type {PeerId} */
let peerId
before(async () => {
@ -20,10 +27,16 @@ describe('metadataBook', () => {
})
describe('metadataBook.set', () => {
let peerStore, mb
/** @type {PeerStore} */
let peerStore
/** @type {MetadataBook} */
let mb
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
mb = peerStore.metadataBook
})
@ -31,9 +44,9 @@ describe('metadataBook', () => {
peerStore.removeAllListeners()
})
it('throws invalid parameters error if invalid PeerId is provided', () => {
it('throws invalid parameters error if invalid PeerId is provided', async () => {
try {
mb.set('invalid peerId')
await mb.set('invalid peerId')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -41,9 +54,9 @@ describe('metadataBook', () => {
throw new Error('invalid peerId should throw error')
})
it('throws invalid parameters error if no key provided', () => {
it('throws invalid parameters error if no metadata provided', async () => {
try {
mb.set(peerId)
await mb.set(peerId)
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -51,9 +64,9 @@ describe('metadataBook', () => {
throw new Error('no key provided should throw error')
})
it('throws invalid parameters error if no value provided', () => {
it('throws invalid parameters error if no value provided', async () => {
try {
mb.set(peerId, 'location')
await mb.setValue(peerId, 'location')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -61,9 +74,9 @@ describe('metadataBook', () => {
throw new Error('no value provided should throw error')
})
it('throws invalid parameters error if value is not a buffer', () => {
it('throws invalid parameters error if value is not a buffer', async () => {
try {
mb.set(peerId, 'location', 'mars')
await mb.setValue(peerId, 'location', 'mars')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -71,30 +84,30 @@ describe('metadataBook', () => {
throw new Error('invalid value provided should throw error')
})
it('stores the content and emit change event', () => {
it('stores the content and emit change event', async () => {
const defer = pDefer()
const metadataKey = 'location'
const metadataValue = uint8ArrayFromString('mars')
peerStore.once('change:metadata', ({ peerId, metadata }) => {
expect(peerId).to.exist()
expect(metadata).to.equal(metadataKey)
expect(metadata.get(metadataKey)).to.equalBytes(metadataValue)
defer.resolve()
})
mb.set(peerId, metadataKey, metadataValue)
await mb.setValue(peerId, metadataKey, metadataValue)
const value = mb.getValue(peerId, metadataKey)
const value = await mb.getValue(peerId, metadataKey)
expect(value).to.equalBytes(metadataValue)
const peerMetadata = mb.get(peerId)
const peerMetadata = await mb.get(peerId)
expect(peerMetadata).to.exist()
expect(peerMetadata.get(metadataKey)).to.equalBytes(metadataValue)
return defer.promise
})
it('emits on set if not storing the exact same content', () => {
it('emits on set if not storing the exact same content', async () => {
const defer = pDefer()
const metadataKey = 'location'
const metadataValue1 = uint8ArrayFromString('mars')
@ -109,22 +122,22 @@ describe('metadataBook', () => {
})
// set 1
mb.set(peerId, metadataKey, metadataValue1)
await mb.setValue(peerId, metadataKey, metadataValue1)
// set 2 (same content)
mb.set(peerId, metadataKey, metadataValue2)
await mb.setValue(peerId, metadataKey, metadataValue2)
const value = mb.getValue(peerId, metadataKey)
const value = await mb.getValue(peerId, metadataKey)
expect(value).to.equalBytes(metadataValue2)
const peerMetadata = mb.get(peerId)
const peerMetadata = await mb.get(peerId)
expect(peerMetadata).to.exist()
expect(peerMetadata.get(metadataKey)).to.equalBytes(metadataValue2)
return defer.promise
})
it('does not emit on set if it is storing the exact same content', () => {
it('does not emit on set if it is storing the exact same content', async () => {
const defer = pDefer()
const metadataKey = 'location'
const metadataValue = uint8ArrayFromString('mars')
@ -138,10 +151,10 @@ describe('metadataBook', () => {
})
// set 1
mb.set(peerId, metadataKey, metadataValue)
await mb.setValue(peerId, metadataKey, metadataValue)
// set 2 (same content)
mb.set(peerId, metadataKey, metadataValue)
await mb.setValue(peerId, metadataKey, metadataValue)
// Wait 50ms for incorrect second event
setTimeout(() => {
@ -153,16 +166,22 @@ describe('metadataBook', () => {
})
describe('metadataBook.get', () => {
let peerStore, mb
/** @type {PeerStore} */
let peerStore
/** @type {MetadataBook} */
let mb
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
mb = peerStore.metadataBook
})
it('throws invalid parameters error if invalid PeerId is provided', () => {
it('throws invalid parameters error if invalid PeerId is provided', async () => {
try {
mb.get('invalid peerId')
await mb.get('invalid peerId')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -170,35 +189,43 @@ describe('metadataBook', () => {
throw new Error('invalid peerId should throw error')
})
it('returns undefined if no metadata is known for the provided peer', () => {
const metadata = mb.get(peerId)
it('returns empty if no metadata is known for the provided peer', async () => {
const metadata = await mb.get(peerId)
expect(metadata).to.not.exist()
expect(metadata).to.be.empty()
})
it('returns the metadata stored', () => {
it('returns the metadata stored', async () => {
const metadataKey = 'location'
const metadataValue = uint8ArrayFromString('mars')
const metadata = new Map()
metadata.set(metadataKey, metadataValue)
mb.set(peerId, metadataKey, metadataValue)
await mb.set(peerId, metadata)
const peerMetadata = mb.get(peerId)
const peerMetadata = await mb.get(peerId)
expect(peerMetadata).to.exist()
expect(peerMetadata.get(metadataKey)).to.equalBytes(metadataValue)
})
})
describe('metadataBook.getValue', () => {
let peerStore, mb
/** @type {PeerStore} */
let peerStore
/** @type {MetadataBook} */
let mb
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
mb = peerStore.metadataBook
})
it('throws invalid parameters error if invalid PeerId is provided', () => {
it('throws invalid parameters error if invalid PeerId is provided', async () => {
try {
mb.getValue('invalid peerId')
await mb.getValue('invalid peerId')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -206,48 +233,53 @@ describe('metadataBook', () => {
throw new Error('invalid peerId should throw error')
})
it('returns undefined if no metadata is known for the provided peer', () => {
it('returns undefined if no metadata is known for the provided peer', async () => {
const metadataKey = 'location'
const metadata = mb.getValue(peerId, metadataKey)
const metadata = await mb.getValue(peerId, metadataKey)
expect(metadata).to.not.exist()
})
it('returns the metadata value stored for the given key', () => {
it('returns the metadata value stored for the given key', async () => {
const metadataKey = 'location'
const metadataValue = uint8ArrayFromString('mars')
mb.set(peerId, metadataKey, metadataValue)
await mb.setValue(peerId, metadataKey, metadataValue)
const value = mb.getValue(peerId, metadataKey)
const value = await mb.getValue(peerId, metadataKey)
expect(value).to.exist()
expect(value).to.equalBytes(metadataValue)
})
it('returns undefined if no metadata is known for the provided peer and key', () => {
it('returns undefined if no metadata is known for the provided peer and key', async () => {
const metadataKey = 'location'
const metadataBadKey = 'nickname'
const metadataValue = uint8ArrayFromString('mars')
mb.set(peerId, metadataKey, metadataValue)
const metadata = mb.getValue(peerId, metadataBadKey)
await mb.setValue(peerId, metadataKey, metadataValue)
const metadata = await mb.getValue(peerId, metadataBadKey)
expect(metadata).to.not.exist()
})
})
describe('metadataBook.delete', () => {
let peerStore, mb
/** @type {PeerStore} */
let peerStore
/** @type {MetadataBook} */
let mb
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
mb = peerStore.metadataBook
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
it('throws invalid parameters error if invalid PeerId is provided', async () => {
try {
mb.delete('invalid peerId')
await mb.delete('invalid peerId')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -255,16 +287,14 @@ describe('metadataBook', () => {
throw new Error('invalid peerId should throw error')
})
it('returns false if no records exist for the peer and no event is emitted', () => {
it('should not emit event if no records exist for the peer', async () => {
const defer = pDefer()
peerStore.on('change:metadata', () => {
defer.reject()
})
const deleted = mb.delete(peerId)
expect(deleted).to.equal(false)
await mb.delete(peerId)
// Wait 50ms for incorrect invalid event
setTimeout(() => {
@ -274,37 +304,41 @@ describe('metadataBook', () => {
return defer.promise
})
it('returns true if the record exists and an event is emitted', () => {
it('should emit an event if the record exists for the peer', async () => {
const defer = pDefer()
const metadataKey = 'location'
const metadataValue = uint8ArrayFromString('mars')
mb.set(peerId, metadataKey, metadataValue)
await mb.setValue(peerId, metadataKey, metadataValue)
// Listen after set
peerStore.on('change:metadata', () => {
defer.resolve()
})
const deleted = mb.delete(peerId)
expect(deleted).to.equal(true)
await mb.delete(peerId)
return defer.promise
})
})
describe('metadataBook.deleteValue', () => {
let peerStore, mb
/** @type {PeerStore} */
let peerStore
/** @type {MetadataBook} */
let mb
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
mb = peerStore.metadataBook
})
it('throws invalid parameters error if invalid PeerId is provided', () => {
it('throws invalid parameters error if invalid PeerId is provided', async () => {
try {
mb.deleteValue('invalid peerId')
await mb.deleteValue('invalid peerId')
} catch (/** @type {any} */ err) {
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
return
@ -312,7 +346,7 @@ describe('metadataBook', () => {
throw new Error('invalid peerId should throw error')
})
it('returns false if no records exist for the peer and no event is emitted', () => {
it('should not emit event if no records exist for the peer', async () => {
const defer = pDefer()
const metadataKey = 'location'
@ -320,9 +354,7 @@ describe('metadataBook', () => {
defer.reject()
})
const deleted = mb.deleteValue(peerId, metadataKey)
expect(deleted).to.equal(false)
await mb.deleteValue(peerId, metadataKey)
// Wait 50ms for incorrect invalid event
setTimeout(() => {
@ -332,45 +364,19 @@ describe('metadataBook', () => {
return defer.promise
})
it('returns true if the record exists and an event is emitted', () => {
it('should emit event if a record exists for the peer', async () => {
const defer = pDefer()
const metadataKey = 'location'
const metadataValue = uint8ArrayFromString('mars')
mb.set(peerId, metadataKey, metadataValue)
await mb.setValue(peerId, metadataKey, metadataValue)
// Listen after set
peerStore.on('change:metadata', () => {
defer.resolve()
})
const deleted = mb.deleteValue(peerId, metadataKey)
expect(deleted).to.equal(true)
return defer.promise
})
it('returns false if there is a record for the peer but not the given metadata key', () => {
const defer = pDefer()
const metadataKey = 'location'
const metadataBadKey = 'nickname'
const metadataValue = uint8ArrayFromString('mars')
mb.set(peerId, metadataKey, metadataValue)
peerStore.on('change:metadata', () => {
defer.reject()
})
const deleted = mb.deleteValue(peerId, metadataBadKey)
expect(deleted).to.equal(false)
// Wait 50ms for incorrect invalid event
setTimeout(() => {
defer.resolve()
}, 50)
await mb.deleteValue(peerId, metadataKey)
return defer.promise
})

View File

@ -6,6 +6,7 @@ const sinon = require('sinon')
const baseOptions = require('../utils/base-options')
const peerUtils = require('../utils/creators/peer')
const all = require('it-all')
describe('libp2p.peerStore', () => {
let libp2p, remoteLibp2p
@ -35,13 +36,14 @@ describe('libp2p.peerStore', () => {
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)
const localPeers = await all(libp2p.peerStore.getPeers())
const publicKeyInLocalPeer = localPeers.get(remoteIdStr).id.pubKey
expect(localPeers.length).to.equal(1)
const publicKeyInLocalPeer = localPeers[0].id.pubKey
expect(publicKeyInLocalPeer.bytes).to.equalBytes(remoteLibp2p.peerId.pubKey.bytes)
const publicKeyInRemotePeer = remoteLibp2p.peerStore.keyBook.get(libp2p.peerId)
const publicKeyInRemotePeer = await remoteLibp2p.peerStore.keyBook.get(libp2p.peerId)
expect(publicKeyInRemotePeer).to.exist()
expect(publicKeyInRemotePeer.bytes).to.equalBytes(libp2p.peerId.pubKey.bytes)
})

View File

@ -2,11 +2,11 @@
/* eslint-env mocha */
const { expect } = require('aegir/utils/chai')
const all = require('it-all')
const PeerStore = require('../../src/peer-store')
const { Multiaddr } = require('multiaddr')
const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string')
const { MemoryDatastore } = require('datastore-core/memory')
const peerUtils = require('../utils/creators/peer')
const addr1 = new Multiaddr('/ip4/127.0.0.1/tcp/8000')
@ -18,6 +18,10 @@ const proto1 = '/protocol1'
const proto2 = '/protocol2'
const proto3 = '/protocol3'
/**
* @typedef {import('../../src/peer-store/types').PeerStore} PeerStore
*/
describe('peer-store', () => {
let peerIds
before(async () => {
@ -27,62 +31,65 @@ describe('peer-store', () => {
})
describe('empty books', () => {
/** @type {PeerStore} */
let peerStore
beforeEach(() => {
peerStore = new PeerStore({ peerId: peerIds[4] })
peerStore = new PeerStore({
peerId: peerIds[4],
datastore: new MemoryDatastore()
})
})
it('has an empty map of peers', () => {
const peers = peerStore.peers
expect(peers.size).to.equal(0)
it('has an empty map of peers', async () => {
const peers = await all(peerStore.getPeers())
expect(peers.length).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('deletes a peerId', async () => {
await peerStore.addressBook.set(peerIds[0], [new Multiaddr('/ip4/127.0.0.1/tcp/4001')])
await expect(peerStore.has(peerIds[0])).to.eventually.be.true()
await peerStore.delete(peerIds[0])
await expect(peerStore.has(peerIds[0])).to.eventually.be.false()
})
it('returns undefined on trying to find a non existant peerId', () => {
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()
it('sets the peer\'s public key to the KeyBook', async () => {
await peerStore.keyBook.set(peerIds[0], peerIds[0].pubKey)
await expect(peerStore.keyBook.get(peerIds[0])).to.eventually.deep.equal(peerIds[0].pubKey)
})
})
describe('previously populated books', () => {
/** @type {PeerStore} */
let peerStore
beforeEach(() => {
peerStore = new PeerStore({ peerId: peerIds[4] })
beforeEach(async () => {
peerStore = new PeerStore({
peerId: peerIds[4],
datastore: new MemoryDatastore()
})
// Add peer0 with { addr1, addr2 } and { proto1 }
peerStore.addressBook.set(peerIds[0], [addr1, addr2])
peerStore.protoBook.set(peerIds[0], [proto1])
await peerStore.addressBook.set(peerIds[0], [addr1, addr2])
await peerStore.protoBook.set(peerIds[0], [proto1])
// Add peer1 with { addr3 } and { proto2, proto3 }
peerStore.addressBook.set(peerIds[1], [addr3])
peerStore.protoBook.set(peerIds[1], [proto2, proto3])
await peerStore.addressBook.set(peerIds[1], [addr3])
await peerStore.protoBook.set(peerIds[1], [proto2, proto3])
// Add peer2 with { addr4 }
peerStore.addressBook.set(peerIds[2], [addr4])
await peerStore.addressBook.set(peerIds[2], [addr4])
// Add peer3 with { addr4 } and { proto2 }
peerStore.addressBook.set(peerIds[3], [addr4])
peerStore.protoBook.set(peerIds[3], [proto2])
await peerStore.addressBook.set(peerIds[3], [addr4])
await peerStore.protoBook.set(peerIds[3], [proto2])
})
it('has peers', () => {
const peers = peerStore.peers
it('has peers', async () => {
const peers = await all(peerStore.getPeers())
expect(peers.size).to.equal(4)
expect(Array.from(peers.keys())).to.have.members([
expect(peers.length).to.equal(4)
expect(peers.map(peer => peer.id.toB58String())).to.have.members([
peerIds[0].toB58String(),
peerIds[1].toB58String(),
peerIds[2].toB58String(),
@ -90,47 +97,45 @@ describe('peer-store', () => {
])
})
it('returns true on deleting a stored peer', () => {
const deleted = peerStore.delete(peerIds[0])
expect(deleted).to.equal(true)
it('deletes a stored peer', async () => {
await peerStore.delete(peerIds[0])
const peers = peerStore.peers
expect(peers.size).to.equal(3)
const peers = await all(peerStore.getPeers())
expect(peers.length).to.equal(3)
expect(Array.from(peers.keys())).to.not.have.members([peerIds[0].toB58String()])
})
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)
it('deletes a stored peer which is only on one book', async () => {
await peerStore.delete(peerIds[2])
const peers = peerStore.peers
expect(peers.size).to.equal(3)
const peers = await all(peerStore.getPeers())
expect(peers.length).to.equal(3)
})
it('gets the stored information of a peer in all its books', () => {
const peer = peerStore.get(peerIds[0])
it('gets the stored information of a peer in all its books', async () => {
const peer = await peerStore.get(peerIds[0])
expect(peer).to.exist()
expect(peer.protocols).to.have.members([proto1])
const peerMultiaddrs = peer.addresses.map((mi) => mi.multiaddr)
expect(peerMultiaddrs).to.have.members([addr1, addr2])
expect(peerMultiaddrs).to.have.deep.members([addr1, addr2])
expect(peer.id).to.exist()
expect(peer.id.toB58String()).to.equal(peerIds[0].toB58String())
})
it('gets the stored information of a peer that is not present in all its books', () => {
const peers = peerStore.get(peerIds[2])
it('gets the stored information of a peer that is not present in all its books', async () => {
const peers = await peerStore.get(peerIds[2])
expect(peers).to.exist()
expect(peers.protocols.length).to.eql(0)
const peerMultiaddrs = peers.addresses.map((mi) => mi.multiaddr)
expect(peerMultiaddrs).to.have.members([addr4])
expect(peerMultiaddrs).to.have.deep.members([addr4])
})
it('can find all the peers supporting a protocol', () => {
it('can find all the peers supporting a protocol', async () => {
const peerSupporting2 = []
for (const [, peer] of peerStore.peers.entries()) {
for await (const peer of peerStore.getPeers()) {
if (peer.protocols.includes(proto2)) {
peerSupporting2.push(peer)
}
@ -141,67 +146,71 @@ describe('peer-store', () => {
expect(peerSupporting2[1].id.toB58String()).to.eql(peerIds[3].toB58String())
})
it('can find all the peers listening on a given address', () => {
const peerListenint4 = []
it('can find all the peers listening on a given address', async () => {
const peerListening4 = []
for (const [, peer] of peerStore.peers.entries()) {
const multiaddrs = peer.addresses.map((mi) => mi.multiaddr)
for await (const peer of peerStore.getPeers()) {
const multiaddrs = peer.addresses.map((mi) => mi.multiaddr.toString())
if (multiaddrs.includes(addr4)) {
peerListenint4.push(peer)
if (multiaddrs.includes(addr4.toString())) {
peerListening4.push(peer)
}
}
expect(peerListenint4.length).to.eql(2)
expect(peerListenint4[0].id.toB58String()).to.eql(peerIds[2].toB58String())
expect(peerListenint4[1].id.toB58String()).to.eql(peerIds[3].toB58String())
expect(peerListening4.length).to.eql(2)
expect(peerListening4[0].id.toB58String()).to.eql(peerIds[2].toB58String())
expect(peerListening4[1].id.toB58String()).to.eql(peerIds[3].toB58String())
})
})
describe('peerStore.peers', () => {
describe('peerStore.getPeers', () => {
/** @type {PeerStore} */
let peerStore
beforeEach(() => {
peerStore = new PeerStore({ peerId: peerIds[4] })
peerStore = new PeerStore({
peerId: peerIds[4],
datastore: new MemoryDatastore()
})
})
it('returns peers if only addresses are known', () => {
peerStore.addressBook.set(peerIds[0], [addr1])
it('returns peers if only addresses are known', async () => {
await peerStore.addressBook.set(peerIds[0], [addr1])
const peers = peerStore.peers
expect(peers.size).to.equal(1)
const peers = await all(peerStore.getPeers())
expect(peers.length).to.equal(1)
const peerData = peers.get(peerIds[0].toB58String())
const peerData = peers[0]
expect(peerData).to.exist()
expect(peerData.id).to.exist()
expect(peerData.addresses).to.have.lengthOf(1)
expect(peerData.protocols).to.have.lengthOf(0)
expect(peerData.metadata).to.not.exist()
expect(peerData.metadata).to.be.empty()
})
it('returns peers if only protocols are known', () => {
peerStore.protoBook.set(peerIds[0], [proto1])
it('returns peers if only protocols are known', async () => {
await peerStore.protoBook.set(peerIds[0], [proto1])
const peers = peerStore.peers
expect(peers.size).to.equal(1)
const peers = await all(peerStore.getPeers())
expect(peers.length).to.equal(1)
const peerData = peers.get(peerIds[0].toB58String())
const peerData = peers[0]
expect(peerData).to.exist()
expect(peerData.id).to.exist()
expect(peerData.addresses).to.have.lengthOf(0)
expect(peerData.protocols).to.have.lengthOf(1)
expect(peerData.metadata).to.not.exist()
expect(peerData.metadata).to.be.empty()
})
it('returns peers if only metadata is known', () => {
it('returns peers if only metadata is known', async () => {
const metadataKey = 'location'
const metadataValue = uint8ArrayFromString('earth')
peerStore.metadataBook.set(peerIds[0], metadataKey, metadataValue)
await peerStore.metadataBook.setValue(peerIds[0], metadataKey, metadataValue)
const peers = peerStore.peers
expect(peers.size).to.equal(1)
const peers = await all(peerStore.getPeers())
expect(peers.length).to.equal(1)
const peerData = peers.get(peerIds[0].toB58String())
const peerData = peers[0]
expect(peerData).to.exist()
expect(peerData.id).to.exist()
expect(peerData.addresses).to.have.lengthOf(0)

View File

@ -1,608 +0,0 @@
'use strict'
/* eslint-env mocha */
const { expect } = require('aegir/utils/chai')
const sinon = require('sinon')
const Envelope = require('../../src/record/envelope')
const PeerRecord = require('../../src/record/peer-record')
const PeerStore = require('../../src/peer-store/persistent')
const { Multiaddr } = require('multiaddr')
const { MemoryDatastore } = require('datastore-core/memory')
const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string')
const peerUtils = require('../utils/creators/peer')
describe('Persisted PeerStore', () => {
let datastore, peerStore
let peerId
before(async () => {
[peerId] = await peerUtils.createPeerId({ fixture: false })
})
describe('start and stop flows', () => {
beforeEach(() => {
datastore = new MemoryDatastore()
peerStore = new PeerStore({ datastore, peerId })
})
afterEach(() => peerStore.stop())
it('should try to load content from an empty datastore on start', async () => {
const spyQuery = sinon.spy(datastore, 'query')
const spyProcessEntry = sinon.spy(peerStore, '_processDatastoreEntry')
await peerStore.start()
expect(spyQuery).to.have.property('callCount', 1)
expect(spyProcessEntry).to.have.property('callCount', 0)
// No data to populate
expect(peerStore.peers.size).to.eq(0)
})
it('should try to commit data on stop but should not add to batch if not exists', async () => {
const spyDs = sinon.spy(peerStore, '_commitData')
const spyBatch = sinon.spy(datastore, 'batch')
await peerStore.start()
expect(spyDs).to.have.property('callCount', 0)
await peerStore.stop()
expect(spyBatch).to.have.property('callCount', 0)
expect(spyDs).to.have.property('callCount', 1)
})
})
describe('simple setup with content stored per change (threshold 1)', () => {
beforeEach(() => {
datastore = new MemoryDatastore()
peerStore = new PeerStore({ datastore, peerId, threshold: 1 })
})
afterEach(() => peerStore.stop())
it('should store peerStore content on datastore after peer marked as dirty (threshold 1)', async () => {
const [peer] = await peerUtils.createPeerId({ number: 2 })
const multiaddrs = [new Multiaddr('/ip4/156.10.1.22/tcp/1000')]
const protocols = ['/ping/1.0.0']
const spyDirty = sinon.spy(peerStore, '_addDirtyPeer')
const spyDs = sinon.spy(datastore, 'batch')
const commitSpy = sinon.spy(peerStore, '_commitData')
await peerStore.start()
// AddressBook
peerStore.addressBook.set(peer, multiaddrs)
expect(spyDirty).to.have.property('callCount', 1) // Address
expect(spyDs).to.have.property('callCount', 1)
// let batch commit complete
await Promise.all(commitSpy.returnValues)
// ProtoBook
peerStore.protoBook.set(peer, protocols)
expect(spyDirty).to.have.property('callCount', 2) // Protocol
expect(spyDs).to.have.property('callCount', 2)
// let batch commit complete
await Promise.all(commitSpy.returnValues)
// Should have three peer records stored in the datastore
const queryParams = {
prefix: '/peers/'
}
let count = 0
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
count++
}
expect(count).to.equal(2)
// Validate data
const storedPeer = peerStore.get(peer)
expect(storedPeer.id.toB58String()).to.eql(peer.toB58String())
expect(storedPeer.protocols).to.have.members(protocols)
expect(storedPeer.addresses.map((a) => a.multiaddr.toString())).to.have.members([multiaddrs[0].toString()])
expect(storedPeer.addresses.map((a) => a.isCertified)).to.have.members([false])
})
it('should load content to the peerStore when restart but not put in datastore again', async () => {
const spyDs = sinon.spy(datastore, 'batch')
const peers = await peerUtils.createPeerId({ number: 2 })
const commitSpy = sinon.spy(peerStore, '_commitData')
const multiaddrs = [
new Multiaddr('/ip4/156.10.1.22/tcp/1000'),
new Multiaddr('/ip4/156.10.1.23/tcp/1000')
]
const protocols = ['/ping/1.0.0']
await peerStore.start()
// AddressBook
peerStore.addressBook.set(peers[0], [multiaddrs[0]])
peerStore.addressBook.set(peers[1], [multiaddrs[1]])
// let batch commit complete
await Promise.all(commitSpy.returnValues)
// KeyBook
peerStore.keyBook.set(peers[0], peers[0].pubKey)
peerStore.keyBook.set(peers[1], peers[1].pubKey)
// no batch commit as public key inline
// ProtoBook
peerStore.protoBook.set(peers[0], protocols)
peerStore.protoBook.set(peers[1], protocols)
// let batch commit complete
await Promise.all(commitSpy.returnValues)
// MetadataBook
peerStore.metadataBook.set(peers[0], 'location', uint8ArrayFromString('earth'))
// let batch commit complete
await Promise.all(commitSpy.returnValues)
expect(spyDs).to.have.property('callCount', 5) // 2 Address + 2 Proto + 1 Metadata
expect(peerStore.peers.size).to.equal(2)
await peerStore.stop()
peerStore.keyBook.data.clear()
peerStore.addressBook.data.clear()
peerStore.protoBook.data.clear()
// Load on restart
const spy = sinon.spy(peerStore, '_processDatastoreEntry')
await peerStore.start()
expect(spy).to.have.property('callCount', 5)
expect(spyDs).to.have.property('callCount', 5)
expect(peerStore.peers.size).to.equal(2)
expect(peerStore.addressBook.data.size).to.equal(2)
expect(peerStore.keyBook.data.size).to.equal(0)
expect(peerStore.protoBook.data.size).to.equal(2)
expect(peerStore.metadataBook.data.size).to.equal(1)
})
it('should delete content from the datastore on delete', async () => {
const [peer] = await peerUtils.createPeerId()
const multiaddrs = [new Multiaddr('/ip4/156.10.1.22/tcp/1000')]
const protocols = ['/ping/1.0.0']
const commitSpy = sinon.spy(peerStore, '_commitData')
await peerStore.start()
// AddressBook
peerStore.addressBook.set(peer, multiaddrs)
// ProtoBook
peerStore.protoBook.set(peer, protocols)
// MetadataBook
peerStore.metadataBook.set(peer, 'location', uint8ArrayFromString('earth'))
// let batch commit complete
await Promise.all(commitSpy.returnValues)
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')
const spyMetadataBook = sinon.spy(peerStore.metadataBook, 'delete')
// Delete from PeerStore
peerStore.delete(peer)
// let batch commit complete
await Promise.all(commitSpy.returnValues)
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(spyMetadataBook).to.have.property('callCount', 1)
expect(spyDs).to.have.property('callCount', 3)
// Should have zero peer records stored in the datastore
const queryParams = {
prefix: '/peers/'
}
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
throw new Error('Datastore should be empty')
}
})
it('should store certified peer records after peer marked as dirty (threshold 1)', async () => {
const [peerId] = await peerUtils.createPeerId()
const multiaddrs = [new Multiaddr('/ip4/156.10.1.22/tcp/1000')]
const spyDirty = sinon.spy(peerStore, '_addDirtyPeer')
const spyDs = sinon.spy(datastore, 'batch')
const commitSpy = sinon.spy(peerStore, '_commitData')
await peerStore.start()
const peerRecord = new PeerRecord({
peerId,
multiaddrs
})
const envelope = await Envelope.seal(peerRecord, peerId)
// consume peer record
const consumed = peerStore.addressBook.consumePeerRecord(envelope)
expect(consumed).to.eql(true)
expect(spyDirty).to.have.property('callCount', 1) // Address
expect(spyDs).to.have.property('callCount', 1)
// let batch commit complete
await Promise.all(commitSpy.returnValues)
// Should have three peer records stored in the datastore
const queryParams = {
prefix: '/peers/'
}
let count = 0
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
count++
}
expect(count).to.equal(1)
// Validate data
const storedPeer = peerStore.get(peerId)
expect(storedPeer.id.toB58String()).to.eql(peerId.toB58String())
expect(storedPeer.addresses.map((a) => a.multiaddr.toString())).to.have.members([multiaddrs[0].toString()])
expect(storedPeer.addresses.map((a) => a.isCertified)).to.have.members([true])
})
it('should load certified peer records to the peerStore when restart but not put in datastore again', async () => {
const spyDs = sinon.spy(datastore, 'batch')
const peers = await peerUtils.createPeerId({ number: 2 })
const commitSpy = sinon.spy(peerStore, '_commitData')
const multiaddrs = [
new Multiaddr('/ip4/156.10.1.22/tcp/1000'),
new Multiaddr('/ip4/156.10.1.23/tcp/1000')
]
const peerRecord0 = new PeerRecord({
peerId: peers[0],
multiaddrs: [multiaddrs[0]]
})
const envelope0 = await Envelope.seal(peerRecord0, peers[0])
const peerRecord1 = new PeerRecord({
peerId: peers[1],
multiaddrs: [multiaddrs[1]]
})
const envelope1 = await Envelope.seal(peerRecord1, peers[1])
await peerStore.start()
// AddressBook
let consumed = peerStore.addressBook.consumePeerRecord(envelope0)
expect(consumed).to.eql(true)
consumed = peerStore.addressBook.consumePeerRecord(envelope1)
expect(consumed).to.eql(true)
// let batch commit complete
await Promise.all(commitSpy.returnValues)
expect(spyDs).to.have.property('callCount', 2) // 2 Address + 2 Key + 2 Proto + 1 Metadata
expect(peerStore.peers.size).to.equal(2)
await peerStore.stop()
peerStore.addressBook.data.clear()
// Load on restart
const spy = sinon.spy(peerStore, '_processDatastoreEntry')
await peerStore.start()
expect(spy).to.have.property('callCount', 2)
expect(spyDs).to.have.property('callCount', 2)
expect(peerStore.peers.size).to.equal(2)
expect(peerStore.addressBook.data.size).to.equal(2)
expect(peerStore.addressBook.getRawEnvelope(peers[0])).to.exist()
expect(peerStore.addressBook.getRawEnvelope(peers[1])).to.exist()
// Validate stored envelopes
const storedEnvelope0 = await peerStore.addressBook.getPeerRecord(peers[0])
expect(envelope0.equals(storedEnvelope0)).to.eql(true)
const storedEnvelope1 = await peerStore.addressBook.getPeerRecord(peers[1])
expect(envelope1.equals(storedEnvelope1)).to.eql(true)
// Validate multiaddrs
const storedPeer0 = peerStore.get(peers[0])
expect(storedPeer0.id.toB58String()).to.eql(peers[0].toB58String())
expect(storedPeer0.addresses.map((a) => a.multiaddr.toString())).to.have.members([multiaddrs[0].toString()])
expect(storedPeer0.addresses.map((a) => a.isCertified)).to.have.members([true])
const storedPeer1 = peerStore.get(peers[1])
expect(storedPeer1.id.toB58String()).to.eql(peers[1].toB58String())
expect(storedPeer1.addresses.map((a) => a.multiaddr.toString())).to.have.members([multiaddrs[1].toString()])
expect(storedPeer1.addresses.map((a) => a.isCertified)).to.have.members([true])
})
it('should delete certified peer records from the datastore on delete', async () => {
const [peer] = await peerUtils.createPeerId()
const multiaddrs = [new Multiaddr('/ip4/156.10.1.22/tcp/1000')]
const commitSpy = sinon.spy(peerStore, '_commitData')
await peerStore.start()
// AddressBook
const peerRecord = new PeerRecord({
peerId: peer,
multiaddrs
})
const envelope = await Envelope.seal(peerRecord, peer)
// consume peer record
const consumed = peerStore.addressBook.consumePeerRecord(envelope)
expect(consumed).to.eql(true)
// let batch commit complete
await Promise.all(commitSpy.returnValues)
expect(peerStore.addressBook.getRawEnvelope(peer)).to.exist()
const spyDs = sinon.spy(datastore, 'batch')
const spyAddressBook = sinon.spy(peerStore.addressBook, 'delete')
// Delete from PeerStore
peerStore.delete(peer)
// let batch commit complete
await Promise.all(commitSpy.returnValues)
await peerStore.stop()
expect(spyAddressBook).to.have.property('callCount', 1)
expect(spyDs).to.have.property('callCount', 1)
// Should have zero peer records stored in the datastore
const queryParams = {
prefix: '/peers/'
}
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
throw new Error('Datastore should be empty')
}
expect(peerStore.addressBook.getRawEnvelope(peer)).to.not.exist()
})
})
describe('setup with content not stored per change (threshold 2)', () => {
beforeEach(() => {
datastore = new MemoryDatastore()
peerStore = new PeerStore({ datastore, peerId, threshold: 2 })
})
afterEach(() => peerStore.stop())
it('should not commit until threshold is reached', async () => {
const spyDirty = sinon.spy(peerStore, '_addDirtyPeer')
const spyDirtyMetadata = sinon.spy(peerStore, '_addDirtyPeerMetadata')
const spyDs = sinon.spy(datastore, 'batch')
const commitSpy = sinon.spy(peerStore, '_commitData')
const peers = await peerUtils.createPeerId({ number: 2 })
const multiaddrs = [new Multiaddr('/ip4/156.10.1.22/tcp/1000')]
const protocols = ['/ping/1.0.0']
await peerStore.start()
expect(spyDirty).to.have.property('callCount', 0)
expect(spyDs).to.have.property('callCount', 0)
// Add Peer0 data in multiple books
peerStore.addressBook.set(peers[0], multiaddrs)
peerStore.protoBook.set(peers[0], protocols)
peerStore.metadataBook.set(peers[0], 'location', uint8ArrayFromString('earth'))
// let batch commit complete
await Promise.all(commitSpy.returnValues)
// Remove data from the same Peer
peerStore.addressBook.delete(peers[0])
// let batch commit complete
await Promise.all(commitSpy.returnValues)
expect(spyDirty).to.have.property('callCount', 3) // 2 AddrBook ops, 1 ProtoBook op
expect(spyDirtyMetadata).to.have.property('callCount', 1) // 1 MetadataBook op
expect(peerStore._dirtyPeers.size).to.equal(1)
expect(spyDs).to.have.property('callCount', 0)
const queryParams = {
prefix: '/peers/'
}
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
throw new Error('Datastore should be empty')
}
// Add data for second book
peerStore.addressBook.set(peers[1], multiaddrs)
// let batch commit complete
await Promise.all(commitSpy.returnValues)
expect(spyDirty).to.have.property('callCount', 4)
expect(spyDirtyMetadata).to.have.property('callCount', 1)
expect(spyDs).to.have.property('callCount', 1)
// Should have three peer records stored in the datastore
let count = 0
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
count++
}
expect(count).to.equal(3)
expect(peerStore.peers.size).to.equal(2)
})
it('should commit on stop if threshold was not reached', async () => {
const spyDirty = sinon.spy(peerStore, '_addDirtyPeer')
const spyDs = sinon.spy(datastore, 'batch')
const protocols = ['/ping/1.0.0']
const [peer] = await peerUtils.createPeerId()
await peerStore.start()
// Add Peer data in a book
peerStore.protoBook.set(peer, protocols)
expect(spyDs).to.have.property('callCount', 0)
expect(spyDirty).to.have.property('callCount', 1) // ProtoBook
expect(peerStore._dirtyPeers.size).to.equal(1)
const queryParams = {
prefix: '/peers/'
}
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
throw new Error('Datastore should be empty')
}
await peerStore.stop()
expect(spyDirty).to.have.property('callCount', 1)
expect(spyDs).to.have.property('callCount', 1)
expect(peerStore._dirtyPeers.size).to.equal(0) // Reset
// Should have one peer record stored in the datastore
let count = 0
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
count++
}
expect(count).to.equal(1)
expect(peerStore.peers.size).to.equal(1)
})
})
})
describe('libp2p.peerStore (Persisted)', () => {
describe('disabled by default', () => {
let libp2p
before(async () => {
[libp2p] = await peerUtils.createPeer({
started: false
})
})
afterEach(() => libp2p.stop())
it('should not have have persistence capabilities', async () => {
await libp2p.start()
expect(libp2p.peerStore._dirtyPeers).to.not.exist()
expect(libp2p.peerStore.threshold).to.not.exist()
})
})
describe('enabled', () => {
let libp2p
let memoryDatastore
beforeEach(async () => {
memoryDatastore = new MemoryDatastore()
;[libp2p] = await peerUtils.createPeer({
started: false,
config: {
datastore: memoryDatastore,
peerStore: {
persistence: true,
threshold: 2 // trigger on second peer changed
}
}
})
})
afterEach(() => libp2p.stop())
it('should start on libp2p start and load content', async () => {
const spyPeerStore = sinon.spy(libp2p.peerStore, 'start')
const spyDs = sinon.spy(memoryDatastore, 'query')
await libp2p.start()
expect(spyPeerStore).to.have.property('callCount', 1)
expect(spyDs).to.have.property('callCount', 1)
})
it('should load content to the peerStore when a new node is started with the same datastore', async () => {
const commitSpy = sinon.spy(libp2p.peerStore, '_commitData')
const peers = await peerUtils.createPeerId({ number: 3 })
const multiaddrs = [
new Multiaddr('/ip4/156.10.1.22/tcp/1000'),
new Multiaddr('/ip4/156.10.1.23/tcp/1000')
]
const protocols = ['/ping/1.0.0']
await libp2p.start()
// AddressBook
libp2p.peerStore.addressBook.set(peers[1], [multiaddrs[0]])
libp2p.peerStore.addressBook.set(peers[2], [multiaddrs[1]])
// let batch commit complete
await Promise.all(commitSpy.returnValues)
// ProtoBook
libp2p.peerStore.protoBook.set(peers[1], protocols)
libp2p.peerStore.protoBook.set(peers[2], protocols)
// let batch commit complete
await Promise.all(commitSpy.returnValues)
expect(libp2p.peerStore.peers.size).to.equal(2)
await libp2p.stop()
// Use a new node with the previously populated datastore
const [newNode] = await peerUtils.createPeer({
started: false,
config: {
datastore: memoryDatastore,
peerStore: {
persistence: true
},
config: {
peerDiscovery: {
autoDial: false
}
}
}
})
expect(newNode.peerStore.peers.size).to.equal(0)
const spy = sinon.spy(newNode.peerStore, '_processDatastoreEntry')
await newNode.start()
expect(spy).to.have.property('callCount', 4) // 4 datastore entries
expect(newNode.peerStore.peers.size).to.equal(2)
// Validate data
const peer0 = newNode.peerStore.get(peers[1])
expect(peer0.id.toB58String()).to.eql(peers[1].toB58String())
expect(peer0.protocols).to.have.members(protocols)
expect(peer0.addresses.map((a) => a.multiaddr.toString())).to.have.members([multiaddrs[0].toString()])
const peer1 = newNode.peerStore.get(peers[2])
expect(peer1.id.toB58String()).to.eql(peers[2].toB58String())
expect(peer1.protocols).to.have.members(protocols)
expect(peer1.addresses.map((a) => a.multiaddr.toString())).to.have.members([multiaddrs[1].toString()])
await newNode.stop()
})
})
})

View File

@ -3,7 +3,7 @@
const { expect } = require('aegir/utils/chai')
const sinon = require('sinon')
const { MemoryDatastore } = require('datastore-core/memory')
const pDefer = require('p-defer')
const pWaitFor = require('p-wait-for')
@ -11,12 +11,19 @@ const PeerStore = require('../../src/peer-store')
const peerUtils = require('../utils/creators/peer')
const {
ERR_INVALID_PARAMETERS
codes: { ERR_INVALID_PARAMETERS }
} = require('../../src/errors')
/**
* @typedef {import('../../src/peer-store/types').PeerStore} PeerStore
* @typedef {import('../../src/peer-store/types').ProtoBook} ProtoBook
* @typedef {import('peer-id')} PeerId
*/
const arraysAreEqual = (a, b) => a.length === b.length && a.sort().every((item, index) => b[index] === item)
describe('protoBook', () => {
/** @type {PeerId} */
let peerId
before(async () => {
@ -24,10 +31,16 @@ describe('protoBook', () => {
})
describe('protoBook.set', () => {
let peerStore, pb
/** @type {PeerStore} */
let peerStore
/** @type {ProtoBook} */
let pb
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
pb = peerStore.protoBook
})
@ -35,19 +48,15 @@ describe('protoBook', () => {
peerStore.removeAllListeners()
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
pb.set('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
it('throws invalid parameters error if invalid PeerId is provided', async () => {
await expect(pb.set('invalid peerId')).to.eventually.be.rejected().with.property('code', ERR_INVALID_PARAMETERS)
})
it('throwns invalid parameters error if no protocols provided', () => {
expect(() => {
pb.set(peerId)
}).to.throw(ERR_INVALID_PARAMETERS)
it('throws invalid parameters error if no protocols provided', async () => {
await expect(pb.set(peerId)).to.eventually.be.rejected().with.property('code', ERR_INVALID_PARAMETERS)
})
it('replaces the stored content by default and emit change event', () => {
it('replaces the stored content by default and emit change event', async () => {
const defer = pDefer()
const supportedProtocols = ['protocol1', 'protocol2']
@ -57,14 +66,14 @@ describe('protoBook', () => {
defer.resolve()
})
pb.set(peerId, supportedProtocols)
const protocols = pb.get(peerId)
await pb.set(peerId, supportedProtocols)
const protocols = await pb.get(peerId)
expect(protocols).to.have.deep.members(supportedProtocols)
return defer.promise
await defer.promise
})
it('emits on set if not storing the exact same content', () => {
it('emits on set if not storing the exact same content', async () => {
const defer = pDefer()
const supportedProtocolsA = ['protocol1', 'protocol2']
@ -79,17 +88,17 @@ describe('protoBook', () => {
})
// set 1
pb.set(peerId, supportedProtocolsA)
await pb.set(peerId, supportedProtocolsA)
// set 2 (same content)
pb.set(peerId, supportedProtocolsB)
const protocols = pb.get(peerId)
await pb.set(peerId, supportedProtocolsB)
const protocols = await pb.get(peerId)
expect(protocols).to.have.deep.members(supportedProtocolsB)
return defer.promise
await defer.promise
})
it('does not emit on set if it is storing the exact same content', () => {
it('does not emit on set if it is storing the exact same content', async () => {
const defer = pDefer()
const supportedProtocols = ['protocol1', 'protocol2']
@ -103,10 +112,10 @@ describe('protoBook', () => {
})
// set 1
pb.set(peerId, supportedProtocols)
await pb.set(peerId, supportedProtocols)
// set 2 (same content)
pb.set(peerId, supportedProtocols)
await pb.set(peerId, supportedProtocols)
// Wait 50ms for incorrect second event
setTimeout(() => {
@ -118,10 +127,16 @@ describe('protoBook', () => {
})
describe('protoBook.add', () => {
let peerStore, pb
/** @type {PeerStore} */
let peerStore
/** @type {ProtoBook} */
let pb
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
pb = peerStore.protoBook
})
@ -129,19 +144,15 @@ describe('protoBook', () => {
peerStore.removeAllListeners()
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
pb.add('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
it('throws invalid parameters error if invalid PeerId is provided', async () => {
await expect(pb.add('invalid peerId')).to.eventually.be.rejected().with.property('code', ERR_INVALID_PARAMETERS)
})
it('throwns invalid parameters error if no protocols provided', () => {
expect(() => {
pb.add(peerId)
}).to.throw(ERR_INVALID_PARAMETERS)
it('throws invalid parameters error if no protocols provided', async () => {
await expect(pb.add(peerId)).to.eventually.be.rejected().with.property('code', ERR_INVALID_PARAMETERS)
})
it('adds the new content and emits change event', () => {
it('adds the new content and emits change event', async () => {
const defer = pDefer()
const supportedProtocolsA = ['protocol1', 'protocol2']
@ -157,19 +168,19 @@ describe('protoBook', () => {
})
// Replace
pb.set(peerId, supportedProtocolsA)
let protocols = pb.get(peerId)
await pb.set(peerId, supportedProtocolsA)
let protocols = await pb.get(peerId)
expect(protocols).to.have.deep.members(supportedProtocolsA)
// Add
pb.add(peerId, supportedProtocolsB)
protocols = pb.get(peerId)
await pb.add(peerId, supportedProtocolsB)
protocols = await pb.get(peerId)
expect(protocols).to.have.deep.members(finalProtocols)
return defer.promise
})
it('emits on add if the content to add not exists', () => {
it('emits on add if the content to add not exists', async () => {
const defer = pDefer()
const supportedProtocolsA = ['protocol1']
@ -185,17 +196,17 @@ describe('protoBook', () => {
})
// set 1
pb.set(peerId, supportedProtocolsA)
await pb.set(peerId, supportedProtocolsA)
// set 2 (content already existing)
pb.add(peerId, supportedProtocolsB)
const protocols = pb.get(peerId)
await pb.add(peerId, supportedProtocolsB)
const protocols = await pb.get(peerId)
expect(protocols).to.have.deep.members(finalProtocols)
return defer.promise
})
it('does not emit on add if the content to add already exists', () => {
it('does not emit on add if the content to add already exists', async () => {
const defer = pDefer()
const supportedProtocolsA = ['protocol1', 'protocol2']
@ -210,10 +221,10 @@ describe('protoBook', () => {
})
// set 1
pb.set(peerId, supportedProtocolsA)
await pb.set(peerId, supportedProtocolsA)
// set 2 (content already existing)
pb.add(peerId, supportedProtocolsB)
await pb.add(peerId, supportedProtocolsB)
// Wait 50ms for incorrect second event
setTimeout(() => {
@ -225,10 +236,16 @@ describe('protoBook', () => {
})
describe('protoBook.remove', () => {
let peerStore, pb
/** @type {PeerStore} */
let peerStore
/** @type {ProtoBook} */
let pb
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
pb = peerStore.protoBook
})
@ -236,16 +253,12 @@ describe('protoBook', () => {
peerStore.removeAllListeners()
})
it('throws invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
pb.remove('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
it('throws invalid parameters error if invalid PeerId is provided', async () => {
await expect(pb.remove('invalid peerId')).to.eventually.be.rejected().with.property('code', ERR_INVALID_PARAMETERS)
})
it('throws invalid parameters error if no protocols provided', () => {
expect(() => {
pb.remove(peerId)
}).to.throw(ERR_INVALID_PARAMETERS)
it('throws invalid parameters error if no protocols provided', async () => {
await expect(pb.remove(peerId)).to.eventually.be.rejected().with.property('code', ERR_INVALID_PARAMETERS)
})
it('removes the given protocol and emits change event', async () => {
@ -258,13 +271,13 @@ describe('protoBook', () => {
peerStore.on('change:protocols', spy)
// Replace
pb.set(peerId, supportedProtocols)
let protocols = pb.get(peerId)
await pb.set(peerId, supportedProtocols)
let protocols = await pb.get(peerId)
expect(protocols).to.have.deep.members(supportedProtocols)
// Remove
pb.remove(peerId, removedProtocols)
protocols = pb.get(peerId)
await pb.remove(peerId, removedProtocols)
protocols = await pb.get(peerId)
expect(protocols).to.have.deep.members(finalProtocols)
await pWaitFor(() => spy.callCount === 2)
@ -275,7 +288,7 @@ describe('protoBook', () => {
expect(arraysAreEqual(secondCallArgs.protocols, finalProtocols))
})
it('emits on remove if the content changes', () => {
it('emits on remove if the content changes', async () => {
const spy = sinon.spy()
const supportedProtocols = ['protocol1', 'protocol2']
@ -285,17 +298,17 @@ describe('protoBook', () => {
peerStore.on('change:protocols', spy)
// set
pb.set(peerId, supportedProtocols)
await pb.set(peerId, supportedProtocols)
// remove (content already existing)
pb.remove(peerId, removedProtocols)
const protocols = pb.get(peerId)
await pb.remove(peerId, removedProtocols)
const protocols = await pb.get(peerId)
expect(protocols).to.have.deep.members(finalProtocols)
return pWaitFor(() => spy.callCount === 2)
})
it('does not emit on remove if the content does not change', () => {
it('does not emit on remove if the content does not change', async () => {
const spy = sinon.spy()
const supportedProtocols = ['protocol1', 'protocol2']
@ -304,10 +317,10 @@ describe('protoBook', () => {
peerStore.on('change:protocols', spy)
// set
pb.set(peerId, supportedProtocols)
await pb.set(peerId, supportedProtocols)
// remove
pb.remove(peerId, removedProtocols)
await pb.remove(peerId, removedProtocols)
// Only one event
expect(spy.callCount).to.eql(1)
@ -315,73 +328,79 @@ describe('protoBook', () => {
})
describe('protoBook.get', () => {
let peerStore, pb
/** @type {PeerStore} */
let peerStore
/** @type {ProtoBook} */
let pb
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
pb = peerStore.protoBook
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
pb.get('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
it('throws invalid parameters error if invalid PeerId is provided', async () => {
await expect(pb.get('invalid peerId')).to.eventually.be.rejected().with.property('code', ERR_INVALID_PARAMETERS)
})
it('returns undefined if no protocols are known for the provided peer', () => {
const protocols = pb.get(peerId)
it('returns empty if no protocols are known for the provided peer', async () => {
const protocols = await pb.get(peerId)
expect(protocols).to.not.exist()
expect(protocols).to.be.empty()
})
it('returns the protocols stored', () => {
it('returns the protocols stored', async () => {
const supportedProtocols = ['protocol1', 'protocol2']
pb.set(peerId, supportedProtocols)
await pb.set(peerId, supportedProtocols)
const protocols = pb.get(peerId)
const protocols = await pb.get(peerId)
expect(protocols).to.have.deep.members(supportedProtocols)
})
})
describe('protoBook.delete', () => {
let peerStore, pb
/** @type {PeerStore} */
let peerStore
/** @type {ProtoBook} */
let pb
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
pb = peerStore.protoBook
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
pb.delete('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
it('throws invalid parameters error if invalid PeerId is provided', async () => {
await expect(pb.delete('invalid peerId')).to.eventually.be.rejected().with.property('code', ERR_INVALID_PARAMETERS)
})
it('returns false if no records exist for the peer and no event is emitted', () => {
it('should not emit event if no records exist for the peer', async () => {
const defer = pDefer()
peerStore.on('change:protocols', () => {
defer.reject()
})
const deleted = pb.delete(peerId)
expect(deleted).to.equal(false)
await pb.delete(peerId)
// Wait 50ms for incorrect invalid event
setTimeout(() => {
defer.resolve()
}, 50)
return defer.promise
await defer.promise
})
it('returns true if the record exists and an event is emitted', () => {
it('should emit event if a record exists for the peer', async () => {
const defer = pDefer()
const supportedProtocols = ['protocol1', 'protocol2']
pb.set(peerId, supportedProtocols)
await pb.set(peerId, supportedProtocols)
// Listen after set
peerStore.on('change:protocols', ({ protocols }) => {
@ -389,11 +408,9 @@ describe('protoBook', () => {
defer.resolve()
})
const deleted = pb.delete(peerId)
await pb.delete(peerId)
expect(deleted).to.equal(true)
return defer.promise
await defer.promise
})
})
})

View File

@ -5,7 +5,7 @@ const { expect } = require('aegir/utils/chai')
const pDefer = require('p-defer')
const { EventEmitter } = require('events')
const { MemoryDatastore } = require('datastore-core/memory')
const Topology = require('libp2p-interfaces/src/topology/multicodec-topology')
const PeerStore = require('../../src/peer-store')
const Registrar = require('../../src/registrar')
@ -27,19 +27,23 @@ describe('registrar', () => {
describe('errors', () => {
beforeEach(() => {
peerStore = new PeerStore({ peerId })
peerStore = new PeerStore({
peerId,
datastore: new MemoryDatastore()
})
registrar = new Registrar({ peerStore, connectionManager: new EventEmitter() })
})
it('should fail to register a protocol if no multicodec is provided', () => {
expect(() => registrar.register()).to.throw()
return expect(registrar.register()).to.eventually.be.rejected()
})
it('should fail to register a protocol if an invalid topology is provided', () => {
const fakeTopology = {
random: 1
}
expect(() => registrar.register(fakeTopology)).to.throw()
return expect(registrar.register(fakeTopology)).to.eventually.be.rejected()
})
})
@ -57,7 +61,7 @@ describe('registrar', () => {
afterEach(() => libp2p.stop())
it('should be able to register a protocol', () => {
it('should be able to register a protocol', async () => {
const topologyProps = new Topology({
multicodecs: multicodec,
handlers: {
@ -66,12 +70,12 @@ describe('registrar', () => {
}
})
const identifier = libp2p.registrar.register(topologyProps)
const identifier = await libp2p.registrar.register(topologyProps)
expect(identifier).to.exist()
})
it('should be able to unregister a protocol', () => {
it('should be able to unregister a protocol', async () => {
const topologyProps = new Topology({
multicodecs: multicodec,
handlers: {
@ -80,7 +84,7 @@ describe('registrar', () => {
}
})
const identifier = libp2p.registrar.register(topologyProps)
const identifier = await libp2p.registrar.register(topologyProps)
const success = libp2p.registrar.unregister(identifier)
expect(success).to.eql(true)
@ -100,12 +104,6 @@ describe('registrar', () => {
const conn = await createMockConnection()
const remotePeerId = conn.remotePeer
// Add connected peer with protocol to peerStore and registrar
libp2p.peerStore.protoBook.add(remotePeerId, [multicodec])
libp2p.connectionManager.onConnect(conn)
expect(libp2p.connectionManager.size).to.eql(1)
const topologyProps = new Topology({
multicodecs: multicodec,
handlers: {
@ -124,12 +122,18 @@ describe('registrar', () => {
})
// Register protocol
const identifier = libp2p.registrar.register(topologyProps)
const identifier = await libp2p.registrar.register(topologyProps)
const topology = libp2p.registrar.topologies.get(identifier)
// Topology created
expect(topology).to.exist()
// Add connected peer with protocol to peerStore and registrar
await libp2p.peerStore.protoBook.add(remotePeerId, [multicodec])
await libp2p.connectionManager.onConnect(conn)
expect(libp2p.connectionManager.size).to.eql(1)
await conn.close()
libp2p.connectionManager.onDisconnect(conn)
@ -159,7 +163,7 @@ describe('registrar', () => {
})
// Register protocol
const identifier = libp2p.registrar.register(topologyProps)
const identifier = await libp2p.registrar.register(topologyProps)
const topology = libp2p.registrar.topologies.get(identifier)
// Topology created
@ -171,16 +175,16 @@ describe('registrar', () => {
const remotePeerId = conn.remotePeer
// Add connected peer to peerStore and registrar
libp2p.peerStore.protoBook.set(remotePeerId, [])
libp2p.connectionManager.onConnect(conn)
await libp2p.peerStore.protoBook.set(remotePeerId, [])
// Add protocol to peer and update it
libp2p.peerStore.protoBook.add(remotePeerId, [multicodec])
await libp2p.peerStore.protoBook.add(remotePeerId, [multicodec])
await libp2p.connectionManager.onConnect(conn)
await onConnectDefer.promise
// Remove protocol to peer and update it
libp2p.peerStore.protoBook.set(remotePeerId, [])
await libp2p.peerStore.protoBook.set(remotePeerId, [])
await onDisconnectDefer.promise
})

View File

@ -82,7 +82,7 @@ describe('auto-relay', () => {
const originalMultiaddrsLength = relayLibp2p.multiaddrs.length
// Discover relay
libp2p.peerStore.addressBook.add(relayLibp2p.peerId, relayLibp2p.multiaddrs)
await libp2p.peerStore.addressBook.add(relayLibp2p.peerId, relayLibp2p.multiaddrs)
await libp2p.dial(relayLibp2p.peerId)
// Wait for peer added as listen relay
@ -94,7 +94,7 @@ describe('auto-relay', () => {
expect(libp2p.multiaddrs[originalMultiaddrsLength].getPeerId()).to.eql(relayLibp2p.peerId.toB58String())
// Peer has relay multicodec
const knownProtocols = libp2p.peerStore.protoBook.get(relayLibp2p.peerId)
const knownProtocols = await libp2p.peerStore.protoBook.get(relayLibp2p.peerId)
expect(knownProtocols).to.include(relayMulticodec)
})
})
@ -165,7 +165,7 @@ describe('auto-relay', () => {
sinon.spy(autoRelay1, '_addListenRelay')
// Discover relay
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
const originalMultiaddrs1Length = relayLibp2p1.multiaddrs.length
const originalMultiaddrs2Length = relayLibp2p2.multiaddrs.length
@ -184,7 +184,7 @@ describe('auto-relay', () => {
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p2.peerId.toB58String())
// Peer has relay multicodec
const knownProtocols = relayLibp2p1.peerStore.protoBook.get(relayLibp2p2.peerId)
const knownProtocols = await relayLibp2p1.peerStore.protoBook.get(relayLibp2p2.peerId)
expect(knownProtocols).to.include(relayMulticodec)
})
@ -193,7 +193,7 @@ describe('auto-relay', () => {
const originalMultiaddrs2Length = relayLibp2p2.multiaddrs.length
// Discover relay
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.dial(relayLibp2p2.peerId)
@ -206,7 +206,7 @@ describe('auto-relay', () => {
// Dial from the other through a relay
const relayedMultiaddr2 = new Multiaddr(`${relayLibp2p1.multiaddrs[0]}/p2p/${relayLibp2p1.peerId.toB58String()}/p2p-circuit`)
libp2p.peerStore.addressBook.add(relayLibp2p2.peerId, [relayedMultiaddr2])
await libp2p.peerStore.addressBook.add(relayLibp2p2.peerId, [relayedMultiaddr2])
await libp2p.dial(relayLibp2p2.peerId)
})
@ -220,7 +220,7 @@ describe('auto-relay', () => {
sinon.spy(autoRelay1._listenRelays, 'add')
// Discover one relay and connect
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.dial(relayLibp2p2.peerId)
expect(relayLibp2p1.connectionManager.size).to.eql(1)
@ -237,11 +237,11 @@ describe('auto-relay', () => {
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p2.peerId.toB58String())
// Relay2 has relay multicodec
const knownProtocols2 = relayLibp2p1.peerStore.protoBook.get(relayLibp2p2.peerId)
const knownProtocols2 = await relayLibp2p1.peerStore.protoBook.get(relayLibp2p2.peerId)
expect(knownProtocols2).to.include(relayMulticodec)
// Discover an extra relay and connect
relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.dial(relayLibp2p3.peerId)
// Wait to guarantee the dialed peer is not added as a listen relay
@ -253,7 +253,7 @@ describe('auto-relay', () => {
expect(relayLibp2p1.connectionManager.size).to.eql(2)
// Relay2 has relay multicodec
const knownProtocols3 = relayLibp2p1.peerStore.protoBook.get(relayLibp2p3.peerId)
const knownProtocols3 = await relayLibp2p1.peerStore.protoBook.get(relayLibp2p3.peerId)
expect(knownProtocols3).to.include(relayMulticodec)
})
@ -264,7 +264,7 @@ describe('auto-relay', () => {
sinon.spy(relayLibp2p1.identifyService, 'pushToPeerStore')
// Discover one relay and connect
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.dial(relayLibp2p2.peerId)
// Wait for listenning on the relay
@ -283,7 +283,7 @@ describe('auto-relay', () => {
expect(autoRelay1._listenRelays.size).to.equal(0)
// Identify push for removing listen relay multiaddr
expect(relayLibp2p1.identifyService.pushToPeerStore.callCount).to.equal(2)
await pWaitFor(() => relayLibp2p1.identifyService.pushToPeerStore.callCount === 2)
})
it('should try to listen on other connected peers relayed address if one used relay disconnects', async () => {
@ -294,11 +294,11 @@ describe('auto-relay', () => {
sinon.spy(relayLibp2p1.transportManager, 'listen')
// Discover one relay and connect
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.dial(relayLibp2p2.peerId)
// Discover an extra relay and connect
relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.dial(relayLibp2p3.peerId)
// Wait for both peer to be attempted to added as listen relay
@ -337,11 +337,11 @@ describe('auto-relay', () => {
sinon.spy(relayLibp2p1.transportManager, 'listen')
// Discover one relay and connect
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.dial(relayLibp2p2.peerId)
// Discover an extra relay and connect to gather its Hop support
relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.dial(relayLibp2p3.peerId)
// Wait for both peer to be attempted to added as listen relay
@ -382,11 +382,11 @@ describe('auto-relay', () => {
sinon.spy(relayLibp2p1.transportManager, 'listen')
// Discover one relay and connect
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.dial(relayLibp2p2.peerId)
// Discover an extra relay and connect to gather its Hop support
relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.dial(relayLibp2p3.peerId)
// Wait for both peer to be attempted to added as listen relay
@ -479,7 +479,7 @@ describe('auto-relay', () => {
sinon.spy(autoRelay2, '_addListenRelay')
// Relay 1 discovers Relay 3 and connect
relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.dial(relayLibp2p3.peerId)
// Wait for peer added as listen relay
@ -487,7 +487,7 @@ describe('auto-relay', () => {
expect(autoRelay1._listenRelays.size).to.equal(1)
// Relay 2 discovers Relay 3 and connect
relayLibp2p2.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p2.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p2.dial(relayLibp2p3.peerId)
// Wait for peer added as listen relay
@ -496,7 +496,7 @@ describe('auto-relay', () => {
// Relay 1 discovers Relay 2 relayed multiaddr via Relay 3
const ma2RelayedBy3 = relayLibp2p2.multiaddrs[relayLibp2p2.multiaddrs.length - 1]
relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, [ma2RelayedBy3])
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, [ma2RelayedBy3])
await relayLibp2p1.dial(relayLibp2p2.peerId)
// Peer not added as listen relay

View File

@ -8,7 +8,6 @@ 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 { fromString: uint8ArrayFromString } = require('uint8arrays/from-string')
const { createPeerId } = require('../utils/creators/peer')
@ -46,14 +45,13 @@ describe('Dialing (via relay, TCP)', () => {
return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(libp2p => libp2p.start()))
})
afterEach(() => {
afterEach(async () => {
// Stop each node
return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(async libp2p => {
await libp2p.stop()
// Clear the peer stores
for (const peerIdStr of libp2p.peerStore.peers.keys()) {
const peerId = PeerId.createFromB58String(peerIdStr)
libp2p.peerStore.delete(peerId)
for await (const peer of libp2p.peerStore.getPeers()) {
libp2p.peerStore.delete(peer.id)
}
}))
})

View File

@ -2,7 +2,7 @@
/* eslint-env mocha */
const { expect } = require('aegir/utils/chai')
const { MemoryDatastore } = require('datastore-core/memory')
const AddressManager = require('../../src/address-manager')
const TransportManager = require('../../src/transport-manager')
const PeerStore = require('../../src/peer-store')
@ -33,7 +33,10 @@ describe('Transport Manager (TCP)', () => {
peerId: localPeer,
multiaddrs: addrs,
addressManager: new AddressManager({ listen: addrs }),
peerStore: new PeerStore({ peerId: localPeer })
peerStore: new PeerStore({
peerId: localPeer,
datastore: new MemoryDatastore()
})
},
upgrader: mockUpgrader,
onConnection: () => {}
@ -67,7 +70,7 @@ describe('Transport Manager (TCP)', () => {
})
it('should create self signed peer record on listen', async () => {
let signedPeerRecord = await tm.libp2p.peerStore.addressBook.getPeerRecord(localPeer)
let signedPeerRecord = await tm.libp2p.peerStore.addressBook.getRawEnvelope(localPeer)
expect(signedPeerRecord).to.not.exist()
tm.add(Transport.prototype[Symbol.toStringTag], Transport)

View File

@ -2,17 +2,17 @@
"name": "ts-use",
"private": true,
"dependencies": {
"datastore-level": "^6.0.0",
"ipfs-http-client": "^50.1.2",
"@achingbrain/libp2p-gossipsub": "^0.12.2",
"@chainsafe/libp2p-noise": "^5.0.0",
"datastore-level": "^7.0.1",
"ipfs-http-client": "^55.0.0",
"libp2p": "file:../..",
"libp2p-bootstrap": "^0.13.0",
"libp2p-bootstrap": "^0.14.0",
"libp2p-delegated-content-routing": "^0.11.0",
"libp2p-delegated-peer-routing": "^0.11.1",
"libp2p-gossipsub": "^0.9.0",
"libp2p-interfaces": "^1.0.1",
"libp2p-kad-dht": "^0.26.5",
"libp2p-interfaces": "^4.0.0",
"libp2p-kad-dht": "^0.28.6",
"libp2p-mplex": "^0.10.4",
"@chainsafe/libp2p-noise": "^4.1.0",
"libp2p-record": "^0.10.4",
"libp2p-tcp": "^0.17.1",
"libp2p-websockets": "^0.16.1",

View File

@ -105,8 +105,7 @@ async function main() {
},
datastore: new LevelStore('path/to/store'),
peerStore: {
persistence: false,
threshold: 5
persistence: false
},
keychain: {
pass: 'notsafepassword123456789',

View File

@ -58,12 +58,13 @@ function _populateAddressBooks (peers) {
* @param {Object} [properties]
* @param {number} [properties.number] - number of peers (default: 1).
* @param {boolean} [properties.fixture] - use fixture for peer-id generation (default: true)
* @param {PeerId.CreateOptions} [properties.opts]
* @returns {Promise<Array<PeerId>>}
*/
function createPeerId ({ number = 1, fixture = true } = {}) {
function createPeerId ({ number = 1, fixture = true, opts = {} } = {}) {
return pTimes(number, (i) => fixture
? PeerId.createFromJSON(Peers[i])
: PeerId.create()
: PeerId.create(opts)
)
}

View File

@ -48,7 +48,7 @@ module.exports = async (properties = {}) => {
protocol: protocols[0]
}
},
close: () => { },
close: async () => { },
getStreams: () => openStreams,
...properties
})