Skip to content

Commit 0d55966

Browse files
achingbrainmaschad
andauthored
fix: return closest peers from FIND_NODE (#2499)
The `FIND_NODE` DHT operation should return the closest peers the node knows to the value. It does not need to return itself in the list because the calling peer already knows about it. Fixes #2450 --------- Co-authored-by: Chad Nehemiah <[email protected]>
1 parent 0d5d966 commit 0d55966

File tree

5 files changed

+55
-35
lines changed

5 files changed

+55
-35
lines changed

packages/kad-dht/src/rpc/handlers/find-node.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
import { CodeError } from '@libp2p/interface'
2-
import { protocols } from '@multiformats/multiaddr'
3-
import { equals as uint8ArrayEquals } from 'uint8arrays'
42
import { MessageType } from '../../message/dht.js'
53
import type { PeerInfoMapper } from '../../index.js'
64
import type { Message } from '../../message/dht.js'
75
import type { PeerRouting } from '../../peer-routing/index.js'
86
import type { DHTMessageHandler } from '../index.js'
97
import type { ComponentLogger, Logger, PeerId, PeerInfo } from '@libp2p/interface'
10-
import type { AddressManager } from '@libp2p/interface-internal'
118

129
export interface FindNodeHandlerInit {
1310
peerRouting: PeerRouting
@@ -17,23 +14,20 @@ export interface FindNodeHandlerInit {
1714

1815
export interface FindNodeHandlerComponents {
1916
peerId: PeerId
20-
addressManager: AddressManager
2117
logger: ComponentLogger
2218
}
2319

2420
export class FindNodeHandler implements DHTMessageHandler {
2521
private readonly peerRouting: PeerRouting
2622
private readonly peerInfoMapper: PeerInfoMapper
2723
private readonly peerId: PeerId
28-
private readonly addressManager: AddressManager
2924
private readonly log: Logger
3025

3126
constructor (components: FindNodeHandlerComponents, init: FindNodeHandlerInit) {
3227
const { peerRouting, logPrefix } = init
3328

3429
this.log = components.logger.forComponent(`${logPrefix}:rpc:handlers:find-node`)
3530
this.peerId = components.peerId
36-
this.addressManager = components.addressManager
3731
this.peerRouting = peerRouting
3832
this.peerInfoMapper = init.peerInfoMapper
3933
}
@@ -44,27 +38,19 @@ export class FindNodeHandler implements DHTMessageHandler {
4438
async handle (peerId: PeerId, msg: Message): Promise<Message> {
4539
this.log('incoming request from %p for peers closer to %b', peerId, msg.key)
4640

47-
let closer: PeerInfo[] = []
48-
4941
if (msg.key == null) {
5042
throw new CodeError('Invalid FIND_NODE message received - key was missing', 'ERR_INVALID_MESSAGE')
5143
}
5244

53-
if (uint8ArrayEquals(this.peerId.toBytes(), msg.key)) {
54-
closer = [{
55-
id: this.peerId,
56-
multiaddrs: this.addressManager.getAddresses().map(ma => ma.decapsulateCode(protocols('p2p').code))
57-
}]
58-
} else {
59-
closer = await this.peerRouting.getCloserPeersOffline(msg.key, peerId)
60-
}
45+
const closer: PeerInfo[] = await this.peerRouting.getCloserPeersOffline(msg.key, peerId)
6146

6247
const response: Message = {
6348
type: MessageType.FIND_NODE,
6449
clusterLevel: msg.clusterLevel,
6550
closer: closer
6651
.map(this.peerInfoMapper)
6752
.filter(({ multiaddrs }) => multiaddrs.length)
53+
.filter(({ id }) => !id.equals(this.peerId))
6854
.map(peerInfo => ({
6955
id: peerInfo.id.toBytes(),
7056
multiaddrs: peerInfo.multiaddrs.map(ma => ma.bytes)

packages/kad-dht/src/rpc/handlers/get-providers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class GetProvidersHandler implements DHTMessageHandler {
4040

4141
async handle (peerId: PeerId, msg: Message): Promise<Message> {
4242
if (msg.key == null) {
43-
throw new CodeError('Invalid FIND_NODE message received - key was missing', 'ERR_INVALID_MESSAGE')
43+
throw new CodeError('Invalid GET_PROVIDERS message received - key was missing', 'ERR_INVALID_MESSAGE')
4444
}
4545

4646
let cid

packages/kad-dht/test/kad-dht.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,37 @@ describe('KadDHT', () => {
799799

800800
expect(res).to.not.be.empty()
801801
})
802+
803+
it('should not include itself in getClosestPeers PEER_RESPONSE', async function () {
804+
this.timeout(240 * 1000)
805+
806+
const nDHTs = 30
807+
const dhts = await Promise.all(
808+
new Array(nDHTs).fill(0).map(async () => tdht.spawn())
809+
)
810+
811+
const connected: Array<Promise<void>> = []
812+
813+
for (let i = 0; i < dhts.length - 1; i++) {
814+
connected.push(tdht.connect(dhts[i], dhts[(i + 1) % dhts.length]))
815+
}
816+
817+
await Promise.all(connected)
818+
819+
const res = await all(dhts[1].getClosestPeers(dhts[2].components.peerId.toBytes()))
820+
expect(res).to.not.be.empty()
821+
822+
// no peer should include itself in the response, only other peers that it
823+
// knows who are closer
824+
for (const event of res) {
825+
if (event.name !== 'PEER_RESPONSE') {
826+
continue
827+
}
828+
829+
expect(event.closer.map(peer => peer.id.toString()))
830+
.to.not.include(event.from.toString())
831+
}
832+
})
802833
})
803834

804835
describe('errors', () => {

packages/kad-dht/test/rpc/handlers/find-node.spec.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,13 @@ import { peerIdFromBytes } from '@libp2p/peer-id'
55
import { multiaddr } from '@multiformats/multiaddr'
66
import { expect } from 'aegir/chai'
77
import Sinon, { type SinonStubbedInstance } from 'sinon'
8-
import { stubInterface } from 'sinon-ts'
98
import { type Message, MessageType } from '../../../src/message/dht.js'
109
import { PeerRouting } from '../../../src/peer-routing/index.js'
1110
import { FindNodeHandler } from '../../../src/rpc/handlers/find-node.js'
1211
import { passthroughMapper, removePrivateAddressesMapper, removePublicAddressesMapper } from '../../../src/utils.js'
1312
import { createPeerId } from '../../utils/create-peer-id.js'
1413
import type { DHTMessageHandler } from '../../../src/rpc/index.js'
1514
import type { PeerId } from '@libp2p/interface'
16-
import type { AddressManager } from '@libp2p/interface-internal'
17-
import type { StubbedInstance } from 'sinon-ts'
1815

1916
const T = MessageType.FIND_NODE
2017

@@ -24,18 +21,15 @@ describe('rpc - handlers - FindNode', () => {
2421
let targetPeer: PeerId
2522
let handler: DHTMessageHandler
2623
let peerRouting: SinonStubbedInstance<PeerRouting>
27-
let addressManager: StubbedInstance<AddressManager>
2824

2925
beforeEach(async () => {
3026
peerId = await createPeerId()
3127
sourcePeer = await createPeerId()
3228
targetPeer = await createPeerId()
3329
peerRouting = Sinon.createStubInstance(PeerRouting)
34-
addressManager = stubInterface<AddressManager>()
3530

3631
handler = new FindNodeHandler({
3732
peerId,
38-
addressManager,
3933
logger: defaultLogger()
4034
}, {
4135
peerRouting,
@@ -44,21 +38,33 @@ describe('rpc - handlers - FindNode', () => {
4438
})
4539
})
4640

47-
it('returns self, if asked for self', async () => {
41+
it('returns nodes close to self but excludes self, if asked for self', async () => {
4842
const msg: Message = {
4943
type: T,
5044
key: peerId.multihash.bytes,
5145
closer: [],
5246
providers: []
5347
}
5448

55-
addressManager.getAddresses.returns([
56-
multiaddr(`/ip4/127.0.0.1/tcp/4002/p2p/${peerId.toString()}`),
57-
multiaddr(`/ip4/192.168.1.5/tcp/4002/p2p/${peerId.toString()}`),
58-
multiaddr(`/ip4/221.4.67.0/tcp/4002/p2p/${peerId.toString()}`)
59-
])
49+
peerRouting.getCloserPeersOffline
50+
.withArgs(peerId.multihash.bytes, peerId)
51+
.resolves([{
52+
id: targetPeer, // closer peer
53+
multiaddrs: [
54+
multiaddr('/ip4/127.0.0.1/tcp/4002'),
55+
multiaddr('/ip4/192.168.1.5/tcp/4002'),
56+
multiaddr('/ip4/221.4.67.0/tcp/4002')
57+
]
58+
}, {
59+
id: peerId, // self peer
60+
multiaddrs: [
61+
multiaddr('/ip4/127.0.0.1/tcp/4002'),
62+
multiaddr('/ip4/192.168.1.5/tcp/4002'),
63+
multiaddr('/ip4/221.4.67.0/tcp/4002')
64+
]
65+
}])
6066

61-
const response = await handler.handle(sourcePeer, msg)
67+
const response = await handler.handle(peerId, msg)
6268

6369
if (response == null) {
6470
throw new Error('No response received from handler')
@@ -67,7 +73,8 @@ describe('rpc - handlers - FindNode', () => {
6773
expect(response.closer).to.have.length(1)
6874
const peer = response.closer[0]
6975

70-
expect(peerIdFromBytes(peer.id).toString()).to.equal(peerId.toString())
76+
expect(peerIdFromBytes(peer.id).toString()).to.equal(targetPeer.toString())
77+
expect(peer.multiaddrs).to.not.be.empty()
7178
})
7279

7380
it('returns closer peers', async () => {
@@ -138,7 +145,6 @@ describe('rpc - handlers - FindNode', () => {
138145

139146
handler = new FindNodeHandler({
140147
peerId,
141-
addressManager,
142148
logger: defaultLogger()
143149
}, {
144150
peerRouting,
@@ -181,7 +187,6 @@ describe('rpc - handlers - FindNode', () => {
181187

182188
handler = new FindNodeHandler({
183189
peerId,
184-
addressManager,
185190
logger: defaultLogger()
186191
}, {
187192
peerRouting,

packages/kad-dht/test/rpc/index.node.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import { passthroughMapper } from '../../src/utils.js'
2424
import { createPeerId } from '../utils/create-peer-id.js'
2525
import type { Validators } from '../../src/index.js'
2626
import type { Libp2pEvents, Connection, PeerId, PeerStore } from '@libp2p/interface'
27-
import type { AddressManager } from '@libp2p/interface-internal'
2827
import type { Datastore } from 'interface-datastore'
2928
import type { Duplex, Source } from 'it-stream-types'
3029

@@ -45,7 +44,6 @@ describe('rpc', () => {
4544
peerId,
4645
datastore,
4746
peerStore: stubInterface<PeerStore>(),
48-
addressManager: stubInterface<AddressManager>(),
4947
logger: defaultLogger()
5048
}
5149
components.peerStore = new PersistentPeerStore({

0 commit comments

Comments
 (0)