mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-03-28 05:11:04 +00:00
**Motivation** In lodestar, when we handle "peer:connect" event, we dial the peer which gives another "peer:connect" event and it causes other issues **Motivation** In `onConnect` function, "peer:connect" event should be emitted after we add connection to the `connections` map so that when app dial the peer in "peer:connect" event handler, it uses the same/existing connection
596 lines
22 KiB
TypeScript
596 lines
22 KiB
TypeScript
/* eslint-env mocha */
|
|
|
|
import { expect } from 'aegir/chai'
|
|
import sinon from 'sinon'
|
|
import { Multiaddr } from '@multiformats/multiaddr'
|
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
import { codes } from '../../src/errors.js'
|
|
import { IdentifyService, Message } from '../../src/identify/index.js'
|
|
import Peers from '../fixtures/peers.js'
|
|
import { createLibp2pNode } from '../../src/libp2p.js'
|
|
import { PersistentPeerStore } from '@libp2p/peer-store'
|
|
import { createBaseOptions } from '../utils/base-options.browser.js'
|
|
import { DefaultAddressManager } from '../../src/address-manager/index.js'
|
|
import { MemoryDatastore } from 'datastore-core/memory'
|
|
import { MULTIADDRS_WEBSOCKETS } from '../fixtures/browser.js'
|
|
import * as lp from 'it-length-prefixed'
|
|
import drain from 'it-drain'
|
|
import { pipe } from 'it-pipe'
|
|
import { mockConnectionGater, mockRegistrar, mockUpgrader, connectionPair } from '@libp2p/interface-compliance-tests/mocks'
|
|
import { createFromJSON } from '@libp2p/peer-id-factory'
|
|
import { Components } from '@libp2p/interfaces/components'
|
|
import { PeerRecordUpdater } from '../../src/peer-record-updater.js'
|
|
import {
|
|
MULTICODEC_IDENTIFY,
|
|
MULTICODEC_IDENTIFY_PUSH
|
|
} from '../../src/identify/consts.js'
|
|
import { DefaultConnectionManager } from '../../src/connection-manager/index.js'
|
|
import { DefaultTransportManager } from '../../src/transport-manager.js'
|
|
import { CustomEvent, Startable } from '@libp2p/interfaces'
|
|
import delay from 'delay'
|
|
import pWaitFor from 'p-wait-for'
|
|
import { peerIdFromString } from '@libp2p/peer-id'
|
|
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
|
import type { Libp2pNode } from '../../src/libp2p.js'
|
|
import { pEvent } from 'p-event'
|
|
|
|
const listenMaddrs = [new Multiaddr('/ip4/127.0.0.1/tcp/15002/ws')]
|
|
|
|
const defaultInit = {
|
|
protocolPrefix: 'ipfs',
|
|
host: {
|
|
agentVersion: 'v1.0.0'
|
|
}
|
|
}
|
|
|
|
const protocols = [MULTICODEC_IDENTIFY, MULTICODEC_IDENTIFY_PUSH]
|
|
|
|
async function createComponents (index: number, services: Startable[]) {
|
|
const peerId = await createFromJSON(Peers[index])
|
|
|
|
const components = new Components({
|
|
peerId,
|
|
datastore: new MemoryDatastore(),
|
|
registrar: mockRegistrar(),
|
|
upgrader: mockUpgrader(),
|
|
connectionGater: mockConnectionGater()
|
|
})
|
|
const peerStore = new PersistentPeerStore(components, {
|
|
addressFilter: components.getConnectionGater().filterMultiaddrForPeer
|
|
})
|
|
components.setPeerStore(peerStore)
|
|
components.setAddressManager(new DefaultAddressManager(components, {
|
|
announce: listenMaddrs.map(ma => ma.toString())
|
|
}))
|
|
|
|
const connectionManager = new DefaultConnectionManager(components)
|
|
services.push(connectionManager)
|
|
components.setConnectionManager(connectionManager)
|
|
|
|
const transportManager = new DefaultTransportManager(components)
|
|
services.push(transportManager)
|
|
components.setTransportManager(transportManager)
|
|
|
|
await peerStore.protoBook.set(peerId, protocols)
|
|
|
|
return components
|
|
}
|
|
|
|
describe('Identify', () => {
|
|
let localComponents: Components
|
|
let remoteComponents: Components
|
|
|
|
let localPeerRecordUpdater: PeerRecordUpdater
|
|
let remotePeerRecordUpdater: PeerRecordUpdater
|
|
let services: Startable[]
|
|
|
|
beforeEach(async () => {
|
|
services = []
|
|
|
|
localComponents = await createComponents(0, services)
|
|
remoteComponents = await createComponents(1, services)
|
|
|
|
localPeerRecordUpdater = new PeerRecordUpdater(localComponents)
|
|
remotePeerRecordUpdater = new PeerRecordUpdater(remoteComponents)
|
|
|
|
await Promise.all(
|
|
services.map(s => s.start())
|
|
)
|
|
})
|
|
|
|
afterEach(async () => {
|
|
sinon.restore()
|
|
|
|
await Promise.all(
|
|
services.map(s => s.stop())
|
|
)
|
|
})
|
|
|
|
it('should be able to identify another peer', async () => {
|
|
const localIdentify = new IdentifyService(localComponents, defaultInit)
|
|
const remoteIdentify = new IdentifyService(remoteComponents, defaultInit)
|
|
|
|
await localIdentify.start()
|
|
await remoteIdentify.start()
|
|
|
|
const [localToRemote] = connectionPair(localComponents, remoteComponents)
|
|
|
|
const localAddressBookConsumePeerRecordSpy = sinon.spy(localComponents.getPeerStore().addressBook, 'consumePeerRecord')
|
|
const localProtoBookSetSpy = sinon.spy(localComponents.getPeerStore().protoBook, 'set')
|
|
|
|
// Make sure the remote peer has a peer record to share during identify
|
|
await remotePeerRecordUpdater.update()
|
|
|
|
// Run identify
|
|
await localIdentify.identify(localToRemote)
|
|
|
|
expect(localAddressBookConsumePeerRecordSpy.callCount).to.equal(1)
|
|
expect(localProtoBookSetSpy.callCount).to.equal(1)
|
|
|
|
// Validate the remote peer gets updated in the peer store
|
|
const addresses = await localComponents.getPeerStore().addressBook.get(remoteComponents.getPeerId())
|
|
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.be.true()
|
|
})
|
|
|
|
// LEGACY
|
|
it('should be able to identify another peer with no certified peer records support', async () => {
|
|
const agentVersion = 'js-libp2p/5.0.0'
|
|
const localIdentify = new IdentifyService(localComponents, {
|
|
protocolPrefix: 'ipfs',
|
|
host: {
|
|
agentVersion: agentVersion
|
|
}
|
|
})
|
|
await localIdentify.start()
|
|
const remoteIdentify = new IdentifyService(remoteComponents, {
|
|
protocolPrefix: 'ipfs',
|
|
host: {
|
|
agentVersion: agentVersion
|
|
}
|
|
})
|
|
await remoteIdentify.start()
|
|
|
|
const [localToRemote] = connectionPair(localComponents, remoteComponents)
|
|
|
|
sinon.stub(localComponents.getPeerStore().addressBook, 'consumePeerRecord').throws()
|
|
|
|
const localProtoBookSetSpy = sinon.spy(localComponents.getPeerStore().protoBook, 'set')
|
|
|
|
// Run identify
|
|
await localIdentify.identify(localToRemote)
|
|
|
|
expect(localProtoBookSetSpy.callCount).to.equal(1)
|
|
|
|
// Validate the remote peer gets updated in the peer store
|
|
const addresses = await localComponents.getPeerStore().addressBook.get(remoteComponents.getPeerId())
|
|
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.be.false()
|
|
})
|
|
|
|
it('should throw if identified peer is the wrong peer', async () => {
|
|
const localIdentify = new IdentifyService(localComponents, defaultInit)
|
|
const remoteIdentify = new IdentifyService(remoteComponents, defaultInit)
|
|
|
|
await localIdentify.start()
|
|
await remoteIdentify.start()
|
|
|
|
const [localToRemote] = connectionPair(localComponents, remoteComponents)
|
|
|
|
// send an invalid message
|
|
await remoteComponents.getRegistrar().unhandle(MULTICODEC_IDENTIFY)
|
|
await remoteComponents.getRegistrar().handle(MULTICODEC_IDENTIFY, (data) => {
|
|
void Promise.resolve().then(async () => {
|
|
const { connection, stream } = data
|
|
const signedPeerRecord = await remoteComponents.getPeerStore().addressBook.getRawEnvelope(remoteComponents.getPeerId())
|
|
|
|
const message = Message.Identify.encode({
|
|
protocolVersion: '123',
|
|
agentVersion: '123',
|
|
// send bad public key
|
|
publicKey: localComponents.getPeerId().publicKey ?? new Uint8Array(0),
|
|
listenAddrs: [],
|
|
signedPeerRecord,
|
|
observedAddr: connection.remoteAddr.bytes,
|
|
protocols: []
|
|
})
|
|
|
|
await pipe(
|
|
[message],
|
|
lp.encode(),
|
|
stream,
|
|
drain
|
|
)
|
|
})
|
|
})
|
|
|
|
// Run identify
|
|
await expect(localIdentify.identify(localToRemote))
|
|
.to.eventually.be.rejected()
|
|
.and.to.have.property('code', codes.ERR_INVALID_PEER)
|
|
})
|
|
|
|
it('should store own host data and protocol version into metadataBook on start', async () => {
|
|
const agentVersion = 'js-project/1.0.0'
|
|
const localIdentify = new IdentifyService(localComponents, {
|
|
protocolPrefix: 'ipfs',
|
|
host: {
|
|
agentVersion
|
|
}
|
|
})
|
|
|
|
await expect(localComponents.getPeerStore().metadataBook.getValue(localComponents.getPeerId(), 'AgentVersion'))
|
|
.to.eventually.be.undefined()
|
|
await expect(localComponents.getPeerStore().metadataBook.getValue(localComponents.getPeerId(), 'ProtocolVersion'))
|
|
.to.eventually.be.undefined()
|
|
|
|
await localIdentify.start()
|
|
|
|
await expect(localComponents.getPeerStore().metadataBook.getValue(localComponents.getPeerId(), 'AgentVersion'))
|
|
.to.eventually.deep.equal(uint8ArrayFromString(agentVersion))
|
|
await expect(localComponents.getPeerStore().metadataBook.getValue(localComponents.getPeerId(), 'ProtocolVersion'))
|
|
.to.eventually.be.ok()
|
|
|
|
await localIdentify.stop()
|
|
})
|
|
|
|
describe('push', () => {
|
|
it('should be able to push identify updates to another peer', async () => {
|
|
const localIdentify = new IdentifyService(localComponents, defaultInit)
|
|
const remoteIdentify = new IdentifyService(remoteComponents, defaultInit)
|
|
|
|
await localIdentify.start()
|
|
await remoteIdentify.start()
|
|
|
|
const [localToRemote, remoteToLocal] = connectionPair(localComponents, remoteComponents)
|
|
|
|
// ensure connections are registered by connection manager
|
|
localComponents.getUpgrader().dispatchEvent(new CustomEvent('connection', {
|
|
detail: localToRemote
|
|
}))
|
|
remoteComponents.getUpgrader().dispatchEvent(new CustomEvent('connection', {
|
|
detail: remoteToLocal
|
|
}))
|
|
|
|
// identify both ways
|
|
await localIdentify.identify(localToRemote)
|
|
await remoteIdentify.identify(remoteToLocal)
|
|
|
|
const updatedProtocol = '/special-new-protocol/1.0.0'
|
|
const updatedAddress = new Multiaddr('/ip4/127.0.0.1/tcp/48322')
|
|
|
|
// should have protocols but not our new one
|
|
const identifiedProtocols = await remoteComponents.getPeerStore().protoBook.get(localComponents.getPeerId())
|
|
expect(identifiedProtocols).to.not.be.empty()
|
|
expect(identifiedProtocols).to.not.include(updatedProtocol)
|
|
|
|
// should have addresses but not our new one
|
|
const identifiedAddresses = await remoteComponents.getPeerStore().addressBook.get(localComponents.getPeerId())
|
|
expect(identifiedAddresses).to.not.be.empty()
|
|
expect(identifiedAddresses.map(a => a.multiaddr.toString())).to.not.include(updatedAddress.toString())
|
|
|
|
// update local data - change event will trigger push
|
|
await localComponents.getPeerStore().protoBook.add(localComponents.getPeerId(), [updatedProtocol])
|
|
await localComponents.getPeerStore().addressBook.add(localComponents.getPeerId(), [updatedAddress])
|
|
|
|
// needed to update the peer record and send our supported addresses
|
|
const addressManager = localComponents.getAddressManager()
|
|
addressManager.getAddresses = () => {
|
|
return [updatedAddress]
|
|
}
|
|
|
|
// ensure sequence number of peer record we are about to create is different
|
|
await delay(1000)
|
|
|
|
// make sure we have a peer record to send
|
|
await localPeerRecordUpdater.update()
|
|
|
|
// wait for the remote peer store to notice the changes
|
|
const eventPromise = pEvent(remoteComponents.getPeerStore(), 'change:multiaddrs')
|
|
|
|
// push updated peer record to connections
|
|
await localIdentify.pushToPeerStore()
|
|
|
|
await eventPromise
|
|
|
|
// should have new protocol
|
|
const updatedProtocols = await remoteComponents.getPeerStore().protoBook.get(localComponents.getPeerId())
|
|
expect(updatedProtocols).to.not.be.empty()
|
|
expect(updatedProtocols).to.include(updatedProtocol)
|
|
|
|
// should have new address
|
|
const updatedAddresses = await remoteComponents.getPeerStore().addressBook.get(localComponents.getPeerId())
|
|
expect(updatedAddresses.map(a => {
|
|
return {
|
|
multiaddr: a.multiaddr.toString(),
|
|
isCertified: a.isCertified
|
|
}
|
|
})).to.deep.equal([{
|
|
multiaddr: updatedAddress.toString(),
|
|
isCertified: true
|
|
}])
|
|
|
|
await localIdentify.stop()
|
|
await remoteIdentify.stop()
|
|
})
|
|
|
|
// LEGACY
|
|
it('should be able to push identify updates to another peer with no certified peer records support', async () => {
|
|
const localIdentify = new IdentifyService(localComponents, defaultInit)
|
|
const remoteIdentify = new IdentifyService(remoteComponents, defaultInit)
|
|
|
|
await localIdentify.start()
|
|
await remoteIdentify.start()
|
|
|
|
const [localToRemote, remoteToLocal] = connectionPair(localComponents, remoteComponents)
|
|
|
|
// ensure connections are registered by connection manager
|
|
localComponents.getUpgrader().dispatchEvent(new CustomEvent('connection', {
|
|
detail: localToRemote
|
|
}))
|
|
remoteComponents.getUpgrader().dispatchEvent(new CustomEvent('connection', {
|
|
detail: remoteToLocal
|
|
}))
|
|
|
|
// identify both ways
|
|
await localIdentify.identify(localToRemote)
|
|
await remoteIdentify.identify(remoteToLocal)
|
|
|
|
const updatedProtocol = '/special-new-protocol/1.0.0'
|
|
const updatedAddress = new Multiaddr('/ip4/127.0.0.1/tcp/48322')
|
|
|
|
// should have protocols but not our new one
|
|
const identifiedProtocols = await remoteComponents.getPeerStore().protoBook.get(localComponents.getPeerId())
|
|
expect(identifiedProtocols).to.not.be.empty()
|
|
expect(identifiedProtocols).to.not.include(updatedProtocol)
|
|
|
|
// should have addresses but not our new one
|
|
const identifiedAddresses = await remoteComponents.getPeerStore().addressBook.get(localComponents.getPeerId())
|
|
expect(identifiedAddresses).to.not.be.empty()
|
|
expect(identifiedAddresses.map(a => a.multiaddr.toString())).to.not.include(updatedAddress.toString())
|
|
|
|
// update local data - change event will trigger push
|
|
await localComponents.getPeerStore().protoBook.add(localComponents.getPeerId(), [updatedProtocol])
|
|
await localComponents.getPeerStore().addressBook.add(localComponents.getPeerId(), [updatedAddress])
|
|
|
|
// needed to send our supported addresses
|
|
const addressManager = localComponents.getAddressManager()
|
|
addressManager.getAddresses = () => {
|
|
return [updatedAddress]
|
|
}
|
|
|
|
// wait until remote peer store notices protocol list update
|
|
const waitForUpdate = pEvent(remoteComponents.getPeerStore(), 'change:protocols')
|
|
|
|
await localIdentify.pushToPeerStore()
|
|
|
|
await waitForUpdate
|
|
|
|
// should have new protocol
|
|
const updatedProtocols = await remoteComponents.getPeerStore().protoBook.get(localComponents.getPeerId())
|
|
expect(updatedProtocols).to.not.be.empty()
|
|
expect(updatedProtocols).to.include(updatedProtocol)
|
|
|
|
// should have new address
|
|
const updatedAddresses = await remoteComponents.getPeerStore().addressBook.get(localComponents.getPeerId())
|
|
expect(updatedAddresses.map(a => {
|
|
return {
|
|
multiaddr: a.multiaddr.toString(),
|
|
isCertified: a.isCertified
|
|
}
|
|
})).to.deep.equal([{
|
|
multiaddr: updatedAddress.toString(),
|
|
isCertified: false
|
|
}])
|
|
|
|
await localIdentify.stop()
|
|
await remoteIdentify.stop()
|
|
})
|
|
})
|
|
|
|
describe('libp2p.dialer.identifyService', () => {
|
|
let peerId: PeerId
|
|
let libp2p: Libp2pNode
|
|
let remoteLibp2p: Libp2pNode
|
|
const remoteAddr = MULTIADDRS_WEBSOCKETS[0]
|
|
|
|
before(async () => {
|
|
peerId = await createFromJSON(Peers[0])
|
|
})
|
|
|
|
afterEach(async () => {
|
|
sinon.restore()
|
|
|
|
if (libp2p != null) {
|
|
await libp2p.stop()
|
|
}
|
|
})
|
|
|
|
after(async () => {
|
|
if (remoteLibp2p != null) {
|
|
await remoteLibp2p.stop()
|
|
}
|
|
})
|
|
|
|
it('should run identify automatically after connecting', async () => {
|
|
libp2p = await createLibp2pNode(createBaseOptions({
|
|
peerId
|
|
}))
|
|
|
|
await libp2p.start()
|
|
|
|
if (libp2p.identifyService == null) {
|
|
throw new Error('Identity service was not configured')
|
|
}
|
|
|
|
const identityServiceIdentifySpy = sinon.spy(libp2p.identifyService, 'identify')
|
|
const peerStoreSpyConsumeRecord = sinon.spy(libp2p.peerStore.addressBook, 'consumePeerRecord')
|
|
const peerStoreSpyAdd = sinon.spy(libp2p.peerStore.addressBook, 'add')
|
|
|
|
const connection = await libp2p.dial(remoteAddr)
|
|
expect(connection).to.exist()
|
|
|
|
// Wait for peer store to be updated
|
|
// Dialer._createDialTarget (add), Identify (consume)
|
|
await pWaitFor(() => peerStoreSpyConsumeRecord.callCount === 1 && peerStoreSpyAdd.callCount === 1)
|
|
expect(identityServiceIdentifySpy.callCount).to.equal(1)
|
|
|
|
// The connection should have no open streams
|
|
await pWaitFor(() => connection.streams.length === 0)
|
|
await connection.close()
|
|
})
|
|
|
|
it('should store remote agent and protocol versions in metadataBook after connecting', async () => {
|
|
libp2p = await createLibp2pNode(createBaseOptions({
|
|
peerId
|
|
}))
|
|
|
|
await libp2p.start()
|
|
|
|
if (libp2p.identifyService == null) {
|
|
throw new Error('Identity service was not configured')
|
|
}
|
|
|
|
const identityServiceIdentifySpy = sinon.spy(libp2p.identifyService, 'identify')
|
|
const peerStoreSpyConsumeRecord = sinon.spy(libp2p.peerStore.addressBook, 'consumePeerRecord')
|
|
const peerStoreSpyAdd = sinon.spy(libp2p.peerStore.addressBook, 'add')
|
|
|
|
const connection = await libp2p.dial(remoteAddr)
|
|
expect(connection).to.exist()
|
|
|
|
// Wait for peer store to be updated
|
|
// Dialer._createDialTarget (add), Identify (consume)
|
|
await pWaitFor(() => peerStoreSpyConsumeRecord.callCount === 1 && peerStoreSpyAdd.callCount === 1)
|
|
expect(identityServiceIdentifySpy.callCount).to.equal(1)
|
|
|
|
// The connection should have no open streams
|
|
await pWaitFor(() => connection.streams.length === 0)
|
|
await connection.close()
|
|
|
|
const remotePeer = peerIdFromString(remoteAddr.getPeerId() ?? '')
|
|
|
|
const storedAgentVersion = await libp2p.peerStore.metadataBook.getValue(remotePeer, 'AgentVersion')
|
|
const storedProtocolVersion = await libp2p.peerStore.metadataBook.getValue(remotePeer, 'ProtocolVersion')
|
|
|
|
expect(storedAgentVersion).to.exist()
|
|
expect(storedProtocolVersion).to.exist()
|
|
})
|
|
|
|
it('should push protocol updates to an already connected peer', async () => {
|
|
libp2p = await createLibp2pNode(createBaseOptions({
|
|
peerId
|
|
}))
|
|
|
|
await libp2p.start()
|
|
|
|
if (libp2p.identifyService == null) {
|
|
throw new Error('Identity service was not configured')
|
|
}
|
|
|
|
const identityServiceIdentifySpy = sinon.spy(libp2p.identifyService, 'identify')
|
|
const identityServicePushSpy = sinon.spy(libp2p.identifyService, 'push')
|
|
const connectionPromise = pEvent(libp2p.connectionManager, 'peer:connect')
|
|
const connection = await libp2p.dial(remoteAddr)
|
|
|
|
expect(connection).to.exist()
|
|
// Wait for connection event to be emitted
|
|
await connectionPromise
|
|
|
|
// Wait for identify to finish
|
|
await identityServiceIdentifySpy.firstCall.returnValue
|
|
sinon.stub(libp2p, 'isStarted').returns(true)
|
|
|
|
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(() => identityServicePushSpy.callCount === 2)
|
|
|
|
// Verify the remote peer is notified of both changes
|
|
expect(identityServicePushSpy.callCount).to.equal(2)
|
|
|
|
for (const call of identityServicePushSpy.getCalls()) {
|
|
const [connections] = call.args
|
|
expect(connections.length).to.equal(1)
|
|
expect(connections[0].remotePeer.toString()).to.equal(remoteAddr.getPeerId())
|
|
await call.returnValue
|
|
}
|
|
|
|
// Verify the streams close
|
|
await pWaitFor(() => connection.streams.length === 0)
|
|
})
|
|
|
|
it('should store host data and protocol version into metadataBook', async () => {
|
|
const agentVersion = 'js-project/1.0.0'
|
|
|
|
libp2p = await createLibp2pNode(createBaseOptions({
|
|
peerId,
|
|
host: {
|
|
agentVersion
|
|
}
|
|
}))
|
|
|
|
await libp2p.start()
|
|
|
|
if (libp2p.identifyService == null) {
|
|
throw new Error('Identity service was not configured')
|
|
}
|
|
|
|
const storedAgentVersion = await libp2p.peerStore.metadataBook.getValue(peerId, 'AgentVersion')
|
|
const storedProtocolVersion = await libp2p.peerStore.metadataBook.getValue(peerId, 'ProtocolVersion')
|
|
|
|
expect(agentVersion).to.equal(uint8ArrayToString(storedAgentVersion ?? new Uint8Array()))
|
|
expect(storedProtocolVersion).to.exist()
|
|
})
|
|
|
|
it('should push multiaddr updates to an already connected peer', async () => {
|
|
libp2p = await createLibp2pNode(createBaseOptions({
|
|
peerId
|
|
}))
|
|
|
|
await libp2p.start()
|
|
|
|
if (libp2p.identifyService == null) {
|
|
throw new Error('Identity service was not configured')
|
|
}
|
|
|
|
const identityServiceIdentifySpy = sinon.spy(libp2p.identifyService, 'identify')
|
|
const identityServicePushSpy = sinon.spy(libp2p.identifyService, 'push')
|
|
const connectionPromise = pEvent(libp2p.connectionManager, 'peer:connect')
|
|
const connection = await libp2p.dial(remoteAddr)
|
|
|
|
expect(connection).to.exist()
|
|
// Wait for connection event to be emitted
|
|
await connectionPromise
|
|
|
|
// Wait for identify to finish
|
|
await identityServiceIdentifySpy.firstCall.returnValue
|
|
sinon.stub(libp2p, 'isStarted').returns(true)
|
|
|
|
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(() => identityServicePushSpy.callCount === 1)
|
|
|
|
// Verify the remote peer is notified of change
|
|
expect(identityServicePushSpy.callCount).to.equal(1)
|
|
for (const call of identityServicePushSpy.getCalls()) {
|
|
const [connections] = call.args
|
|
expect(connections.length).to.equal(1)
|
|
expect(connections[0].remotePeer.toString()).to.equal(remoteAddr.getPeerId())
|
|
await call.returnValue
|
|
}
|
|
|
|
// Verify the streams close
|
|
await pWaitFor(() => connection.streams.length === 0)
|
|
})
|
|
})
|
|
})
|