2019-12-01 22:54:59 +01:00
'use strict'
/* eslint-env mocha */
2020-10-15 15:31:33 +01:00
const { expect } = require ( 'aegir/utils/chai' )
2019-12-01 22:54:59 +01:00
const nock = require ( 'nock' )
const sinon = require ( 'sinon' )
2020-11-25 18:50:23 +01:00
const intoStream = require ( 'into-stream' )
2019-12-01 22:54:59 +01:00
2020-11-25 18:50:23 +01:00
const delay = require ( 'delay' )
2019-12-01 22:54:59 +01:00
const pDefer = require ( 'p-defer' )
2020-11-25 18:50:23 +01:00
const pWaitFor = require ( 'p-wait-for' )
2019-12-01 22:54:59 +01:00
const mergeOptions = require ( 'merge-options' )
2021-01-21 12:41:27 +00:00
const drain = require ( 'it-drain' )
const all = require ( 'it-all' )
2019-12-01 22:54:59 +01:00
2020-09-15 12:47:16 +02:00
const ipfsHttpClient = require ( 'ipfs-http-client' )
2019-12-01 22:54:59 +01:00
const DelegatedPeerRouter = require ( 'libp2p-delegated-peer-routing' )
2020-11-25 18:50:23 +01:00
const multiaddr = require ( 'multiaddr' )
const PeerId = require ( 'peer-id' )
2019-12-01 22:54:59 +01:00
const peerUtils = require ( '../utils/creators/peer' )
const { baseOptions , routingOptions } = require ( './utils' )
describe ( 'peer-routing' , ( ) => {
describe ( 'no routers' , ( ) => {
let node
before ( async ( ) => {
[ node ] = await peerUtils . createPeer ( {
config : baseOptions
} )
} )
it ( '.findPeer should return an error' , async ( ) => {
await expect ( node . peerRouting . findPeer ( 'a cid' ) )
. to . eventually . be . rejected ( )
. and . to . have . property ( 'code' , 'NO_ROUTERS_AVAILABLE' )
} )
2020-11-25 18:50:23 +01:00
it ( '.getClosestPeers should return an error' , async ( ) => {
try {
for await ( const _ of node . peerRouting . getClosestPeers ( 'a cid' ) ) { } // eslint-disable-line
throw new Error ( '.getClosestPeers should return an error' )
} catch ( err ) {
expect ( err ) . to . exist ( )
expect ( err . code ) . to . equal ( 'NO_ROUTERS_AVAILABLE' )
}
} )
2019-12-01 22:54:59 +01:00
} )
describe ( 'via dht router' , ( ) => {
const number = 5
let nodes
before ( async ( ) => {
nodes = await peerUtils . createPeer ( {
number ,
config : routingOptions
} )
// Ring dial
await Promise . all (
2020-04-14 14:05:30 +02:00
nodes . map ( ( peer , i ) => peer . dial ( nodes [ ( i + 1 ) % number ] . peerId ) )
2019-12-01 22:54:59 +01:00
)
} )
after ( ( ) => {
sinon . restore ( )
} )
after ( ( ) => Promise . all ( nodes . map ( ( n ) => n . stop ( ) ) ) )
it ( 'should use the nodes dht' , ( ) => {
const deferred = pDefer ( )
sinon . stub ( nodes [ 0 ] . _dht , 'findPeer' ) . callsFake ( ( ) => {
deferred . resolve ( )
2020-04-14 14:05:30 +02:00
return nodes [ 1 ] . peerId
2019-12-01 22:54:59 +01:00
} )
nodes [ 0 ] . peerRouting . findPeer ( )
return deferred . promise
} )
2020-11-25 18:50:23 +01:00
it ( 'should use the nodes dht to get the closest peers' , async ( ) => {
const deferred = pDefer ( )
2021-01-21 12:41:27 +00:00
const [ remotePeerId ] = await peerUtils . createPeerId ( { fixture : false } )
2020-11-25 18:50:23 +01:00
sinon . stub ( nodes [ 0 ] . _dht , 'getClosestPeers' ) . callsFake ( function * ( ) {
deferred . resolve ( )
2021-01-21 12:41:27 +00:00
yield {
id : remotePeerId ,
multiaddrs : [ ]
}
2020-11-25 18:50:23 +01:00
} )
await nodes [ 0 ] . peerRouting . getClosestPeers ( ) . next ( )
return deferred . promise
} )
2019-12-01 22:54:59 +01:00
} )
describe ( 'via delegate router' , ( ) => {
let node
let delegate
beforeEach ( async ( ) => {
2020-09-15 12:47:16 +02:00
delegate = new DelegatedPeerRouter ( ipfsHttpClient ( {
2019-12-01 22:54:59 +01:00
host : '0.0.0.0' ,
protocol : 'http' ,
port : 60197
2020-09-15 12:47:16 +02:00
} ) )
2019-12-01 22:54:59 +01:00
; [ node ] = await peerUtils . createPeer ( {
config : mergeOptions ( baseOptions , {
modules : {
peerRouting : [ delegate ]
} ,
config : {
dht : {
enabled : false
}
}
} )
} )
} )
afterEach ( ( ) => {
nock . cleanAll ( )
sinon . restore ( )
} )
afterEach ( ( ) => node . stop ( ) )
2021-02-11 14:37:34 +01:00
it ( 'should only have one router' , ( ) => {
expect ( node . peerRouting . _routers ) . to . have . lengthOf ( 1 )
} )
2019-12-01 22:54:59 +01:00
it ( 'should use the delegate router to find peers' , async ( ) => {
const deferred = pDefer ( )
2021-01-21 12:41:27 +00:00
const [ remotePeerId ] = await peerUtils . createPeerId ( { fixture : false } )
2019-12-01 22:54:59 +01:00
sinon . stub ( delegate , 'findPeer' ) . callsFake ( ( ) => {
deferred . resolve ( )
2021-01-21 12:41:27 +00:00
return {
id : remotePeerId ,
multiaddrs : [ ]
}
2019-12-01 22:54:59 +01:00
} )
await node . peerRouting . findPeer ( )
return deferred . promise
} )
2020-11-25 18:50:23 +01:00
it ( 'should use the delegate router to get the closest peers' , async ( ) => {
const deferred = pDefer ( )
2021-01-21 12:41:27 +00:00
const [ remotePeerId ] = await peerUtils . createPeerId ( { fixture : false } )
2020-11-25 18:50:23 +01:00
sinon . stub ( delegate , 'getClosestPeers' ) . callsFake ( function * ( ) {
deferred . resolve ( )
2021-01-21 12:41:27 +00:00
yield {
id : remotePeerId ,
multiaddrs : [ ]
}
2020-11-25 18:50:23 +01:00
} )
await node . peerRouting . getClosestPeers ( ) . next ( )
return deferred . promise
} )
2019-12-01 22:54:59 +01:00
it ( 'should be able to find a peer' , async ( ) => {
2021-01-21 12:41:27 +00:00
const peerKey = PeerId . createFromB58String ( 'QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL' )
2019-12-01 22:54:59 +01:00
const mockApi = nock ( 'http://0.0.0.0:60197' )
. post ( '/api/v0/dht/findpeer' )
2020-04-14 12:37:52 +02:00
. query ( true )
2019-12-01 22:54:59 +01:00
. reply ( 200 , ` {"Extra":"","ID":"some other id","Responses":null,"Type":0} \n {"Extra":"","ID":"","Responses":[{"Addrs":["/ip4/127.0.0.1/tcp/4001"],"ID":" ${ peerKey } "}],"Type":2} \n ` , [
'Content-Type' , 'application/json' ,
'X-Chunked-Output' , '1'
] )
2020-04-24 15:54:59 +02:00
const peer = await node . peerRouting . findPeer ( peerKey )
2019-12-01 22:54:59 +01:00
2020-04-24 15:54:59 +02:00
expect ( peer . id ) . to . equal ( peerKey )
2019-12-01 22:54:59 +01:00
expect ( mockApi . isDone ( ) ) . to . equal ( true )
} )
it ( 'should error when a peer cannot be found' , async ( ) => {
const peerKey = 'key of a peer not on the network'
const mockApi = nock ( 'http://0.0.0.0:60197' )
. post ( '/api/v0/dht/findpeer' )
2020-04-14 12:37:52 +02:00
. query ( true )
2019-12-01 22:54:59 +01:00
. reply ( 200 , '{"Extra":"","ID":"some other id","Responses":null,"Type":6}\n{"Extra":"","ID":"yet another id","Responses":null,"Type":0}\n{"Extra":"routing:not found","ID":"","Responses":null,"Type":3}\n' , [
'Content-Type' , 'application/json' ,
'X-Chunked-Output' , '1'
] )
await expect ( node . peerRouting . findPeer ( peerKey ) )
. to . eventually . be . rejected ( )
expect ( mockApi . isDone ( ) ) . to . equal ( true )
} )
it ( 'should handle errors from the api' , async ( ) => {
const peerKey = 'key of a peer not on the network'
const mockApi = nock ( 'http://0.0.0.0:60197' )
. post ( '/api/v0/dht/findpeer' )
2020-04-14 12:37:52 +02:00
. query ( true )
2019-12-01 22:54:59 +01:00
. reply ( 502 )
await expect ( node . peerRouting . findPeer ( peerKey ) )
. to . eventually . be . rejected ( )
expect ( mockApi . isDone ( ) ) . to . equal ( true )
} )
2020-11-25 18:50:23 +01:00
it ( 'should be able to get the closest peers' , async ( ) => {
const peerId = await PeerId . create ( { keyType : 'ed25519' } )
const closest1 = '12D3KooWLewYMMdGWAtuX852n4rgCWkK7EBn4CWbwwBzhsVoKxk3'
const closest2 = '12D3KooWDtoQbpKhtnWddfj72QmpFvvLDTsBLTFkjvgQm6cde2AK'
const mockApi = nock ( 'http://0.0.0.0:60197' )
. post ( '/api/v0/dht/query' )
. query ( true )
. reply ( 200 ,
( ) => intoStream ( [
` {"extra":"","id":" ${ closest1 } ","responses":[{"ID":" ${ closest1 } ","Addrs":["/ip4/127.0.0.1/tcp/63930","/ip4/127.0.0.1/tcp/63930"]}],"type":1} \n ` ,
` {"extra":"","id":" ${ closest2 } ","responses":[{"ID":" ${ closest2 } ","Addrs":["/ip4/127.0.0.1/tcp/63506","/ip4/127.0.0.1/tcp/63506"]}],"type":1} \n ` ,
` {"Extra":"","ID":" ${ closest2 } ","Responses":[],"Type":2} \n ` ,
` {"Extra":"","ID":" ${ closest1 } ","Responses":[],"Type":2} \n `
] ) ,
[
'Content-Type' , 'application/json' ,
'X-Chunked-Output' , '1'
] )
const closestPeers = [ ]
for await ( const peer of node . peerRouting . getClosestPeers ( peerId . id , { timeout : 1000 } ) ) {
closestPeers . push ( peer )
}
expect ( closestPeers ) . to . have . length ( 2 )
expect ( closestPeers [ 0 ] . id . toB58String ( ) ) . to . equal ( closest2 )
expect ( closestPeers [ 0 ] . multiaddrs ) . to . have . lengthOf ( 2 )
expect ( closestPeers [ 1 ] . id . toB58String ( ) ) . to . equal ( closest1 )
expect ( closestPeers [ 1 ] . multiaddrs ) . to . have . lengthOf ( 2 )
expect ( mockApi . isDone ( ) ) . to . equal ( true )
} )
it ( 'should handle errors when getting the closest peers' , async ( ) => {
const peerId = await PeerId . create ( { keyType : 'ed25519' } )
const mockApi = nock ( 'http://0.0.0.0:60197' )
. post ( '/api/v0/dht/query' )
. query ( true )
. reply ( 502 , 'Bad Gateway' , [
'X-Chunked-Output' , '1'
] )
try {
for await ( const _ of node . peerRouting . getClosestPeers ( peerId . id ) ) { } // eslint-disable-line
throw new Error ( 'should handle errors when getting the closest peers' )
} catch ( err ) {
expect ( err ) . to . exist ( )
}
expect ( mockApi . isDone ( ) ) . to . equal ( true )
} )
2019-12-01 22:54:59 +01:00
} )
describe ( 'via dht and delegate routers' , ( ) => {
let node
let delegate
beforeEach ( async ( ) => {
2020-09-15 12:47:16 +02:00
delegate = new DelegatedPeerRouter ( ipfsHttpClient ( {
2019-12-01 22:54:59 +01:00
host : '0.0.0.0' ,
protocol : 'http' ,
port : 60197
2020-09-15 12:47:16 +02:00
} ) )
2019-12-01 22:54:59 +01:00
; [ node ] = await peerUtils . createPeer ( {
config : mergeOptions ( routingOptions , {
modules : {
peerRouting : [ delegate ]
}
} )
} )
} )
afterEach ( ( ) => {
sinon . restore ( )
} )
afterEach ( ( ) => node . stop ( ) )
2021-01-21 12:41:27 +00:00
it ( 'should use the delegate if the dht fails to find the peer' , async ( ) => {
const [ remotePeerId ] = await peerUtils . createPeerId ( { fixture : false } )
const results = {
id : remotePeerId ,
multiaddrs : [ ]
}
2019-12-01 22:54:59 +01:00
2021-01-21 12:41:27 +00:00
sinon . stub ( node . _dht , 'findPeer' ) . callsFake ( ( ) => { } )
2019-12-01 22:54:59 +01:00
sinon . stub ( delegate , 'findPeer' ) . callsFake ( ( ) => {
2021-01-21 12:41:27 +00:00
return results
2019-12-01 22:54:59 +01:00
} )
2021-01-21 12:41:27 +00:00
const peer = await node . peerRouting . findPeer ( remotePeerId )
expect ( peer ) . to . eql ( results )
2019-12-01 22:54:59 +01:00
} )
2021-01-21 12:41:27 +00:00
it ( 'should not wait for the dht to return if the delegate does first' , async ( ) => {
const [ remotePeerId ] = await peerUtils . createPeerId ( { fixture : false } )
const results = {
id : remotePeerId ,
multiaddrs : [ ]
}
2019-12-01 22:54:59 +01:00
2021-01-21 12:41:27 +00:00
const defer = pDefer ( )
sinon . stub ( node . _dht , 'findPeer' ) . callsFake ( async ( ) => {
await defer . promise
} )
2019-12-01 22:54:59 +01:00
sinon . stub ( delegate , 'findPeer' ) . callsFake ( ( ) => {
return results
} )
2021-01-21 12:41:27 +00:00
const peer = await node . peerRouting . findPeer ( remotePeerId )
2019-12-01 22:54:59 +01:00
expect ( peer ) . to . eql ( results )
2021-01-21 12:41:27 +00:00
defer . resolve ( )
2019-12-01 22:54:59 +01:00
} )
2020-11-25 18:50:23 +01:00
2021-01-21 12:41:27 +00:00
it ( 'should not wait for the delegate to return if the dht does first' , async ( ) => {
const [ remotePeerId ] = await peerUtils . createPeerId ( { fixture : false } )
const results = {
id : remotePeerId ,
multiaddrs : [ ]
}
2020-11-25 18:50:23 +01:00
2021-01-21 12:41:27 +00:00
const defer = pDefer ( )
2020-11-25 18:50:23 +01:00
2021-01-21 12:41:27 +00:00
sinon . stub ( node . _dht , 'findPeer' ) . callsFake ( ( ) => {
return results
} )
sinon . stub ( delegate , 'findPeer' ) . callsFake ( async ( ) => {
await defer . promise
2020-11-25 18:50:23 +01:00
} )
2021-01-21 12:41:27 +00:00
const peer = await node . peerRouting . findPeer ( remotePeerId )
expect ( peer ) . to . eql ( results )
defer . resolve ( )
} )
it ( 'should store the addresses of the found peer' , async ( ) => {
const [ remotePeerId ] = await peerUtils . createPeerId ( { fixture : false } )
const results = {
id : remotePeerId ,
multiaddrs : [
multiaddr ( '/ip4/123.123.123.123/tcp/38982' )
]
2020-11-25 18:50:23 +01:00
}
2021-01-21 12:41:27 +00:00
const spy = sinon . spy ( node . peerStore . addressBook , 'add' )
sinon . stub ( node . _dht , 'findPeer' ) . callsFake ( ( ) => {
return results
} )
sinon . stub ( delegate , 'findPeer' ) . callsFake ( ( ) => { } )
await node . peerRouting . findPeer ( remotePeerId )
expect ( spy . calledWith ( results . id , results . multiaddrs ) ) . to . be . true ( )
2020-11-25 18:50:23 +01:00
} )
it ( 'should use the delegate if the dht fails to get the closest peer' , async ( ) => {
2021-01-21 12:41:27 +00:00
const [ remotePeerId ] = await peerUtils . createPeerId ( { fixture : false } )
const results = [ {
id : remotePeerId ,
multiaddrs : [ ]
} ]
2020-11-25 18:50:23 +01:00
sinon . stub ( node . _dht , 'getClosestPeers' ) . callsFake ( function * ( ) { } )
sinon . stub ( delegate , 'getClosestPeers' ) . callsFake ( function * ( ) {
yield results [ 0 ]
} )
2021-01-21 12:41:27 +00:00
const closest = await all ( node . peerRouting . getClosestPeers ( 'a cid' ) )
2020-11-25 18:50:23 +01:00
expect ( closest ) . to . have . length . above ( 0 )
expect ( closest ) . to . eql ( results )
} )
2021-01-21 12:41:27 +00:00
it ( 'should store the addresses of the closest peer' , async ( ) => {
const [ remotePeerId ] = await peerUtils . createPeerId ( { fixture : false } )
const result = {
id : remotePeerId ,
multiaddrs : [
multiaddr ( '/ip4/123.123.123.123/tcp/38982' )
]
}
const spy = sinon . spy ( node . peerStore . addressBook , 'add' )
sinon . stub ( node . _dht , 'getClosestPeers' ) . callsFake ( function * ( ) { } )
sinon . stub ( delegate , 'getClosestPeers' ) . callsFake ( function * ( ) {
yield result
} )
await drain ( node . peerRouting . getClosestPeers ( 'a cid' ) )
expect ( spy . calledWith ( result . id , result . multiaddrs ) ) . to . be . true ( )
} )
it ( 'should dedupe closest peers' , async ( ) => {
const [ remotePeerId ] = await peerUtils . createPeerId ( { fixture : false } )
const results = [ {
id : remotePeerId ,
multiaddrs : [
multiaddr ( '/ip4/123.123.123.123/tcp/38982' )
]
} ]
sinon . stub ( node . _dht , 'getClosestPeers' ) . callsFake ( function * ( ) {
yield * results
} )
sinon . stub ( delegate , 'getClosestPeers' ) . callsFake ( function * ( ) {
yield * results
} )
const peers = await all ( node . peerRouting . getClosestPeers ( 'a cid' ) )
expect ( peers ) . to . be . an ( 'array' ) . with . a . lengthOf ( 1 ) . that . deep . equals ( results )
} )
2020-11-25 18:50:23 +01:00
} )
describe ( 'peer routing refresh manager service' , ( ) => {
let node
let peerIds
before ( async ( ) => {
peerIds = await peerUtils . createPeerId ( { number : 2 } )
} )
afterEach ( ( ) => {
sinon . restore ( )
return node && node . stop ( )
} )
it ( 'should be enabled and start by default' , async ( ) => {
const results = [
{ id : peerIds [ 0 ] , multiaddrs : [ multiaddr ( '/ip4/30.0.0.1/tcp/2000' ) ] } ,
{ id : peerIds [ 1 ] , multiaddrs : [ multiaddr ( '/ip4/32.0.0.1/tcp/2000' ) ] }
]
; [ node ] = await peerUtils . createPeer ( {
config : mergeOptions ( routingOptions , {
peerRouting : {
refreshManager : {
bootDelay : 100
}
}
} ) ,
started : false
} )
sinon . spy ( node . peerStore . addressBook , 'add' )
sinon . stub ( node . _dht , 'getClosestPeers' ) . callsFake ( function * ( ) {
yield results [ 0 ]
yield results [ 1 ]
} )
await node . start ( )
await pWaitFor ( ( ) => node . _dht . getClosestPeers . callCount === 1 )
await pWaitFor ( ( ) => node . peerStore . addressBook . add . callCount === results . length )
const call0 = node . peerStore . addressBook . add . getCall ( 0 )
expect ( call0 . args [ 0 ] . equals ( results [ 0 ] . id ) )
call0 . args [ 1 ] . forEach ( ( m , index ) => {
expect ( m . equals ( results [ 0 ] . multiaddrs [ index ] ) )
} )
const call1 = node . peerStore . addressBook . add . getCall ( 1 )
expect ( call1 . args [ 0 ] . equals ( results [ 1 ] . id ) )
call0 . args [ 1 ] . forEach ( ( m , index ) => {
expect ( m . equals ( results [ 1 ] . multiaddrs [ index ] ) )
} )
} )
it ( 'should support being disabled' , async ( ) => {
[ node ] = await peerUtils . createPeer ( {
config : mergeOptions ( routingOptions , {
peerRouting : {
refreshManager : {
bootDelay : 100 ,
enabled : false
}
}
} ) ,
started : false
} )
sinon . stub ( node . _dht , 'getClosestPeers' ) . callsFake ( function * ( ) {
yield
throw new Error ( 'should not be called' )
} )
await node . start ( )
await delay ( 100 )
expect ( node . _dht . getClosestPeers . callCount === 0 )
} )
it ( 'should start and run recurrently on interval' , async ( ) => {
[ node ] = await peerUtils . createPeer ( {
config : mergeOptions ( routingOptions , {
peerRouting : {
refreshManager : {
interval : 500 ,
bootDelay : 200
}
}
} ) ,
started : false
} )
sinon . stub ( node . _dht , 'getClosestPeers' ) . callsFake ( function * ( ) {
yield { id : peerIds [ 0 ] , multiaddrs : [ multiaddr ( '/ip4/30.0.0.1/tcp/2000' ) ] }
} )
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 )
} )
2019-12-01 22:54:59 +01:00
} )
} )