2019-11-07 12:11:50 +01:00
|
|
|
'use strict'
|
|
|
|
/* eslint-env mocha */
|
|
|
|
|
|
|
|
const chai = require('chai')
|
|
|
|
chai.use(require('dirty-chai'))
|
2019-11-29 16:41:08 +01:00
|
|
|
chai.use(require('chai-as-promised'))
|
2019-11-07 12:11:50 +01:00
|
|
|
const { expect } = chai
|
|
|
|
const sinon = require('sinon')
|
|
|
|
|
|
|
|
const delay = require('delay')
|
|
|
|
const PeerId = require('peer-id')
|
|
|
|
const PeerInfo = require('peer-info')
|
|
|
|
const duplexPair = require('it-pair/duplex')
|
|
|
|
const multiaddr = require('multiaddr')
|
|
|
|
|
|
|
|
const { codes: Errors } = require('../../src/errors')
|
|
|
|
const { IdentifyService, multicodecs } = require('../../src/identify')
|
|
|
|
const Peers = require('../fixtures/peers')
|
|
|
|
const Libp2p = require('../../src')
|
|
|
|
const baseOptions = require('../utils/base-options.browser')
|
|
|
|
|
|
|
|
const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser')
|
|
|
|
const remoteAddr = MULTIADDRS_WEBSOCKETS[0]
|
|
|
|
|
|
|
|
describe('Identify', () => {
|
|
|
|
let localPeer
|
|
|
|
let remotePeer
|
|
|
|
const protocols = new Map([
|
|
|
|
[multicodecs.IDENTIFY, () => {}],
|
|
|
|
[multicodecs.IDENTIFY_PUSH, () => {}]
|
|
|
|
])
|
|
|
|
|
|
|
|
before(async () => {
|
|
|
|
[localPeer, remotePeer] = (await Promise.all([
|
|
|
|
PeerId.createFromJSON(Peers[0]),
|
|
|
|
PeerId.createFromJSON(Peers[1])
|
|
|
|
])).map(id => new PeerInfo(id))
|
|
|
|
})
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
sinon.restore()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should be able to identify another peer', async () => {
|
|
|
|
const localIdentify = new IdentifyService({
|
|
|
|
peerInfo: localPeer,
|
|
|
|
protocols,
|
|
|
|
registrar: {
|
|
|
|
peerStore: {
|
2019-12-03 20:14:15 +01:00
|
|
|
replace: () => {}
|
2019-11-07 12:11:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
const remoteIdentify = new IdentifyService({
|
|
|
|
peerInfo: remotePeer,
|
|
|
|
protocols
|
|
|
|
})
|
|
|
|
|
|
|
|
const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
|
2019-12-03 20:14:15 +01:00
|
|
|
const localConnectionMock = { newStream: () => {}, remotePeer: remotePeer.id }
|
2019-11-07 12:11:50 +01:00
|
|
|
const remoteConnectionMock = { remoteAddr: observedAddr }
|
|
|
|
|
|
|
|
const [local, remote] = duplexPair()
|
|
|
|
sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY })
|
|
|
|
|
2019-12-03 20:14:15 +01:00
|
|
|
sinon.spy(localIdentify.registrar.peerStore, 'replace')
|
2019-11-07 12:11:50 +01:00
|
|
|
|
|
|
|
// Run identify
|
|
|
|
await Promise.all([
|
2019-12-03 20:14:15 +01:00
|
|
|
localIdentify.identify(localConnectionMock),
|
2019-11-07 12:11:50 +01:00
|
|
|
remoteIdentify.handleMessage({
|
|
|
|
connection: remoteConnectionMock,
|
|
|
|
stream: remote,
|
|
|
|
protocol: multicodecs.IDENTIFY
|
|
|
|
})
|
|
|
|
])
|
|
|
|
|
2019-12-03 20:14:15 +01:00
|
|
|
expect(localIdentify.registrar.peerStore.replace.callCount).to.equal(1)
|
2019-11-07 12:11:50 +01:00
|
|
|
// Validate the remote peer gets updated in the peer store
|
2019-12-03 20:14:15 +01:00
|
|
|
const call = localIdentify.registrar.peerStore.replace.firstCall
|
2019-11-07 12:11:50 +01:00
|
|
|
expect(call.args[0].id.bytes).to.equal(remotePeer.id.bytes)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should throw if identified peer is the wrong peer', async () => {
|
|
|
|
const localIdentify = new IdentifyService({
|
|
|
|
peerInfo: localPeer,
|
|
|
|
protocols
|
|
|
|
})
|
|
|
|
const remoteIdentify = new IdentifyService({
|
|
|
|
peerInfo: remotePeer,
|
|
|
|
protocols
|
|
|
|
})
|
|
|
|
|
|
|
|
const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
|
2019-12-03 20:14:15 +01:00
|
|
|
const localConnectionMock = { newStream: () => {}, remotePeer }
|
2019-11-07 12:11:50 +01:00
|
|
|
const remoteConnectionMock = { remoteAddr: observedAddr }
|
|
|
|
|
|
|
|
const [local, remote] = duplexPair()
|
|
|
|
sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY })
|
|
|
|
|
|
|
|
// Run identify
|
2019-11-29 16:41:08 +01:00
|
|
|
const identifyPromise = Promise.all([
|
|
|
|
localIdentify.identify(localConnectionMock, localPeer.id),
|
|
|
|
remoteIdentify.handleMessage({
|
|
|
|
connection: remoteConnectionMock,
|
|
|
|
stream: remote,
|
|
|
|
protocol: multicodecs.IDENTIFY
|
|
|
|
})
|
|
|
|
])
|
|
|
|
|
|
|
|
await expect(identifyPromise)
|
|
|
|
.to.eventually.be.rejected()
|
|
|
|
.and.to.have.property('code', Errors.ERR_INVALID_PEER)
|
2019-11-07 12:11:50 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
describe('push', () => {
|
|
|
|
it('should be able to push identify updates to another peer', async () => {
|
|
|
|
const localIdentify = new IdentifyService({
|
|
|
|
peerInfo: localPeer,
|
|
|
|
registrar: { getConnection: () => {} },
|
|
|
|
protocols: new Map([
|
|
|
|
[multicodecs.IDENTIFY],
|
|
|
|
[multicodecs.IDENTIFY_PUSH],
|
|
|
|
['/echo/1.0.0']
|
|
|
|
])
|
|
|
|
})
|
|
|
|
const remoteIdentify = new IdentifyService({
|
|
|
|
peerInfo: remotePeer,
|
|
|
|
registrar: {
|
|
|
|
peerStore: {
|
2019-12-03 20:14:15 +01:00
|
|
|
replace: () => {}
|
2019-11-07 12:11:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// Setup peer protocols and multiaddrs
|
|
|
|
const localProtocols = new Set([multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0'])
|
|
|
|
const listeningAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
|
|
|
|
sinon.stub(localPeer.multiaddrs, 'toArray').returns([listeningAddr])
|
|
|
|
sinon.stub(localPeer, 'protocols').value(localProtocols)
|
|
|
|
sinon.stub(remotePeer, 'protocols').value(new Set([multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH]))
|
|
|
|
|
|
|
|
const localConnectionMock = { newStream: () => {} }
|
|
|
|
const remoteConnectionMock = { remotePeer: localPeer.id }
|
|
|
|
|
|
|
|
const [local, remote] = duplexPair()
|
|
|
|
sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH })
|
|
|
|
|
|
|
|
sinon.spy(IdentifyService, 'updatePeerAddresses')
|
|
|
|
sinon.spy(IdentifyService, 'updatePeerProtocols')
|
2019-12-03 20:14:15 +01:00
|
|
|
sinon.spy(remoteIdentify.registrar.peerStore, 'replace')
|
2019-11-07 12:11:50 +01:00
|
|
|
|
|
|
|
// Run identify
|
|
|
|
await Promise.all([
|
|
|
|
localIdentify.push([localConnectionMock]),
|
|
|
|
remoteIdentify.handleMessage({
|
|
|
|
connection: remoteConnectionMock,
|
|
|
|
stream: remote,
|
|
|
|
protocol: multicodecs.IDENTIFY_PUSH
|
|
|
|
})
|
|
|
|
])
|
|
|
|
|
|
|
|
expect(IdentifyService.updatePeerAddresses.callCount).to.equal(1)
|
|
|
|
expect(IdentifyService.updatePeerProtocols.callCount).to.equal(1)
|
|
|
|
|
2019-12-03 20:14:15 +01:00
|
|
|
expect(remoteIdentify.registrar.peerStore.replace.callCount).to.equal(1)
|
|
|
|
const [peerInfo] = remoteIdentify.registrar.peerStore.replace.firstCall.args
|
2019-11-07 12:11:50 +01:00
|
|
|
expect(peerInfo.id.bytes).to.eql(localPeer.id.bytes)
|
|
|
|
expect(peerInfo.multiaddrs.toArray()).to.eql([listeningAddr])
|
|
|
|
expect(peerInfo.protocols).to.eql(localProtocols)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('libp2p.dialer.identifyService', () => {
|
|
|
|
let peerInfo
|
|
|
|
let libp2p
|
|
|
|
let remoteLibp2p
|
|
|
|
|
|
|
|
before(async () => {
|
|
|
|
const peerId = await PeerId.createFromJSON(Peers[0])
|
|
|
|
peerInfo = new PeerInfo(peerId)
|
|
|
|
})
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
sinon.restore()
|
|
|
|
libp2p && await libp2p.stop()
|
|
|
|
libp2p = null
|
|
|
|
})
|
|
|
|
|
|
|
|
after(async () => {
|
|
|
|
remoteLibp2p && await remoteLibp2p.stop()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should run identify automatically after connecting', async () => {
|
|
|
|
libp2p = new Libp2p({
|
|
|
|
...baseOptions,
|
|
|
|
peerInfo
|
|
|
|
})
|
|
|
|
|
2019-12-03 10:28:52 +01:00
|
|
|
sinon.spy(libp2p.identifyService, 'identify')
|
2019-12-03 20:14:15 +01:00
|
|
|
sinon.spy(libp2p.peerStore, 'replace')
|
2019-11-07 12:11:50 +01:00
|
|
|
|
|
|
|
const connection = await libp2p.dialer.connectToMultiaddr(remoteAddr)
|
|
|
|
expect(connection).to.exist()
|
|
|
|
// Wait for nextTick to trigger the identify call
|
|
|
|
await delay(1)
|
2019-12-03 10:28:52 +01:00
|
|
|
expect(libp2p.identifyService.identify.callCount).to.equal(1)
|
|
|
|
await libp2p.identifyService.identify.firstCall.returnValue
|
2019-11-07 12:11:50 +01:00
|
|
|
|
2019-12-03 20:14:15 +01:00
|
|
|
expect(libp2p.peerStore.replace.callCount).to.equal(1)
|
2019-11-07 12:11:50 +01:00
|
|
|
await connection.close()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should push protocol updates to an already connected peer', async () => {
|
|
|
|
libp2p = new Libp2p({
|
|
|
|
...baseOptions,
|
|
|
|
peerInfo
|
|
|
|
})
|
|
|
|
|
2019-12-03 10:28:52 +01:00
|
|
|
sinon.spy(libp2p.identifyService, 'identify')
|
|
|
|
sinon.spy(libp2p.identifyService, 'push')
|
2019-11-07 12:11:50 +01:00
|
|
|
sinon.spy(libp2p.peerStore, 'update')
|
|
|
|
|
|
|
|
const connection = await libp2p.dialer.connectToMultiaddr(remoteAddr)
|
|
|
|
expect(connection).to.exist()
|
|
|
|
// Wait for nextTick to trigger the identify call
|
|
|
|
await delay(1)
|
|
|
|
|
|
|
|
// Wait for identify to finish
|
2019-12-03 10:28:52 +01:00
|
|
|
await libp2p.identifyService.identify.firstCall.returnValue
|
2019-11-29 16:41:08 +01:00
|
|
|
sinon.stub(libp2p, 'isStarted').returns(true)
|
2019-11-07 12:11:50 +01:00
|
|
|
|
|
|
|
libp2p.handle('/echo/2.0.0', () => {})
|
|
|
|
libp2p.unhandle('/echo/2.0.0')
|
|
|
|
|
|
|
|
// Verify the remote peer is notified of both changes
|
2019-12-03 10:28:52 +01:00
|
|
|
expect(libp2p.identifyService.push.callCount).to.equal(2)
|
|
|
|
for (const call of libp2p.identifyService.push.getCalls()) {
|
2019-11-07 12:11:50 +01:00
|
|
|
const [connections] = call.args
|
|
|
|
expect(connections.length).to.equal(1)
|
|
|
|
expect(connections[0].remotePeer.toB58String()).to.equal(remoteAddr.getPeerId())
|
|
|
|
const results = await call.returnValue
|
|
|
|
expect(results.length).to.equal(1)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|