Skip to content

Commit 26a79b3

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

4 files changed

Lines changed: 42 additions & 17 deletions

File tree

libp2p/protocols/kademlia/find.nim

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -504,12 +504,16 @@ 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+
## Over-fetches by `excluded.len` so dropping self and `requester` still fills the reply.
509+
let excluded = [kad.switch.peerInfo.peerId.toKey(), requester.toKey()]
510+
let closestPeerKeys = kad.rtable
511+
.findClosest(target, kad.config.replication + excluded.len)
512+
.filterIt(it notin excluded)
513+
514+
return kad.switch.toPeers(
515+
closestPeerKeys[0 ..< min(kad.config.replication, closestPeerKeys.len)]
516+
)
513517

514518
method handleFindNode*(
515519
kad: KadDHT, stream: Stream, msg: Message
@@ -519,7 +523,8 @@ method handleFindNode*(
519523
return
520524

521525
let response = Message(
522-
msgType: Opt.some(MessageType.findNode), closerPeers: kad.findClosestPeers(msgKey)
526+
msgType: Opt.some(MessageType.findNode),
527+
closerPeers: kad.findClosestPeers(msgKey, stream.peerId),
523528
)
524529
let encoded = response.encode(kad.config.hideConnectionStatus)
525530
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: 27 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,28 @@ 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 for an unrelated target":
258+
let kads = setupKadSwitches(3)
259+
startAndDeferStop(kads)
260+
261+
await connectHub(kads[0], kads[1 ..^ 1])
262+
263+
# kads[0] knows kads[1] and kads[2], and both fit in a reply, so pre-filter
264+
# kads[1] would get itself back even though the target is unrelated to it.
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
254274
kads[2].rtable.selfId in closerPeersIds
255275

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

0 commit comments

Comments
 (0)