Skip to content

Commit 105e565

Browse files
committed
fix(kad): filter out requester from closerPeers
1 parent 8a7b54e commit 105e565

4 files changed

Lines changed: 45 additions & 17 deletions

File tree

libp2p/protocols/kademlia/find.nim

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -504,12 +504,18 @@ proc findPeer*(
504504

505505
return ok(PeerInfo(peerId: target, addrs: kad.switch.peerStore[AddressBook][target]))
506506

507-
proc findClosestPeers*(kad: KadDHT, target: Key): seq[Peer] =
508-
let closestPeerKeys = kad.rtable.findClosest(target, kad.config.replication).filterIt(
509-
it != kad.switch.peerInfo.peerId.toKey()
510-
)
511-
512-
return kad.switch.toPeers(closestPeerKeys)
507+
proc findClosestPeers*(kad: KadDHT, target: Key, requester: PeerId): seq[Peer] =
508+
## Returns up to `replication` peers closest to `target`, excluding self and the
509+
## `requester`. Handing peers back their own id stalls lookups on clients that
510+
## don't filter it out, so ask for enough extra keys to still fill the reply.
511+
let excluded = @[kad.switch.peerInfo.peerId.toKey(), requester.toKey()]
512+
let closestPeerKeys = kad.rtable
513+
.findClosest(target, kad.config.replication + excluded.len)
514+
.filterIt(it notin excluded)
515+
516+
return kad.switch.toPeers(
517+
closestPeerKeys[0 ..< min(kad.config.replication, closestPeerKeys.len)]
518+
)
513519

514520
method handleFindNode*(
515521
kad: KadDHT, stream: Stream, msg: Message
@@ -519,7 +525,8 @@ method handleFindNode*(
519525
return
520526

521527
let response = Message(
522-
msgType: Opt.some(MessageType.findNode), closerPeers: kad.findClosestPeers(msgKey)
528+
msgType: Opt.some(MessageType.findNode),
529+
closerPeers: kad.findClosestPeers(msgKey, stream.peerId),
523530
)
524531
let encoded = response.encode(kad.config.hideConnectionStatus)
525532
kad_message_bytes_sent.inc(encoded.len.int64, labelValues = [$MessageType.findNode])

libp2p/protocols/kademlia/get.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ method handleGetValue*(
177177
let response = Message(
178178
msgType: Opt.some(MessageType.getValue),
179179
key: Opt.some(key),
180-
closerPeers: kad.findClosestPeers(key),
180+
closerPeers: kad.findClosestPeers(key, stream.peerId),
181181
)
182182
let encoded = response.encode(kad.config.hideConnectionStatus)
183183
kad_message_bytes_sent.inc(encoded.len.int64, labelValues = [$MessageType.getValue])
@@ -197,7 +197,7 @@ method handleGetValue*(
197197
timeReceived: Opt.some(entryRecord.time),
198198
)
199199
),
200-
closerPeers: kad.findClosestPeers(key),
200+
closerPeers: kad.findClosestPeers(key, stream.peerId),
201201
)
202202
let encoded = response.encode(kad.config.hideConnectionStatus)
203203
kad_message_bytes_sent.inc(encoded.len.int64, labelValues = [$MessageType.getValue])

libp2p/protocols/kademlia/provider.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ proc handleGetProviders*(
391391
let response = Message(
392392
msgType: Opt.some(MessageType.getProviders),
393393
key: msg.key,
394-
closerPeers: kad.findClosestPeers(msgKey),
394+
closerPeers: kad.findClosestPeers(msgKey, stream.peerId),
395395
providerPeers: providers.toSeq(),
396396
)
397397
let encoded = response.encode(kad.config.hideConnectionStatus)

tests/libp2p/kademlia/test_find.nim

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,11 @@ suite "KadDHT Find":
217217
check kads[0].hasKey(kads[2].rtable.selfId)
218218

219219
asyncTest "Find node with empty key returns closest peers":
220-
let kads = setupKadSwitches(2)
220+
let kads = setupKadSwitches(3)
221221
startAndDeferStop(kads)
222222

223-
await connect(kads[0], kads[1])
223+
# Setup: kads[0] <-> kads[1], kads[0] <-> kads[2]
224+
await connectHub(kads[0], kads[1 ..^ 1])
224225

225226
# Send FIND_NODE with empty key directly
226227
let emptyKey: Key = @[]
@@ -231,9 +232,9 @@ suite "KadDHT Find":
231232
check:
232233
response.msgType == MessageType.findNode
233234
response.closerPeers.len == 1
234-
response.closerPeers[0].id.get() == kads[1].rtable.selfId
235+
response.closerPeers[0].id.get() == kads[2].rtable.selfId
235236

236-
asyncTest "Find node for own PeerID returns closest peers":
237+
asyncTest "Find node for own PeerID excludes the requester":
237238
let kads = setupKadSwitches(3)
238239
startAndDeferStop(kads)
239240

@@ -248,9 +249,29 @@ suite "KadDHT Find":
248249
let closerPeersIds = response.closerPeers.mapIt(it.id.get())
249250
check:
250251
response.msgType == MessageType.findNode
251-
# kads[0] knows kads[1] and kads[2], should return both as closest peers
252-
response.closerPeers.len == 2
253-
kads[1].rtable.selfId in closerPeersIds
252+
# kads[0] knows kads[1] and kads[2], but must not hand kads[1] its own id back
253+
response.closerPeers.len == 1
254+
kads[1].rtable.selfId notin closerPeersIds
255+
kads[2].rtable.selfId in closerPeersIds
256+
257+
asyncTest "Find node excludes the requester and self from closerPeers":
258+
let kads = setupKadSwitches(3)
259+
startAndDeferStop(kads)
260+
261+
# Setup: kads[0] <-> kads[1], kads[0] <-> kads[2]
262+
await connectHub(kads[0], kads[1 ..^ 1])
263+
264+
# kads[1] asks kads[0] for a random target; kads[0]'s table holds kads[1] and kads[2]
265+
let response = (
266+
await kads[1].dispatchFindNode(
267+
kads[0].switch.peerInfo.peerId, randomPeerId().toKey()
268+
)
269+
).value()
270+
271+
let closerPeersIds = response.closerPeers.mapIt(it.id.get())
272+
check:
273+
kads[1].rtable.selfId notin closerPeersIds # the requester
274+
kads[0].rtable.selfId notin closerPeersIds # the responder itself
254275
kads[2].rtable.selfId in closerPeersIds
255276

256277
asyncTest "Find node with empty routing table returns empty result":

0 commit comments

Comments
 (0)