From 16bbbc663c191d66e0726ae9bb02dde7b2d16576 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 5 Nov 2026 09:54:12 -0600 Subject: [PATCH 1/2] chore: stricter closest peer --- pkg/topology/kademlia/kademlia.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pkg/topology/kademlia/kademlia.go b/pkg/topology/kademlia/kademlia.go index 728ca83e491..68d5867f38f 100644 --- a/pkg/topology/kademlia/kademlia.go +++ b/pkg/topology/kademlia/kademlia.go @@ -1356,22 +1356,16 @@ func (k *Kad) ClosestPeer(addr swarm.Address, includeSelf bool, filter topology. closest := swarm.ZeroAddress - if includeSelf && k.reachability == p2p.ReachabilityStatusPublic { + if includeSelf { closest = k.base } - prox := swarm.Proximity(k.base.Bytes(), addr.Bytes()) - // iterate starting from bin 0 to the maximum bin err := k.EachConnectedPeerRev(func(peer swarm.Address, bin uint8) (bool, bool, error) { if swarm.ContainsAddress(skipPeers, peer) { return false, false, nil } - if bin > prox && !closest.IsZero() { - return true, false, nil - } - if closest.IsZero() { closest = peer return false, false, nil From 30c26c4f5a6b89db2d1027a0e29b092d0404fbb0 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 10 Nov 2026 15:13:07 -0600 Subject: [PATCH 2/2] chore: add missing test vectors --- pkg/topology/kademlia/kademlia_test.go | 40 ++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/pkg/topology/kademlia/kademlia_test.go b/pkg/topology/kademlia/kademlia_test.go index 35e2b122087..ec7b8d98c8a 100644 --- a/pkg/topology/kademlia/kademlia_test.go +++ b/pkg/topology/kademlia/kademlia_test.go @@ -973,6 +973,12 @@ func TestClosestPeer(t *testing.T) { { Address: swarm.MustParseHexAddress("6000000000000000000000000000000000000000000000000000000000000000"), // binary 0110 -> po 1 to base }, + { + Address: swarm.MustParseHexAddress("1000000000000000000000000000000000000000000000000000000000000000"), // binary 0001 -> po 3 to base + }, + { + Address: swarm.MustParseHexAddress("0800000000000000000000000000000000000000000000000000000000000000"), // binary 0000 1 -> po 4 to base + }, } disc := mock.NewDiscovery() @@ -1002,7 +1008,7 @@ func TestClosestPeer(t *testing.T) { addOne(t, beeCrypto.NewDefaultSigner(pk), kad, ab, v.Address) } - waitPeers(t, kad, 3) + waitPeers(t, kad, 5) // ClosestPeer only considers self when the node is publicly reachable. kad.UpdateReachability(p2p.ReachabilityStatusPublic) @@ -1011,8 +1017,10 @@ func TestClosestPeer(t *testing.T) { chunkAddress swarm.Address // chunk address to test expectedPeer int // points to the index of the connectedPeers slice. -1 means self (baseOverlay) includeSelf bool + skipPeers []int // indexes of connectedPeers to skip }{ { + // po(base, chunk) = 1, the peer in the deeper bin 3 must not stop the search chunkAddress: swarm.MustParseHexAddress("7000000000000000000000000000000000000000000000000000000000000000"), // 0111, wants peer 2 expectedPeer: 2, includeSelf: true, @@ -1047,13 +1055,39 @@ func TestClosestPeer(t *testing.T) { expectedPeer: -1, includeSelf: true, }, + { + chunkAddress: swarm.MustParseHexAddress("0000001000000000000000000000000000000000000000000000000000000000"), // 1000 want peer 4 + expectedPeer: 4, + includeSelf: false, + }, { chunkAddress: swarm.MustParseHexAddress("0000001000000000000000000000000000000000000000000000000000000000"), // 1000 want peer 1 - expectedPeer: 1, // smallest distance: 2894... + expectedPeer: 1, // smallest distance once peers 3 and 4 are skipped includeSelf: false, + skipPeers: []int{3, 4}, + }, + { + // po(base, chunk) = 2, the peers in the deeper bins 3 and 4 are skipped + chunkAddress: swarm.MustParseHexAddress("3000000000000000000000000000000000000000000000000000000000000000"), // 0011, want peer 2 + expectedPeer: 2, + includeSelf: false, + skipPeers: []int{3, 4}, + }, + { + // po(base, chunk) = 2, the peers in bins 0-1 are skipped, so only peers in bins + // deeper than po(base, chunk) are left. The peer in bin 4 is closer than the one + // in bin 3, so the search must not stop after the first deeper bin. + chunkAddress: swarm.MustParseHexAddress("2000000000000000000000000000000000000000000000000000000000000000"), // 0010, want peer 4 + expectedPeer: 4, + includeSelf: false, + skipPeers: []int{0, 1, 2}, }, } { - peer, err := kad.ClosestPeer(tc.chunkAddress, tc.includeSelf, topology.Select{}) + var skip []swarm.Address + for _, i := range tc.skipPeers { + skip = append(skip, connectedPeers[i].Address) + } + peer, err := kad.ClosestPeer(tc.chunkAddress, tc.includeSelf, topology.Select{}, skip...) if tc.expectedPeer == -1 { if !errors.Is(err, topology.ErrWantSelf) { t.Fatalf("wanted %v but got %v", topology.ErrWantSelf, err)