diff --git a/src/identify/index.js b/src/identify/index.js index af9741d9..d59e537a 100644 --- a/src/identify/index.js +++ b/src/identify/index.js @@ -82,9 +82,6 @@ class IdentifyService { this._protocols = protocols this.handleMessage = this.handleMessage.bind(this) - - // TODO: this should be stored in the certified AddressBook in follow up PR - this._selfRecord = undefined } /** @@ -184,22 +181,18 @@ class IdentifyService { // Get the observedAddr if there is one observedAddr = IdentifyService.getCleanMultiaddr(observedAddr) - let addresses - try { const envelope = await Envelope.openAndCertify(signedPeerRecord, PeerRecord.DOMAIN) - const peerRecord = await PeerRecord.createFromProtobuf(envelope.payload) - - addresses = peerRecord.multiaddrs + if (this.peerStore.addressBook.consumePeerRecord(envelope)) { + return + } } catch (err) { log('received invalid envelope, discard it and fallback to listenAddrs is available', err) - // Try Legacy - addresses = listenAddrs } // Update peers data in PeerStore try { - this.peerStore.addressBook.set(id, addresses.map((addr) => multiaddr(addr))) + this.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr))) } catch (err) { log.error('received invalid addrs', err) } @@ -294,21 +287,24 @@ class IdentifyService { const id = connection.remotePeer - let addresses - try { const envelope = await Envelope.openAndCertify(message.signedPeerRecord, PeerRecord.DOMAIN) - const peerRecord = await PeerRecord.createFromProtobuf(envelope.payload) - - addresses = peerRecord.multiaddrs + if (this.peerStore.addressBook.consumePeerRecord(envelope)) { + return + } } catch (err) { +<<<<<<< HEAD log('received invalid envelope, discard it and fallback to listenAddrs is available', err) // Try Legacy addresses = message.listenAddrs +======= + log('received invalid envelope, discard it and fallback to listenAddrs is available') +>>>>>>> chore: add certified peer records to persisted peer store } + // Update peers data in PeerStore try { - this.peerStore.addressBook.set(id, addresses.map((addr) => multiaddr(addr))) + this.peerStore.addressBook.set(id, message.listenAddrs.map((addr) => multiaddr(addr))) } catch (err) { log.error('received invalid addrs', err) } @@ -321,10 +317,12 @@ class IdentifyService { * Get self signed peer record raw envelope. * @return {Buffer} */ - async _getSelfPeerRecord () { + async _getSelfPeerRecord() { + const selfSignedPeerRecord = this.peerStore.addressBook.getRawEnvelope(this.peerId) + // TODO: support invalidation when dynamic multiaddrs are supported - if (this._selfRecord) { - return this._selfRecord + if (selfSignedPeerRecord) { + return selfSignedPeerRecord } try { @@ -333,10 +331,9 @@ class IdentifyService { multiaddrs: this._libp2p.multiaddrs }) const envelope = await Envelope.seal(peerRecord, this.peerId) + this.peerStore.addressBook.consumePeerRecord(envelope) - this._selfRecord = envelope.marshal() - - return this._selfRecord + return this.peerStore.addressBook.getRawEnvelope(this.peerId) } catch (err) { log.error('failed to get self peer record') } diff --git a/src/peer-store/address-book.js b/src/peer-store/address-book.js index 4cd5bd0c..d2677fe2 100644 --- a/src/peer-store/address-book.js +++ b/src/peer-store/address-book.js @@ -72,8 +72,8 @@ class AddressBook extends Book { } /** - * ConsumePeerRecord adds addresses from a signed peer.PeerRecord contained in a record envelope. - * This will return a boolean that indicates if the record was successfully processed and integrated + * ConsumePeerRecord adds addresses from a signed peer record contained in a record envelope. + * This will return a boolean that indicates if the record was successfully processed and added * into the AddressBook. * @param {Envelope} envelope * @return {boolean} @@ -93,31 +93,25 @@ class AddressBook extends Book { return false } + // ensure the record has multiaddrs + if (!peerRecord.multiaddrs || !peerRecord.multiaddrs.length) { + return false + } + const peerId = peerRecord.peerId const id = peerId.toB58String() const entry = this.data.get(id) || {} const storedRecord = entry.record // ensure seq is greater than, or equal to, the last received - if (storedRecord && - storedRecord.seqNumber >= peerRecord.seqNumber) { - return false - } - - // ensure the record has multiaddrs - if (!peerRecord.multiaddrs || !peerRecord.multiaddrs.length) { + if (storedRecord && storedRecord.seqNumber >= peerRecord.seqNumber) { return false } const addresses = this._toAddresses(peerRecord.multiaddrs, true) - // TODO: new record with different addresses from stored record - // - Remove the older ones? - // - Change to uncertified? - - // TODO: events - // Should a multiaddr only modified to certified trigger an event? - // - Needed for persistent peer store + // Replace unsigned addresses by the new ones from the record + // TODO: Once we have ttls for the addresses, we should merge these in. this._setData(peerId, { addresses, record: { @@ -130,19 +124,34 @@ class AddressBook extends Book { return true } + /** + * Get a peer raw envelope. + * @param {PeerId} peerId + * @return {Buffer} + */ + getRawEnvelope (peerId) { + const entry = this.data.get(peerId.toB58String()) + + if (!entry || !entry.record || !entry.record.raw) { + return undefined + } + + return entry.record.raw + } + /** * Get an Envelope containing a PeerRecord for the given peer. * @param {PeerId} peerId * @return {Promise} */ getPeerRecord (peerId) { - const entry = this.data.get(peerId.toB58String()) + const raw = this.getRawEnvelope(peerId) - if (!entry || !entry.record || !entry.record.raw) { - return + if (!raw) { + return undefined } - return Envelope.createFromProtobuf(entry.record.raw) + return Envelope.createFromProtobuf(raw) } /** @@ -170,7 +179,7 @@ class AddressBook extends Book { // Already knows the peer if (rec && rec.length === addresses.length) { - const intersection = rec.filter((mi) => addresses.some((newMi) => mi.multiaddr.equals(newMi.multiaddr))) + const intersection = rec.filter((addr) => addresses.some((newAddr) => addr.multiaddr.equals(newAddr.multiaddr))) // Are new addresses equal to the old ones? // If yes, no changes needed! @@ -214,9 +223,9 @@ class AddressBook extends Book { const rec = entry.addresses // Add recorded uniquely to the new array (Union) - rec && rec.forEach((mi) => { - if (!addresses.find(r => r.multiaddr.equals(mi.multiaddr))) { - addresses.push(mi) + rec && rec.forEach((addr) => { + if (!addresses.find(r => r.multiaddr.equals(addr.multiaddr))) { + addresses.push(addr) } }) @@ -249,7 +258,6 @@ class AddressBook extends Book { * @returns {Array} */ get (peerId) { - // TODO: should we return Entry instead?? if (!PeerId.isPeerId(peerId)) { throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS) } diff --git a/src/peer-store/persistent/index.js b/src/peer-store/persistent/index.js index c6df7b5d..331faf1b 100644 --- a/src/peer-store/persistent/index.js +++ b/src/peer-store/persistent/index.js @@ -177,21 +177,24 @@ class PersistentPeerStore extends PeerStore { const b32key = peerId.toString() const key = new Key(`${NAMESPACE_ADDRESS}${b32key}`) - const addresses = this.addressBook.get(peerId) + const entry = this.addressBook.data.get(peerId.toB58String()) try { // Deleted from the book - if (!addresses) { + if (!entry) { batch.delete(key) return } const encodedData = Addresses.encode({ - addrs: addresses.map((address) => ({ + addrs: entry.addresses.map((address) => ({ multiaddr: address.multiaddr.buffer - })) + })), + certified_record: entry.record ? { + seq: entry.record.seqNumber, + raw: entry.record.raw + } : undefined }) - batch.put(key, encodedData) } catch (err) { log.error(err) @@ -299,7 +302,11 @@ class PersistentPeerStore extends PeerStore { { addresses: decoded.addrs.map((address) => ({ multiaddr: multiaddr(address.multiaddr) - })) + })), + record: decoded.certified_record ? { + raw: decoded.certified_record.raw, + seqNumber: decoded.certified_record.seq + } : undefined }, { emit: false }) break diff --git a/test/dialing/direct.spec.js b/test/dialing/direct.spec.js index 69b54ecc..d5b38a2b 100644 --- a/test/dialing/direct.spec.js +++ b/test/dialing/direct.spec.js @@ -363,7 +363,7 @@ describe('Dialing (direct, WebSockets)', () => { const connection = await libp2p.dial(remoteAddr) expect(connection).to.exist() - sinon.spy(libp2p.peerStore.addressBook, 'set') + sinon.spy(libp2p.peerStore.addressBook, 'consumePeerRecord') sinon.spy(libp2p.peerStore.protoBook, 'set') // Wait for onConnection to be called @@ -372,7 +372,8 @@ describe('Dialing (direct, WebSockets)', () => { expect(libp2p.identifyService.identify.callCount).to.equal(1) await libp2p.identifyService.identify.firstCall.returnValue - expect(libp2p.peerStore.addressBook.set.callCount).to.equal(1) + // Self + New peer + expect(libp2p.peerStore.addressBook.consumePeerRecord.callCount).to.equal(2) expect(libp2p.peerStore.protoBook.set.callCount).to.equal(1) }) diff --git a/test/identify/index.spec.js b/test/identify/index.spec.js index b92dc83a..80995b98 100644 --- a/test/identify/index.spec.js +++ b/test/identify/index.spec.js @@ -19,6 +19,7 @@ const { IdentifyService, multicodecs } = require('../../src/identify') const Peers = require('../fixtures/peers') const Libp2p = require('../../src') const Envelope = require('../../src/record/envelope') +const PeerStore = require('../../src/peer-store') const baseOptions = require('../utils/base-options.browser') const pkg = require('../../package.json') @@ -50,14 +51,7 @@ describe('Identify', () => { libp2p: { peerId: localPeer, connectionManager: new EventEmitter(), - peerStore: { - addressBook: { - set: () => { } - }, - protoBook: { - set: () => { } - } - }, + peerStore: new PeerStore(), multiaddrs: listenMaddrs }, protocols @@ -67,6 +61,7 @@ describe('Identify', () => { libp2p: { peerId: remotePeer, connectionManager: new EventEmitter(), + peerStore: new PeerStore(), multiaddrs: listenMaddrs }, protocols @@ -109,17 +104,7 @@ describe('Identify', () => { libp2p: { peerId: localPeer, connectionManager: new EventEmitter(), - peerStore: { - addressBook: { - set: () => {} - }, - protoBook: { - set: () => { } - }, - metadataBook: { - set: () => { } - } - }, + peerStore: new PeerStore(), multiaddrs: listenMaddrs }, protocols @@ -129,6 +114,7 @@ describe('Identify', () => { libp2p: { peerId: remotePeer, connectionManager: new EventEmitter(), + peerStore: new PeerStore(), multiaddrs: listenMaddrs }, protocols @@ -142,7 +128,7 @@ describe('Identify', () => { sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY }) sinon.stub(Envelope, 'openAndCertify').throws() - sinon.spy(localIdentify.peerStore.addressBook, 'set') + sinon.spy(localIdentify.peerStore.addressBook, 'consumePeerRecord') sinon.spy(localIdentify.peerStore.protoBook, 'set') sinon.spy(localIdentify.peerStore.metadataBook, 'set') @@ -156,7 +142,7 @@ describe('Identify', () => { }) ]) - expect(localIdentify.peerStore.addressBook.set.callCount).to.equal(1) + expect(localIdentify.peerStore.addressBook.consumePeerRecord.callCount).to.equal(1) expect(localIdentify.peerStore.protoBook.set.callCount).to.equal(1) const metadataArgs = localIdentify.peerStore.metadataBook.set.firstCall.args @@ -165,11 +151,11 @@ describe('Identify', () => { expect(metadataArgs[2].toString()).to.equal(`js-libp2p/${pkg.version}`) // Validate the remote peer gets updated in the peer store - const call = localIdentify.peerStore.addressBook.set.firstCall - expect(call.args[0].id.bytes).to.equal(remotePeer.bytes) - expect(call.args[1]).to.exist() - expect(call.args[1]).have.lengthOf(listenMaddrs.length) - expect(call.args[1][0].equals(listenMaddrs[0])) + const addresses = 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])) + expect(addresses.map((a) => a.isCertified)[0]).to.eql(true) }) it('should throw if identified peer is the wrong peer', async () => { @@ -177,14 +163,7 @@ describe('Identify', () => { libp2p: { peerId: localPeer, connectionManager: new EventEmitter(), - peerStore: { - addressBook: { - set: () => { } - }, - protoBook: { - set: () => { } - } - }, + peerStore: new PeerStore(), multiaddrs: [] }, protocols @@ -193,6 +172,7 @@ describe('Identify', () => { libp2p: { peerId: remotePeer, connectionManager: new EventEmitter(), + peerStore: new PeerStore(), multiaddrs: [] }, protocols @@ -229,6 +209,7 @@ describe('Identify', () => { libp2p: { peerId: localPeer, connectionManager: new EventEmitter(), + peerStore: new PeerStore(), multiaddrs: listenMaddrs }, protocols: new Map([ @@ -241,14 +222,7 @@ describe('Identify', () => { libp2p: { peerId: remotePeer, connectionManager, - peerStore: { - addressBook: { - set: () => { } - }, - protoBook: { - set: () => { } - } - }, + peerStore: new PeerStore(), multiaddrs: [] } }) @@ -261,7 +235,7 @@ describe('Identify', () => { const [local, remote] = duplexPair() sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH }) - sinon.spy(remoteIdentify.peerStore.addressBook, 'set') + sinon.spy(remoteIdentify.peerStore.addressBook, 'consumePeerRecord') sinon.spy(remoteIdentify.peerStore.protoBook, 'set') // Run identify @@ -274,11 +248,14 @@ describe('Identify', () => { }) ]) - expect(remoteIdentify.peerStore.addressBook.set.callCount).to.equal(1) + expect(remoteIdentify.peerStore.addressBook.consumePeerRecord.callCount).to.equal(1) expect(remoteIdentify.peerStore.protoBook.set.callCount).to.equal(1) - const [peerId, multiaddrs] = remoteIdentify.peerStore.addressBook.set.firstCall.args - expect(peerId.bytes).to.eql(localPeer.bytes) - expect(multiaddrs).to.eql(listenMaddrs) + + const addresses = localIdentify.peerStore.addressBook.get(localPeer) + expect(addresses).to.exist() + expect(addresses).have.lengthOf(listenMaddrs.length) + expect(addresses.map((a) => a.multiaddr)).to.eql(listenMaddrs) + const [peerId2, protocols] = remoteIdentify.peerStore.protoBook.set.firstCall.args expect(peerId2.bytes).to.eql(localPeer.bytes) expect(protocols).to.eql(Array.from(localProtocols)) @@ -293,6 +270,7 @@ describe('Identify', () => { libp2p: { peerId: localPeer, connectionManager: new EventEmitter(), + peerStore: new PeerStore(), multiaddrs: listenMaddrs }, protocols: new Map([ @@ -305,14 +283,7 @@ describe('Identify', () => { libp2p: { peerId: remotePeer, connectionManager, - peerStore: { - addressBook: { - set: () => { } - }, - protoBook: { - set: () => { } - } - }, + peerStore: new PeerStore(), multiaddrs: [] } }) @@ -326,7 +297,7 @@ describe('Identify', () => { sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH }) sinon.stub(Envelope, 'openAndCertify').throws() - sinon.spy(remoteIdentify.peerStore.addressBook, 'set') + sinon.spy(remoteIdentify.peerStore.addressBook, 'consumePeerRecord') sinon.spy(remoteIdentify.peerStore.protoBook, 'set') // Run identify @@ -339,11 +310,14 @@ describe('Identify', () => { }) ]) - expect(remoteIdentify.peerStore.addressBook.set.callCount).to.equal(1) + expect(remoteIdentify.peerStore.addressBook.consumePeerRecord.callCount).to.equal(1) expect(remoteIdentify.peerStore.protoBook.set.callCount).to.equal(1) - const [peerId, multiaddrs] = remoteIdentify.peerStore.addressBook.set.firstCall.args - expect(peerId.bytes).to.eql(localPeer.bytes) - expect(multiaddrs).to.eql(listenMaddrs) + + const addresses = localIdentify.peerStore.addressBook.get(localPeer) + expect(addresses).to.exist() + expect(addresses).have.lengthOf(listenMaddrs.length) + expect(addresses.map((a) => a.multiaddr)).to.eql(listenMaddrs) + const [peerId2, protocols] = remoteIdentify.peerStore.protoBook.set.firstCall.args expect(peerId2.bytes).to.eql(localPeer.bytes) expect(protocols).to.eql(Array.from(localProtocols)) @@ -378,15 +352,15 @@ describe('Identify', () => { await libp2p.start() sinon.spy(libp2p.identifyService, 'identify') - const peerStoreSpySet = sinon.spy(libp2p.peerStore.addressBook, 'set') + const peerStoreSpyConsumeRecord = sinon.spy(libp2p.peerStore.addressBook, 'consumePeerRecord') const peerStoreSpyAdd = sinon.spy(libp2p.peerStore.addressBook, 'add') const connection = await libp2p.dialer.connectToPeer(remoteAddr) expect(connection).to.exist() // Wait for peer store to be updated - // Dialer._createDialTarget (add), Identify (replace) - await pWaitFor(() => peerStoreSpySet.callCount === 1 && peerStoreSpyAdd.callCount === 1) + // Dialer._createDialTarget (add), Identify (consume), Create self (consume) + await pWaitFor(() => peerStoreSpyConsumeRecord.callCount === 2 && peerStoreSpyAdd.callCount === 1) expect(libp2p.identifyService.identify.callCount).to.equal(1) // The connection should have no open streams diff --git a/test/peer-store/address-book.spec.js b/test/peer-store/address-book.spec.js index eb77bc74..2273cdb2 100644 --- a/test/peer-store/address-book.spec.js +++ b/test/peer-store/address-book.spec.js @@ -459,7 +459,8 @@ describe('addressBook', () => { return defer.promise }) - it('with same data currently in AddressBook (not certified)', async () => { + it('emits change:multiaddrs event with same data currently in AddressBook (not certified)', async () => { + const defer = pDefer() const multiaddrs = [addr1, addr2] // Set addressBook data @@ -482,10 +483,19 @@ describe('addressBook', () => { }) const envelope = await Envelope.seal(peerRecord, peerId) + peerStore.once('change:multiaddrs', ({ peerId, multiaddrs }) => { + expect(peerId).to.exist() + expect(multiaddrs).to.eql(multiaddrs) + defer.resolve() + }) + // consume peer record const consumed = ab.consumePeerRecord(envelope) expect(consumed).to.eql(true) + // Wait event + await defer.promise + // Validate data exists and certified addrs = ab.get(peerId) expect(addrs).to.exist() @@ -496,7 +506,8 @@ describe('addressBook', () => { }) }) - it('with previous partial data in AddressBook (not certified)', async () => { + it('emits change:multiaddrs event with previous partial data in AddressBook (not certified)', async () => { + const defer = pDefer() const multiaddrs = [addr1, addr2] // Set addressBook data @@ -516,10 +527,19 @@ describe('addressBook', () => { }) const envelope = await Envelope.seal(peerRecord, peerId) + peerStore.once('change:multiaddrs', ({ peerId, multiaddrs }) => { + expect(peerId).to.exist() + expect(multiaddrs).to.eql(multiaddrs) + defer.resolve() + }) + // consume peer record const consumed = ab.consumePeerRecord(envelope) expect(consumed).to.eql(true) + // Wait event + await defer.promise + // Validate data exists and certified addrs = ab.get(peerId) expect(addrs).to.exist() @@ -531,6 +551,7 @@ describe('addressBook', () => { }) it('with previous different data in AddressBook (not certified)', async () => { + const defer = pDefer() const multiaddrsUncertified = [addr3] const multiaddrsCertified = [addr1, addr2] @@ -553,10 +574,19 @@ describe('addressBook', () => { }) const envelope = await Envelope.seal(peerRecord, peerId) + peerStore.once('change:multiaddrs', ({ peerId, multiaddrs }) => { + expect(peerId).to.exist() + expect(multiaddrs).to.eql(multiaddrs) + defer.resolve() + }) + // consume peer record const consumed = ab.consumePeerRecord(envelope) expect(consumed).to.eql(true) + // Wait event + await defer.promise + // Validate data exists and certified addrs = ab.get(peerId) expect(addrs).to.exist() @@ -565,7 +595,6 @@ describe('addressBook', () => { expect(addr.isCertified).to.eql(true) expect(multiaddrsCertified[index].equals(addr.multiaddr)).to.eql(true) }) - // TODO: should it has the older one? }) }) diff --git a/test/peer-store/persisted-peer-store.spec.js b/test/peer-store/persisted-peer-store.spec.js index 43b761a4..4bfda1cd 100644 --- a/test/peer-store/persisted-peer-store.spec.js +++ b/test/peer-store/persisted-peer-store.spec.js @@ -5,7 +5,11 @@ const chai = require('chai') chai.use(require('dirty-chai')) const { expect } = 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('interface-datastore') @@ -99,6 +103,7 @@ describe('Persisted PeerStore', () => { 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 () => { @@ -211,7 +216,162 @@ describe('Persisted PeerStore', () => { } }) - // TODO: certified? + it('should store certified peer records after peer marked as dirty (threshold 1)', async () => { + const [peerId] = await peerUtils.createPeerId() + const multiaddrs = [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 = [ + multiaddr('/ip4/156.10.1.22/tcp/1000'), + 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.isEqual(storedEnvelope0)).to.eql(true) + + const storedEnvelope1 = await peerStore.addressBook.getPeerRecord(peers[1]) + expect(envelope1.isEqual(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()]) + + 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()]) + }) + + it('should delete certified peer records from the datastore on delete', async () => { + const [peer] = await peerUtils.createPeerId() + const multiaddrs = [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)', () => {