Skip to content

Commit 009c2a4

Browse files
fix(kademlia): neighborhood rebroadcast sends wrong peer lists
1 parent e0fb3a6 commit 009c2a4

3 files changed

Lines changed: 91 additions & 13 deletions

File tree

pkg/topology/kademlia/export_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
package kademlia
66

77
import (
8+
"context"
9+
810
"github.com/ethersphere/bee/v2/pkg/swarm"
911
"github.com/ethersphere/bee/v2/pkg/topology"
1012
im "github.com/ethersphere/bee/v2/pkg/topology/kademlia/internal/metrics"
@@ -24,6 +26,12 @@ func (k *Kad) MarkConnectedPeersSeen() error {
2426
return k.markConnectedPeersSeen()
2527
}
2628

29+
// RebroadcastNeighborhood runs the neighborhood gossip the manage loop
30+
// performs every fifteen minutes.
31+
func (k *Kad) RebroadcastNeighborhood(ctx context.Context) {
32+
k.rebroadcastNeighborhood(ctx)
33+
}
34+
2735
const (
2836
DefaultBitSuffixLength = defaultBitSuffixLength
2937
DefaultSaturationPeers = defaultSaturationPeers

pkg/topology/kademlia/kademlia.go

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"math/big"
1414
"math/rand"
1515
"path/filepath"
16+
"slices"
1617
"sync"
1718
"time"
1819

@@ -537,6 +538,26 @@ func (k *Kad) markConnectedPeersSeen() error {
537538
return k.addressBook.Seen(peers...)
538539
}
539540

541+
// rebroadcastNeighborhood tells each neighbor about the other neighbors.
542+
func (k *Kad) rebroadcastNeighborhood(ctx context.Context) {
543+
var neighbors []swarm.Address
544+
_ = k.connectedPeers.EachBin(func(addr swarm.Address, bin uint8) (stop bool, jumpToNext bool, err error) {
545+
if bin < k.neighborhoodDepth() {
546+
return true, false, nil
547+
}
548+
neighbors = append(neighbors, addr)
549+
return false, false, nil
550+
})
551+
for i, peer := range neighbors {
552+
// Concat allocates a new slice; appending to neighbors[:i] in place
553+
// would overwrite the entries the loop has yet to visit.
554+
others := slices.Concat(neighbors[:i], neighbors[i+1:])
555+
if err := k.discovery.BroadcastPeers(ctx, peer, others...); err != nil {
556+
k.logger.Debug("broadcast neighborhood failure", "peer_address", peer, "error", err)
557+
}
558+
}
559+
}
560+
540561
// manage is a forever loop that manages the connection to new peers
541562
// once they get added or once others leave.
542563
func (k *Kad) manage() {
@@ -617,19 +638,7 @@ func (k *Kad) manage() {
617638
case <-k.quit:
618639
return
619640
case <-time.After(15 * time.Minute):
620-
var neighbors []swarm.Address
621-
_ = k.connectedPeers.EachBin(func(addr swarm.Address, bin uint8) (stop bool, jumpToNext bool, err error) {
622-
if bin < k.neighborhoodDepth() {
623-
return true, false, nil
624-
}
625-
neighbors = append(neighbors, addr)
626-
return false, false, nil
627-
})
628-
for i, peer := range neighbors {
629-
if err := k.discovery.BroadcastPeers(ctx, peer, append(neighbors[:i], neighbors[i+1:]...)...); err != nil {
630-
k.logger.Debug("broadcast neighborhood failure", "peer_address", peer, "error", err)
631-
}
632-
}
641+
k.rebroadcastNeighborhood(ctx)
633642
}
634643
}
635644
})

pkg/topology/kademlia/kademlia_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,6 +1779,67 @@ func TestAnnounceNeighborhoodToNeighbor(t *testing.T) {
17791779
}
17801780
}
17811781

1782+
// TestRebroadcastNeighborhood checks that the periodic neighborhood gossip
1783+
// sends every neighbor exactly one message listing all the other neighbors.
1784+
func TestRebroadcastNeighborhood(t *testing.T) {
1785+
t.Parallel()
1786+
1787+
var (
1788+
conns int32
1789+
base, kad, ab, disc, signer = newTestKademlia(t, &conns, nil, kademlia.Options{})
1790+
)
1791+
1792+
// With a storage radius of zero every connected peer is a neighbor.
1793+
kad.SetStorageRadius(0)
1794+
1795+
if err := kad.Start(context.Background()); err != nil {
1796+
t.Fatal(err)
1797+
}
1798+
testutil.CleanupCloser(t, kad)
1799+
1800+
const n = 4
1801+
neighbors := make([]swarm.Address, n)
1802+
for i := range neighbors {
1803+
neighbors[i] = swarm.RandAddressAt(t, base, i)
1804+
connectOne(t, signer, kad, ab, neighbors[i], nil)
1805+
}
1806+
waitPeers(t, kad, n)
1807+
1808+
// Connecting peer j announces it to the j peers connected before it and
1809+
// introduces those j peers to it. Wait for all of that to land before
1810+
// clearing the recorder, so only the rebroadcast is measured.
1811+
wantAnnounces := 0
1812+
for j := 1; j < n; j++ {
1813+
wantAnnounces += j + 1
1814+
}
1815+
if err := spinlock.Wait(spinLockWaitTime, func() bool {
1816+
return disc.Broadcasts() == wantAnnounces
1817+
}); err != nil {
1818+
t.Fatalf("waiting for announce broadcasts: got %d, want %d", disc.Broadcasts(), wantAnnounces)
1819+
}
1820+
disc.Reset()
1821+
1822+
kad.RebroadcastNeighborhood(context.Background())
1823+
1824+
for _, addressee := range neighbors {
1825+
got, ok := disc.AddresseeRecords(addressee)
1826+
if !ok {
1827+
t.Fatalf("neighbor %s received no rebroadcast", addressee)
1828+
}
1829+
if len(got) != n-1 {
1830+
t.Fatalf("neighbor %s received %d peers, want %d", addressee, len(got), n-1)
1831+
}
1832+
if swarm.ContainsAddress(got, addressee) {
1833+
t.Fatalf("neighbor %s was told about itself", addressee)
1834+
}
1835+
for _, other := range neighbors {
1836+
if !other.Equal(addressee) && !swarm.ContainsAddress(got, other) {
1837+
t.Fatalf("neighbor %s was not told about %s", addressee, other)
1838+
}
1839+
}
1840+
}
1841+
}
1842+
17821843
func TestIteratorOpts(t *testing.T) {
17831844
t.Parallel()
17841845

0 commit comments

Comments
 (0)