3
3
import { expect } from 'aegir/chai'
4
4
import random from 'lodash.random'
5
5
import sinon from 'sinon'
6
- import { RoutingTable } from '../src/routing-table/index.js'
6
+ import { KAD_CLOSE_TAG_NAME , KAD_CLOSE_TAG_VALUE , KBUCKET_SIZE , RoutingTable } from '../src/routing-table/index.js'
7
7
import * as kadUtils from '../src/utils.js'
8
8
import { createPeerId , createPeerIds } from './utils/create-peer-id.js'
9
9
import { PROTOCOL_DHT } from '../src/constants.js'
10
10
import { peerIdFromString } from '@libp2p/peer-id'
11
11
import { Components } from '@libp2p/components'
12
12
import { mockConnectionManager } from '@libp2p/interface-mocks'
13
+ import { PersistentPeerStore } from '@libp2p/peer-store'
14
+ import { MemoryDatastore } from 'datastore-core'
15
+ import { createEd25519PeerId } from '@libp2p/peer-id-factory'
16
+ import { sortClosestPeers } from './utils/sort-closest-peers.js'
17
+ import pWaitFor from 'p-wait-for'
18
+ import { pipe } from 'it-pipe'
19
+ import all from 'it-all'
20
+ import { PeerSet } from '@libp2p/peer-collections'
13
21
14
22
describe ( 'Routing Table' , ( ) => {
15
23
let table : RoutingTable
@@ -20,7 +28,9 @@ describe('Routing Table', () => {
20
28
21
29
components = new Components ( {
22
30
peerId : await createPeerId ( ) ,
23
- connectionManager : mockConnectionManager ( )
31
+ connectionManager : mockConnectionManager ( ) ,
32
+ datastore : new MemoryDatastore ( ) ,
33
+ peerStore : new PersistentPeerStore ( )
24
34
} )
25
35
26
36
table = new RoutingTable ( {
@@ -207,4 +217,90 @@ describe('Routing Table', () => {
207
217
// evicted the old peer
208
218
expect ( table . kb . get ( oldPeer . id ) ) . to . be . null ( )
209
219
} )
220
+
221
+ it ( 'tags newly found kad-close peers' , async ( ) => {
222
+ const remotePeer = await createEd25519PeerId ( )
223
+ const tagPeerSpy = sinon . spy ( components . getPeerStore ( ) , 'tagPeer' )
224
+
225
+ await table . add ( remotePeer )
226
+
227
+ expect ( tagPeerSpy . callCount ) . to . equal ( 0 , 'did not debounce call to peerStore.tagPeer' )
228
+
229
+ await pWaitFor ( ( ) => {
230
+ return tagPeerSpy . callCount === 1
231
+ } )
232
+
233
+ expect ( tagPeerSpy . callCount ) . to . equal ( 1 , 'did not tag kad-close peer' )
234
+ expect ( tagPeerSpy . getCall ( 0 ) . args [ 0 ] . toString ( ) ) . to . equal ( remotePeer . toString ( ) )
235
+ expect ( tagPeerSpy . getCall ( 0 ) . args [ 1 ] ) . to . equal ( KAD_CLOSE_TAG_NAME )
236
+ expect ( tagPeerSpy . getCall ( 0 ) . args [ 2 ] ) . to . have . property ( 'value' , KAD_CLOSE_TAG_VALUE )
237
+ } )
238
+
239
+ it ( 'removes tags from kad-close peers when closer peers are found' , async ( ) => {
240
+ async function getTaggedPeers ( ) : Promise < PeerSet > {
241
+ return new PeerSet ( await pipe (
242
+ await components . getPeerStore ( ) . all ( ) ,
243
+ async function * ( source ) {
244
+ for await ( const peer of source ) {
245
+ const tags = await components . getPeerStore ( ) . getTags ( peer . id )
246
+ const kadCloseTags = tags . filter ( tag => tag . name === KAD_CLOSE_TAG_NAME )
247
+
248
+ if ( kadCloseTags . length > 0 ) {
249
+ yield peer . id
250
+ }
251
+ }
252
+ } ,
253
+ async ( source ) => await all ( source )
254
+ ) )
255
+ }
256
+
257
+ const tagPeerSpy = sinon . spy ( components . getPeerStore ( ) , 'tagPeer' )
258
+ const unTagPeerSpy = sinon . spy ( components . getPeerStore ( ) , 'unTagPeer' )
259
+ const localNodeId = await kadUtils . convertPeerId ( components . getPeerId ( ) )
260
+ const sortedPeerList = await sortClosestPeers (
261
+ await Promise . all (
262
+ new Array ( KBUCKET_SIZE + 1 ) . fill ( 0 ) . map ( async ( ) => await createEd25519PeerId ( ) )
263
+ ) ,
264
+ localNodeId
265
+ )
266
+
267
+ // sort list furthest -> closest
268
+ sortedPeerList . reverse ( )
269
+
270
+ // fill the table up to the first kbucket size
271
+ for ( let i = 0 ; i < KBUCKET_SIZE ; i ++ ) {
272
+ await table . add ( sortedPeerList [ i ] )
273
+ }
274
+
275
+ // should have all added contacts in the root kbucket
276
+ expect ( table . kb ?. count ( ) ) . to . equal ( KBUCKET_SIZE , 'did not fill kbuckets' )
277
+ expect ( table . kb ?. root . contacts ) . to . have . lengthOf ( KBUCKET_SIZE , 'split root kbucket when we should not have' )
278
+ expect ( table . kb ?. root . left ) . to . be . null ( 'split root kbucket when we should not have' )
279
+ expect ( table . kb ?. root . right ) . to . be . null ( 'split root kbucket when we should not have' )
280
+
281
+ await pWaitFor ( ( ) => {
282
+ return tagPeerSpy . callCount === KBUCKET_SIZE
283
+ } )
284
+
285
+ // make sure we tagged all of the peers as kad-close
286
+ const taggedPeers = await getTaggedPeers ( )
287
+ expect ( taggedPeers . difference ( new PeerSet ( sortedPeerList . slice ( 0 , sortedPeerList . length - 1 ) ) ) ) . to . have . property ( 'size' , 0 )
288
+ tagPeerSpy . resetHistory ( )
289
+
290
+ // add a node that is closer than any added so far
291
+ await table . add ( sortedPeerList [ sortedPeerList . length - 1 ] )
292
+
293
+ expect ( table . kb ?. count ( ) ) . to . equal ( KBUCKET_SIZE + 1 , 'did not fill kbuckets' )
294
+ expect ( table . kb ?. root . left ) . to . not . be . null ( 'did not split root kbucket when we should have' )
295
+ expect ( table . kb ?. root . right ) . to . not . be . null ( 'did not split root kbucket when we should have' )
296
+
297
+ // wait for tag new peer and untag old peer
298
+ await pWaitFor ( ( ) => {
299
+ return tagPeerSpy . callCount === 1 && unTagPeerSpy . callCount === 1
300
+ } )
301
+
302
+ // should have updated list of tagged peers
303
+ const finalTaggedPeers = await getTaggedPeers ( )
304
+ expect ( finalTaggedPeers . difference ( new PeerSet ( sortedPeerList . slice ( 1 ) ) ) ) . to . have . property ( 'size' , 0 )
305
+ } )
210
306
} )
0 commit comments