diff --git a/swarm/network/kademlia.go b/swarm/network/kademlia.go index 5422afc098..4d9dcd3624 100644 --- a/swarm/network/kademlia.go +++ b/swarm/network/kademlia.go @@ -168,15 +168,13 @@ func (k *Kademlia) Register(peers ...*BzzAddr) error { return nil } -// SuggestPeer returns a known peer for the lowest proximity bin for the -// lowest bincount below depth -// naturally if there is an empty row it returns a peer for that +// SuggestPeer returns an unconnected peer address as a peer suggestion for connection func (k *Kademlia) SuggestPeer() (a *BzzAddr, o int, want bool) { k.lock.Lock() defer k.lock.Unlock() minsize := k.MinBinSize radius := neighbourhoodRadiusForPot(k.conns, k.NeighbourhoodSize, k.base) - // if there is a callable neighbour within the current proxBin, connect + // finds a callable neighbour within the current neighbourhood radius // this makes sure nearest neighbour set is fully connected var ppo int k.addrs.EachNeighbour(k.base, Pof, func(val pot.Val, po int) bool { @@ -196,6 +194,7 @@ func (k *Kademlia) SuggestPeer() (a *BzzAddr, o int, want bool) { return a, 0, false } + // if there are no callable neighbours, find the undersaturated bins from shallow to deep var bpo []int prev := -1 k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool { @@ -210,13 +209,10 @@ func (k *Kademlia) SuggestPeer() (a *BzzAddr, o int, want bool) { } return size > 0 && po < radius }) - // all buckets are full, ie., minsize == k.MinBinSize + // all buckets are saturated, ie., minsize >= k.MinBinSize, no peer suggested if len(bpo) == 0 { return nil, 0, false } - // as long as we got candidate peers to connect to - // dont ask for new peers (want = false) - // try to select a candidate peer // find the first callable peer nxt := bpo[0] k.addrs.EachBin(k.base, Pof, nxt, func(po, _ int, f func(func(pot.Val) bool) bool) bool { @@ -398,15 +394,18 @@ func (k *Kademlia) eachAddr(base []byte, o int, f func(*BzzAddr, int) bool) { }) } +// NeighbourhoodDepth returns the depth for the pot, see depthForPot func (k *Kademlia) NeighbourhoodDepth() (depth int) { k.lock.RLock() defer k.lock.RUnlock() return depthForPot(k.conns, k.NeighbourhoodSize, k.base) } -// neighbourhoodRadiusForPot returns the proximity order that defines the distance of -// the nearest neighbour set with cardinality >= MinProxBinSize -// if there is altogether less than MinProxBinSize peers it returns 0 +// neighbourhoodRadiusForPot returns the neighbourhood radius of the kademlia +// neighbourhood radius encloses the nearest neighbour set with cardinality >= neighbourhoodSize +// i.e., neighbourhood radius is the deepest PO such that all bins not shallower altogether +// contain at least neighbourhoodSize connected peers +// if there is altogether less than neighbourhoodSize peers connected, it returns 0 // caller must hold the lock func neighbourhoodRadiusForPot(p *pot.Pot, neighbourhoodSize int, pivotAddr []byte) (depth int) { if p.Size() <= neighbourhoodSize { @@ -435,15 +434,19 @@ func neighbourhoodRadiusForPot(p *pot.Pot, neighbourhoodSize int, pivotAddr []by } // depthForPot returns the depth for the pot +// depth is the radius of the minimal extension of nearest neighbourhood that +// includes all empty PO bins. I.e., depth is the deepest PO such that +// - it is not deeper than neighbourhood radius +// - all bins shallower than depth are not empty // caller must hold the lock -func depthForPot(p *pot.Pot, minProxBinSize int, pivotAddr []byte) (depth int) { - if p.Size() <= minProxBinSize { +func depthForPot(p *pot.Pot, neighbourhoodSize int, pivotAddr []byte) (depth int) { + if p.Size() <= neighbourhoodSize { return 0 } // determining the depth is a two-step process - // first we find the proximity bin of the shallowest of the MinProxBinSize peers + // first we find the proximity bin of the shallowest of the neighbourhoodSize peers // the numeric value of depth cannot be higher than this - maxDepth := neighbourhoodRadiusForPot(p, minProxBinSize, pivotAddr) + maxDepth := neighbourhoodRadiusForPot(p, neighbourhoodSize, pivotAddr) // the second step is to test for empty bins in order from shallowest to deepest // if an empty bin is found, this will be the actual depth @@ -752,7 +755,7 @@ func (k *Kademlia) Healthy(pp *PeerPot) *Health { k.lock.RLock() defer k.lock.RUnlock() if len(pp.NNSet) < k.NeighbourhoodSize { - panic("wrong peerpot") + log.Warn("peerpot NNSet < NeighbourhoodSize") } gotnn, countgotnn, culpritsgotnn := k.connectedNeighbours(pp.NNSet) knownn, countknownn, culpritsknownn := k.knowNeighbours(pp.NNSet) diff --git a/swarm/network/kademlia_test.go b/swarm/network/kademlia_test.go index 32f2e914f0..a096e7b44d 100644 --- a/swarm/network/kademlia_test.go +++ b/swarm/network/kademlia_test.go @@ -170,18 +170,18 @@ func TestHealthStrict(t *testing.T) { // no peers // unhealthy (and lonely) k := newTestKademlia("11111111") - // assertHealth(t, k, false, false) + assertHealth(t, k, false, false) // know one peer but not connected // unhealthy Register(k, "11100000") log.Trace(k.String()) - // assertHealth(t, k, false, false) + assertHealth(t, k, false, false) // know one peer and connected // healthy On(k, "11100000") - // assertHealth(t, k, true, false) + assertHealth(t, k, true, false) // know two peers, only one connected // unhealthy diff --git a/swarm/network/simulations/discovery/discovery_test.go b/swarm/network/simulations/discovery/discovery_test.go index d5435768ac..3daf9650d8 100644 --- a/swarm/network/simulations/discovery/discovery_test.go +++ b/swarm/network/simulations/discovery/discovery_test.go @@ -352,7 +352,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt } healthy := &network.Health{} addr := id.String() - ppmap := network.NewPeerPotMap(network.NewKadParams().MinProxBinSize, addrs) + ppmap := network.NewPeerPotMap(network.NewKadParams().NeighbourhoodSize, addrs) if err := client.Call(&healthy, "hive_healthy", ppmap[common.Bytes2Hex(id.Bytes())]); err != nil { return fmt.Errorf("error getting node health: %s", err) }