From b6acefb17457200e1599c8d457b66d882bbaa54c Mon Sep 17 00:00:00 2001 From: achingbrain Date: Fri, 4 Oct 2024 17:07:38 +0100 Subject: [PATCH] fix: track closest peers separately from main routing table The routing table is a balance trie where the path to the leaf node storing the contact is derived from the prefix of the kad id of the contact. This makes it great for starting a query because we can quickly find contacts in the kad-vicinity of the target, but it's less good for knowing peers that are in our kad-vicinity, since the bits that make us kad-close to another peer might not be in the prefix. Instead, use a peer distance list that we update whenever a peer successfully completes a `PING` operation. Periodically check this list and tag the closes peers with `KEEP_ALIVE` so we maintain connections to them, which will ensure we propagate changes in our PeerInfo to those peers most likely to answer `FIND_PEER` queries for our data. --- .../src/{peer-list => }/peer-distance-list.ts | 2 +- packages/kad-dht/src/peer-list/index.ts | 54 -------- packages/kad-dht/src/peer-routing/index.ts | 2 +- .../src/routing-table/closest-peers.ts | 113 +++++++++++++++ packages/kad-dht/src/routing-table/index.ts | 90 +++++------- .../kad-dht/src/routing-table/k-bucket.ts | 38 ++---- packages/kad-dht/test/closest-peers.spec.ts | 129 ++++++++++++++++++ .../kad-dht/test/peer-distance-list.spec.ts | 2 +- packages/kad-dht/test/peer-list.spec.ts | 26 ---- packages/kad-dht/test/routing-table.spec.ts | 65 +++------ 10 files changed, 308 insertions(+), 213 deletions(-) rename packages/kad-dht/src/{peer-list => }/peer-distance-list.ts (98%) delete mode 100644 packages/kad-dht/src/peer-list/index.ts create mode 100644 packages/kad-dht/src/routing-table/closest-peers.ts create mode 100644 packages/kad-dht/test/closest-peers.spec.ts delete mode 100644 packages/kad-dht/test/peer-list.spec.ts diff --git a/packages/kad-dht/src/peer-list/peer-distance-list.ts b/packages/kad-dht/src/peer-distance-list.ts similarity index 98% rename from packages/kad-dht/src/peer-list/peer-distance-list.ts rename to packages/kad-dht/src/peer-distance-list.ts index b90aac4a74..d9f2a35375 100644 --- a/packages/kad-dht/src/peer-list/peer-distance-list.ts +++ b/packages/kad-dht/src/peer-distance-list.ts @@ -1,6 +1,6 @@ import { xor as uint8ArrayXor } from 'uint8arrays/xor' import { xorCompare as uint8ArrayXorCompare } from 'uint8arrays/xor-compare' -import { convertPeerId } from '../utils.js' +import { convertPeerId } from './utils.js' import type { PeerId, PeerInfo } from '@libp2p/interface' interface PeerDistance { diff --git a/packages/kad-dht/src/peer-list/index.ts b/packages/kad-dht/src/peer-list/index.ts deleted file mode 100644 index 104d75a07d..0000000000 --- a/packages/kad-dht/src/peer-list/index.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { PeerId } from '@libp2p/interface' - -/** - * A list of unique peers. - */ -export class PeerList { - private readonly list: PeerId[] - - constructor () { - this.list = [] - } - - /** - * Add a new peer. Returns `true` if it was a new one - */ - push (peerId: PeerId): boolean { - if (!this.has(peerId)) { - this.list.push(peerId) - - return true - } - - return false - } - - /** - * Check if this PeerInfo is already in here - */ - has (peerId: PeerId): boolean { - const match = this.list.find((i) => i.equals(peerId)) - return Boolean(match) - } - - /** - * Get the list as an array - */ - toArray (): PeerId[] { - return this.list.slice() - } - - /** - * Remove the last element - */ - pop (): PeerId | undefined { - return this.list.pop() - } - - /** - * The length of the list - */ - get length (): number { - return this.list.length - } -} diff --git a/packages/kad-dht/src/peer-routing/index.ts b/packages/kad-dht/src/peer-routing/index.ts index 03f418e4bd..40deb58c6a 100644 --- a/packages/kad-dht/src/peer-routing/index.ts +++ b/packages/kad-dht/src/peer-routing/index.ts @@ -5,7 +5,7 @@ import { Libp2pRecord } from '@libp2p/record' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import { QueryError, InvalidRecordError } from '../errors.js' import { MessageType } from '../message/dht.js' -import { PeerDistanceList } from '../peer-list/peer-distance-list.js' +import { PeerDistanceList } from '../peer-distance-list.js' import { queryErrorEvent, finalPeerEvent, diff --git a/packages/kad-dht/src/routing-table/closest-peers.ts b/packages/kad-dht/src/routing-table/closest-peers.ts new file mode 100644 index 0000000000..b54838e8e1 --- /dev/null +++ b/packages/kad-dht/src/routing-table/closest-peers.ts @@ -0,0 +1,113 @@ +import { KEEP_ALIVE } from '@libp2p/interface' +import { PeerSet } from '@libp2p/peer-collections' +import { PeerDistanceList } from '../peer-distance-list.js' +import { convertPeerId } from '../utils.js' +import type { RoutingTable } from './index.js' +import type { ComponentLogger, Logger, Metrics, PeerId, PeerStore, Startable } from '@libp2p/interface' + +export const PEER_SET_SIZE = 20 +export const REFRESH_INTERVAL = 5000 +export const KAD_CLOSE_TAG_NAME = 'kad-close' +export const KAD_CLOSE_TAG_VALUE = 50 + +export interface ClosestPeersInit { + logPrefix: string + routingTable: RoutingTable + peerSetSize?: number + refreshInterval?: number + closeTagName?: string + closeTagValue?: number +} + +export interface ClosestPeersComponents { + peerId: PeerId + peerStore: PeerStore + metrics?: Metrics + logger: ComponentLogger +} + +/** + * Contains a list of the kad-closest peers encountered on the network. + * + * Once every few seconds, if the list has changed, it tags the closest peers. + */ +export class ClosestPeers implements Startable { + private readonly routingTable: RoutingTable + private readonly components: ClosestPeersComponents + private closestPeers: PeerSet + private newPeers?: PeerDistanceList + private readonly refreshInterval: number + private readonly peerSetSize: number + private timeout?: ReturnType + private readonly closeTagName: string + private readonly closeTagValue: number + private readonly log: Logger + + constructor (components: ClosestPeersComponents, init: ClosestPeersInit) { + this.components = components + this.log = components.logger.forComponent(`${init.logPrefix}:routing-table`) + this.routingTable = init.routingTable + this.refreshInterval = init.refreshInterval ?? REFRESH_INTERVAL + this.peerSetSize = init.peerSetSize ?? PEER_SET_SIZE + this.closeTagName = init.closeTagName ?? KAD_CLOSE_TAG_NAME + this.closeTagValue = init.closeTagValue ?? KAD_CLOSE_TAG_VALUE + + this.closestPeers = new PeerSet() + this.onPeerPing = this.onPeerPing.bind(this) + } + + async start (): Promise { + const targetKadId = await convertPeerId(this.components.peerId) + this.newPeers = new PeerDistanceList(targetKadId, this.peerSetSize) + this.routingTable.addEventListener('peer:ping', this.onPeerPing) + + this.timeout = setInterval(() => { + this.updatePeerTags() + .catch(err => { + this.log.error('error updating peer tags - %e', err) + }) + }, this.refreshInterval) + } + + stop (): void { + this.routingTable.removeEventListener('peer:ping', this.onPeerPing) + clearTimeout(this.timeout) + } + + onPeerPing (event: CustomEvent): void { + this.newPeers?.add({ id: event.detail, multiaddrs: [] }) + .catch(err => { + this.log.error('error adding peer to distance list - %e', err) + }) + } + + async updatePeerTags (): Promise { + const newClosest = new PeerSet(this.newPeers?.peers.map(peer => peer.id)) + const added = newClosest.difference(this.closestPeers) + const removed = this.closestPeers.difference(newClosest) + this.closestPeers = newClosest + + await Promise.all([ + ...[...added].map(async peerId => { + await this.components.peerStore.merge(peerId, { + tags: { + [this.closeTagName]: { + value: this.closeTagValue + }, + [KEEP_ALIVE]: { + value: 1 + } + } + }) + }), + ...[...removed].map(async peerId => { + await this.components.peerStore.merge(peerId, { + tags: { + [this.closeTagName]: undefined, + [KEEP_ALIVE]: undefined + } + }) + }) + ]) + } +} diff --git a/packages/kad-dht/src/routing-table/index.ts b/packages/kad-dht/src/routing-table/index.ts index 5295dc394b..44d34ba7c2 100644 --- a/packages/kad-dht/src/routing-table/index.ts +++ b/packages/kad-dht/src/routing-table/index.ts @@ -1,4 +1,4 @@ -import { KEEP_ALIVE, TypedEventEmitter, setMaxListeners } from '@libp2p/interface' +import { TypedEventEmitter, setMaxListeners, start, stop } from '@libp2p/interface' import { AdaptiveTimeout } from '@libp2p/utils/adaptive-timeout' import { PeerQueue } from '@libp2p/utils/peer-queue' import { anySignal } from 'any-signal' @@ -6,16 +6,15 @@ import parallel from 'it-parallel' import { EventTypes } from '../index.js' import { MessageType } from '../message/dht.js' import * as utils from '../utils.js' +import { ClosestPeers } from './closest-peers.js' import { KBucket, isLeafBucket } from './k-bucket.js' import type { Bucket, LeafBucket, Peer } from './k-bucket.js' import type { Network } from '../network.js' -import type { AbortOptions, ComponentLogger, CounterGroup, Logger, Metric, Metrics, PeerId, PeerStore, Startable, Stream, TagOptions } from '@libp2p/interface' +import type { AbortOptions, ComponentLogger, CounterGroup, Logger, Metric, Metrics, PeerId, PeerStore, Startable, Stream } from '@libp2p/interface' import type { AdaptiveTimeoutInit } from '@libp2p/utils/adaptive-timeout' -export const KAD_CLOSE_TAG_NAME = 'kad-close' -export const KAD_CLOSE_TAG_VALUE = 50 export const KBUCKET_SIZE = 20 -export const PREFIX_LENGTH = 7 +export const PREFIX_LENGTH = 8 export const PING_NEW_CONTACT_TIMEOUT = 2000 export const PING_NEW_CONTACT_CONCURRENCY = 20 export const PING_NEW_CONTACT_MAX_QUEUE_SIZE = 100 @@ -50,6 +49,8 @@ export interface RoutingTableInit { populateFromDatastoreOnStart?: boolean populateFromDatastoreLimit?: number lastPingThreshold?: number + closestPeerSetSize?: number + closestPeerSetRefreshInterval?: number } export interface RoutingTableComponents { @@ -62,6 +63,7 @@ export interface RoutingTableComponents { export interface RoutingTableEvents { 'peer:add': CustomEvent 'peer:remove': CustomEvent + 'peer:ping': CustomEvent } /** @@ -71,6 +73,7 @@ export class RoutingTable extends TypedEventEmitter implemen public kBucketSize: number public kb: KBucket public network: Network + private readonly closestPeerTagger: ClosestPeers private readonly log: Logger private readonly components: RoutingTableComponents private running: boolean @@ -83,8 +86,6 @@ export class RoutingTable extends TypedEventEmitter implemen private readonly protocol: string private readonly peerTagName: string private readonly peerTagValue: number - private readonly closeTagName: string - private readonly closeTagValue: number private readonly metrics?: { routingTableSize: Metric routingTableKadBucketTotal: Metric @@ -106,13 +107,10 @@ export class RoutingTable extends TypedEventEmitter implemen this.network = init.network this.peerTagName = init.peerTagName ?? KAD_PEER_TAG_NAME this.peerTagValue = init.peerTagValue ?? KAD_PEER_TAG_VALUE - this.closeTagName = init.closeTagName ?? KAD_CLOSE_TAG_NAME - this.closeTagValue = init.closeTagValue ?? KAD_CLOSE_TAG_VALUE this.pingOldContacts = this.pingOldContacts.bind(this) this.verifyNewContact = this.verifyNewContact.bind(this) this.peerAdded = this.peerAdded.bind(this) this.peerRemoved = this.peerRemoved.bind(this) - this.peerMoved = this.peerMoved.bind(this) this.populateFromDatastoreOnStart = init.populateFromDatastoreOnStart ?? POPULATE_FROM_DATASTORE_ON_START this.populateFromDatastoreLimit = init.populateFromDatastoreLimit ?? POPULATE_FROM_DATASTORE_LIMIT @@ -149,8 +147,16 @@ export class RoutingTable extends TypedEventEmitter implemen ping: this.pingOldContacts, verify: this.verifyNewContact, onAdd: this.peerAdded, - onRemove: this.peerRemoved, - onMove: this.peerMoved + onRemove: this.peerRemoved + }) + + this.closestPeerTagger = new ClosestPeers(this.components, { + logPrefix: init.logPrefix, + routingTable: this, + peerSetSize: init.closestPeerSetSize, + refreshInterval: init.closestPeerSetRefreshInterval, + closeTagName: init.closeTagName, + closeTagValue: init.closeTagValue }) if (this.components.metrics != null) { @@ -173,6 +179,7 @@ export class RoutingTable extends TypedEventEmitter implemen async start (): Promise { this.running = true + await start(this.closestPeerTagger) await this.kb.addSelfPeer(this.components.peerId) } @@ -205,9 +212,7 @@ export class RoutingTable extends TypedEventEmitter implemen this.log('failed to add peer %p to routing table, removing kad-dht peer tags - %e') await this.components.peerStore.merge(peer.id, { tags: { - [this.closeTagName]: undefined, - [this.peerTagName]: undefined, - [KEEP_ALIVE]: undefined + [this.peerTagName]: undefined } }) } @@ -222,29 +227,19 @@ export class RoutingTable extends TypedEventEmitter implemen async stop (): Promise { this.running = false + await stop(this.closestPeerTagger) this.pingOldContactQueue.abort() this.pingNewContactQueue.abort() } private async peerAdded (peer: Peer, bucket: LeafBucket): Promise { if (!this.components.peerId.equals(peer.peerId)) { - const tags: Record = { - [this.peerTagName]: { - value: this.peerTagValue - } - } - - if (bucket.containsSelf === true) { - tags[this.closeTagName] = { - value: this.closeTagValue - } - tags[KEEP_ALIVE] = { - value: 1 - } - } - await this.components.peerStore.merge(peer.peerId, { - tags + tags: { + [this.peerTagName]: { + value: this.peerTagValue + } + } }) } @@ -257,9 +252,7 @@ export class RoutingTable extends TypedEventEmitter implemen if (!this.components.peerId.equals(peer.peerId)) { await this.components.peerStore.merge(peer.peerId, { tags: { - [this.closeTagName]: undefined, - [this.peerTagName]: undefined, - [KEEP_ALIVE]: undefined + [this.peerTagName]: undefined } }) } @@ -269,30 +262,6 @@ export class RoutingTable extends TypedEventEmitter implemen this.safeDispatchEvent('peer:remove', { detail: peer.peerId }) } - private async peerMoved (peer: Peer, oldBucket: LeafBucket, newBucket: LeafBucket): Promise { - if (this.components.peerId.equals(peer.peerId)) { - return - } - - const tags: Record = { - [this.closeTagName]: undefined, - [KEEP_ALIVE]: undefined - } - - if (newBucket.containsSelf === true) { - tags[this.closeTagName] = { - value: this.closeTagValue - } - tags[KEEP_ALIVE] = { - value: 1 - } - } - - await this.components.peerStore.merge(peer.peerId, { - tags - }) - } - /** * Called on the `ping` event from `k-bucket` when a bucket is full * and cannot split. @@ -410,6 +379,11 @@ export class RoutingTable extends TypedEventEmitter implemen if (event.type === EventTypes.PEER_RESPONSE) { if (event.messageType === MessageType.PING) { this.log('contact %p ping ok', contact.peerId) + + this.safeDispatchEvent('peer:ping', { + detail: contact.peerId + }) + return true } diff --git a/packages/kad-dht/src/routing-table/k-bucket.ts b/packages/kad-dht/src/routing-table/k-bucket.ts index 6f9bf67770..68c934e7f2 100644 --- a/packages/kad-dht/src/routing-table/k-bucket.ts +++ b/packages/kad-dht/src/routing-table/k-bucket.ts @@ -3,7 +3,7 @@ import map from 'it-map' import { equals as uint8ArrayEquals } from 'uint8arrays/equals' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import { xor as uint8ArrayXor } from 'uint8arrays/xor' -import { PeerDistanceList } from '../peer-list/peer-distance-list.js' +import { PeerDistanceList } from '../peer-distance-list.js' import { convertPeerId } from '../utils.js' import { KBUCKET_SIZE, LAST_PING_THRESHOLD, PING_OLD_CONTACT_COUNT, PREFIX_LENGTH } from './index.js' import type { PeerId } from '@libp2p/interface' @@ -58,7 +58,7 @@ export interface KBucketOptions { * this value, the deeper the tree will grow and the slower the lookups will * be but the peers returned will be more specific to the key. * - * @default 32 + * @default 8 */ prefixLength?: number @@ -98,7 +98,6 @@ export interface KBucketOptions { verify: VerifyFunction onAdd?: OnAddCallback onRemove?: OnRemoveCallback - onMove?: OnMoveCallback } export interface Peer { @@ -110,7 +109,6 @@ export interface Peer { export interface LeafBucket { prefix: string depth: number - containsSelf?: boolean peers: Peer[] } @@ -156,7 +154,6 @@ export class KBucket { this.verify = options.verify this.onAdd = options.onAdd this.onRemove = options.onRemove - this.onMove = options.onMove this.addingPeerMap = new PeerMap() this.root = { @@ -172,9 +169,6 @@ export class KBucket { kadId: await convertPeerId(peerId), lastPing: Date.now() } - - const bucket = this._determineBucket(this.localPeer.kadId) - bucket.containsSelf = true } /** @@ -411,14 +405,13 @@ export class KBucket { */ private _determineBucket (kadId: Uint8Array): LeafBucket { const bitString = uint8ArrayToString(kadId, 'base2') - const prefix = bitString.substring(0, this.prefixLength) function findBucket (bucket: Bucket, bitIndex: number = 0): LeafBucket { if (isLeafBucket(bucket)) { return bucket } - const bit = prefix[bitIndex] + const bit = bitString[bitIndex] if (bit === '0') { return findBucket(bucket.left, bitIndex + 1) @@ -448,36 +441,23 @@ export class KBucket { * @param {any} bucket - bucket for splitting */ private async _split (bucket: LeafBucket): Promise { - const depth = bucket.prefix === '' ? bucket.depth : bucket.depth + 1 - // create child buckets const left: LeafBucket = { prefix: '0', - depth, + depth: bucket.depth + 1, peers: [] } const right: LeafBucket = { prefix: '1', - depth, + depth: bucket.depth + 1, peers: [] } - if (bucket.containsSelf === true && this.localPeer != null) { - delete bucket.containsSelf - const selfNodeBitString = uint8ArrayToString(this.localPeer.kadId, 'base2') - - if (selfNodeBitString[depth] === '0') { - left.containsSelf = true - } else { - right.containsSelf = true - } - } - // redistribute peers for (const peer of bucket.peers) { const bitString = uint8ArrayToString(peer.kadId, 'base2') - if (bitString[depth] === '0') { + if (bitString[bucket.depth] === '0') { left.peers.push(peer) await this.onMove?.(peer, bucket, left) } else { @@ -493,10 +473,14 @@ export class KBucket { function convertToInternalBucket (bucket: any, left: any, right: any): bucket is InternalBucket { delete bucket.peers - delete bucket.containsSelf bucket.left = left bucket.right = right + if (bucket.prefix === '') { + delete bucket.depth + delete bucket.prefix + } + return true } diff --git a/packages/kad-dht/test/closest-peers.spec.ts b/packages/kad-dht/test/closest-peers.spec.ts new file mode 100644 index 0000000000..d804fde54b --- /dev/null +++ b/packages/kad-dht/test/closest-peers.spec.ts @@ -0,0 +1,129 @@ +import { generateKeyPair } from '@libp2p/crypto/keys' +import { KEEP_ALIVE, start, stop } from '@libp2p/interface' +import { defaultLogger } from '@libp2p/logger' +import { peerIdFromPrivateKey, peerIdFromString } from '@libp2p/peer-id' +import { expect } from 'aegir/chai' +import delay from 'delay' +import { stubInterface } from 'sinon-ts' +import { xor } from 'uint8arrays/xor' +import { xorCompare } from 'uint8arrays/xor-compare' +import { ClosestPeers } from '../src/routing-table/closest-peers.js' +import { convertPeerId } from '../src/utils.js' +import type { RoutingTable } from '../src/routing-table/index.js' +import type { ComponentLogger, PeerId, PeerStore } from '@libp2p/interface' +import type { StubbedInstance } from 'sinon-ts' + +interface ClosestPeersComponents { + peerId: PeerId + logger: ComponentLogger + peerStore: StubbedInstance +} + +describe('closest-peers', () => { + let closestPeers: ClosestPeers + let components: ClosestPeersComponents + let routingTable: RoutingTable + let peers: Array<{ peerId: PeerId, kadId: Uint8Array, distance: Uint8Array }> + + beforeEach(async () => { + const nodePeerId = peerIdFromString('12D3KooWNq99a7DtUgvzyiHwvBX4m7TDLmn6nLZvJUzSt72wc1Zu') + const nodeKadId = await convertPeerId(nodePeerId) + + peers = [] + + for (let i = 0; i < 10; i++) { + const key = await generateKeyPair('Ed25519') + const peerId = peerIdFromPrivateKey(key) + const kadId = await convertPeerId(peerId) + const distance = xor(kadId, nodeKadId) + + peers.push({ + peerId, + kadId, + distance + }) + } + + peers.sort((a, b) => xorCompare(a.distance, b.distance)) + + routingTable = stubInterface() + + components = { + peerId: nodePeerId, + logger: defaultLogger(), + peerStore: stubInterface() + } + + closestPeers = new ClosestPeers(components, { + logPrefix: '', + routingTable, + peerSetSize: 2 + }) + + await start(closestPeers) + }) + + afterEach(async () => { + await stop(closestPeers) + }) + + it('should tag closest peers', async () => { + closestPeers.onPeerPing(new CustomEvent('peer:ping', { detail: peers[0].peerId })) + closestPeers.onPeerPing(new CustomEvent('peer:ping', { detail: peers[1].peerId })) + closestPeers.onPeerPing(new CustomEvent('peer:ping', { detail: peers[2].peerId })) + + // peers are added asynchronously + await delay(100) + + await closestPeers.updatePeerTags() + + assertTagged(peers[0].peerId, components.peerStore) + assertTagged(peers[1].peerId, components.peerStore) + + expect(components.peerStore.merge.calledWith(peers[2].peerId)).to.be.false() + }) + + it('should untag previous closest peers', async () => { + closestPeers.onPeerPing(new CustomEvent('peer:ping', { detail: peers[1].peerId })) + closestPeers.onPeerPing(new CustomEvent('peer:ping', { detail: peers[2].peerId })) + + // peers are added asynchronously + await delay(100) + await closestPeers.updatePeerTags() + + assertTagged(peers[1].peerId, components.peerStore) + assertTagged(peers[2].peerId, components.peerStore) + + // a new peer is pinged that is closer than the previous ones + closestPeers.onPeerPing(new CustomEvent('peer:ping', { detail: peers[0].peerId })) + + // peers are added asynchronously + await delay(100) + await closestPeers.updatePeerTags() + + // kad-furthest peer should have been untagged + assertUnTagged(peers[2].peerId, components.peerStore) + }) +}) + +function assertTagged (peerId: PeerId, peerStore: StubbedInstance): void { + expect(peerStore.merge.calledWith(peerId, { + tags: { + 'kad-close': { + value: 50 + }, + [KEEP_ALIVE]: { + value: 1 + } + } + })).to.be.true() +} + +function assertUnTagged (peerId: PeerId, peerStore: StubbedInstance): void { + expect(peerStore.merge.calledWith(peerId, { + tags: { + 'kad-close': undefined, + [KEEP_ALIVE]: undefined + } + })).to.be.true() +} diff --git a/packages/kad-dht/test/peer-distance-list.spec.ts b/packages/kad-dht/test/peer-distance-list.spec.ts index 729aeffff5..35732ba43c 100644 --- a/packages/kad-dht/test/peer-distance-list.spec.ts +++ b/packages/kad-dht/test/peer-distance-list.spec.ts @@ -2,7 +2,7 @@ import { peerIdFromString } from '@libp2p/peer-id' import { expect } from 'aegir/chai' -import { PeerDistanceList } from '../src/peer-list/peer-distance-list.js' +import { PeerDistanceList } from '../src/peer-distance-list.js' import * as kadUtils from '../src/utils.js' describe('PeerDistanceList', () => { diff --git a/packages/kad-dht/test/peer-list.spec.ts b/packages/kad-dht/test/peer-list.spec.ts deleted file mode 100644 index e82710008c..0000000000 --- a/packages/kad-dht/test/peer-list.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-env mocha */ - -import { expect } from 'aegir/chai' -import { PeerList } from '../src/peer-list/index.js' -import { createPeerIds } from './utils/create-peer-id.js' -import type { PeerId } from '@libp2p/interface' - -describe('PeerList', () => { - let peers: PeerId[] - - before(async () => { - peers = await createPeerIds(3) - }) - - it('basics', () => { - const l = new PeerList() - - expect(l.push(peers[0])).to.eql(true) - expect(l.push(peers[0])).to.eql(false) - expect(l).to.have.length(1) - expect(l.push(peers[1])).to.eql(true) - expect(l.pop()).to.eql(peers[1]) - expect(l).to.have.length(1) - expect(l.toArray()).to.eql([peers[0]]) - }) -}) diff --git a/packages/kad-dht/test/routing-table.spec.ts b/packages/kad-dht/test/routing-table.spec.ts index 180e746580..b7aef27984 100644 --- a/packages/kad-dht/test/routing-table.spec.ts +++ b/packages/kad-dht/test/routing-table.spec.ts @@ -1,7 +1,7 @@ /* eslint-env mocha */ import { generateKeyPair } from '@libp2p/crypto/keys' -import { TypedEventEmitter, stop, start, KEEP_ALIVE } from '@libp2p/interface' +import { TypedEventEmitter, stop, start } from '@libp2p/interface' import { defaultLogger } from '@libp2p/logger' import { peerIdFromString, peerIdFromPrivateKey } from '@libp2p/peer-id' import { persistentPeerStore } from '@libp2p/peer-store' @@ -15,7 +15,7 @@ import { stubInterface, type StubbedInstance } from 'sinon-ts' import { PROTOCOL } from '../src/constants.js' import { MessageType } from '../src/message/dht.js' import { peerResponseEvent } from '../src/query/events.js' -import { KAD_CLOSE_TAG_NAME, KAD_PEER_TAG_NAME, KAD_PEER_TAG_VALUE, RoutingTable, type RoutingTableComponents } from '../src/routing-table/index.js' +import { KAD_PEER_TAG_NAME, KAD_PEER_TAG_VALUE, RoutingTable, type RoutingTableComponents } from '../src/routing-table/index.js' import { isLeafBucket } from '../src/routing-table/k-bucket.js' import * as kadUtils from '../src/utils.js' import { createPeerId, createPeerIds } from './utils/create-peer-id.js' @@ -98,71 +98,63 @@ describe('Routing Table', () => { const trie = collect(table.kb.root) expect(trie).to.deep.equal({ - prefix: '', - depth: 0, left: { prefix: '0', - depth: 0, + depth: 1, left: { prefix: '0', - depth: 1, + depth: 2, left: { prefix: '0', - depth: 2, + depth: 3, peers: [ 'QmYobx1VAHP7Mi88LcDvLeQoWcc1Aa2rynYHpdEPBqHZi3', // 00010 'QmYobx1VAHP7Mi88LcDvLeQoWcc1Aa2rynYHpdEPBqHZi7' // 00011 - ], - containsSelf: true + ] }, right: { prefix: '1', - depth: 2, + depth: 3, peers: [ 'QmYobx1VAHP7Mi88LcDvLeQoWcc1Aa2rynYHpdEPBqHZiA' // 00111 - ], - containsSelf: false + ] } }, right: { prefix: '1', - depth: 1, + depth: 2, peers: [ 'QmYobx1VAHP7Mi88LcDvLeQoWcc1Aa2rynYHpdEPBqHZiB' // 01000 - ], - containsSelf: false + ] } }, right: { prefix: '1', - depth: 0, + depth: 1, left: { prefix: '0', - depth: 1, + depth: 2, peers: [ 'QmYobx1VAHP7Mi88LcDvLeQoWcc1Aa2rynYHpdEPBqHZiE' // 10111 - ], - containsSelf: false + ] }, right: { prefix: '1', - depth: 1, + depth: 2, left: { prefix: '0', - depth: 2, + depth: 3, peers: [ 'QmYobx1VAHP7Mi88LcDvLeQoWcc1Aa2rynYHpdEPBqHZib' // 11001 - ], - containsSelf: false + ] }, right: { prefix: '1', - depth: 2, + depth: 3, peers: [ 'QmYobx1VAHP7Mi88LcDvLeQoWcc1Aa2rynYHpdEPBqHZiC', // 11111 'QmYobx1VAHP7Mi88LcDvLeQoWcc1Aa2rynYHpdEPBqHZiD' // 11110 - ], - containsSelf: false + ] } } } @@ -173,8 +165,7 @@ describe('Routing Table', () => { return { prefix: bucket.prefix, depth: bucket.depth, - peers: bucket.peers.map(p => p.peerId.toString()), - containsSelf: Boolean(bucket.containsSelf) + peers: bucket.peers.map(p => p.peerId.toString()) } } else { obj.prefix = bucket.prefix @@ -183,7 +174,7 @@ describe('Routing Table', () => { obj.right = collect(bucket.right, {}) } - return obj + return JSON.parse(JSON.stringify(obj)) } }) @@ -233,14 +224,6 @@ describe('Routing Table', () => { const tags = [...peer.tags.keys()] expect(tags).to.contain(KAD_PEER_TAG_NAME) - - if (bucket.containsSelf === true) { - expect(tags).to.contain(KAD_CLOSE_TAG_NAME) - expect(tags).to.contain(KEEP_ALIVE) - } else { - expect(tags).to.not.contain(KAD_CLOSE_TAG_NAME) - expect(tags).to.not.contain(KEEP_ALIVE) - } } } else { if (bucket.left != null) { @@ -271,8 +254,6 @@ describe('Routing Table', () => { const tags = [...peer.tags.keys()] expect(tags).to.not.contain(KAD_PEER_TAG_NAME) - expect(tags).to.not.contain(KAD_CLOSE_TAG_NAME) - expect(tags).to.not.contain(KEEP_ALIVE) }) it('emits peer:add event', async () => { @@ -456,8 +437,6 @@ describe('Routing Table', () => { // current close peer should be marked close const closePeerData = await components.peerStore.get(peerIds[1]) - expect(closePeerData.tags.has(KAD_CLOSE_TAG_NAME)).to.be.true() - expect(closePeerData.tags.has(KEEP_ALIVE)).to.be.true() expect(closePeerData.tags.has(KAD_PEER_TAG_NAME)).to.be.true() const newPeer = peerIdFromString('QmYobx1VAHP7Mi88LcDvLeQoWcc1Aa2rynYHpdEPBqHZiA') // 00111 @@ -466,14 +445,10 @@ describe('Routing Table', () => { // new peer should be marked close const newPeerData = await components.peerStore.get(newPeer) - expect(newPeerData.tags.has(KAD_CLOSE_TAG_NAME)).to.be.true() - expect(newPeerData.tags.has(KEEP_ALIVE)).to.be.true() expect(newPeerData.tags.has(KAD_PEER_TAG_NAME)).to.be.true() // not close but not evicted from the table because it wasn't full yet const movedPeerData = await components.peerStore.get(peerIds[1]) - expect(movedPeerData.tags.has(KAD_CLOSE_TAG_NAME)).to.be.false() - expect(movedPeerData.tags.has(KEEP_ALIVE)).to.be.false() expect(movedPeerData.tags.has(KAD_PEER_TAG_NAME)).to.be.true() })