From cbf6d77a7f5c5a4e0694bce8d1a051a8040c1ce3 Mon Sep 17 00:00:00 2001 From: lash Date: Mon, 17 Dec 2018 20:45:05 +0100 Subject: [PATCH] swarm/network: WIP Create Healthy assertion tests --- swarm/network/hive.go | 29 ++++++--- swarm/network/kademlia.go | 15 +++-- swarm/network/kademlia_test.go | 64 ++++++++++++++++++- .../simulations/discovery/discovery_test.go | 16 +++-- 4 files changed, 102 insertions(+), 22 deletions(-) diff --git a/swarm/network/hive.go b/swarm/network/hive.go index 155a19c339..3d973fa255 100644 --- a/swarm/network/hive.go +++ b/swarm/network/hive.go @@ -17,6 +17,7 @@ package network import ( + "bytes" "fmt" "sync" "time" @@ -248,17 +249,27 @@ func (h *Hive) savePeers() error { // It evaluates the healthiness based on the addresses passed as argument // in relation to the base address of the hive instance the method is called on func (h *Hive) Healthy(addrs [][]byte) *Health { - k := NewKademlia(h.BaseAddr(), NewKadParams()) + //k := NewKademlia(h.BaseAddr(), NewKadParams()) + pivotK := *h.Kademlia + kads := []*Kademlia{&pivotK} for _, a := range addrs { - p := &Peer{ - BzzPeer: &BzzPeer{ - BzzAddr: &BzzAddr{ - OAddr: a, - }, - }, + if bytes.Equal(a, h.BaseAddr()) { + continue } - k.On(p) + kads = append(kads, NewKademlia(a, kadParamsFromInstance(h.Kademlia))) } - pp := NewPeerPotMap([]*Kademlia{k}) + pp := NewPeerPotMap(kads) return pp[common.Bytes2Hex(h.BaseAddr())].Healthy() } + +func kadParamsFromInstance(k *Kademlia) *KadParams { + return &KadParams{ + MaxProxDisplay: k.MaxProxDisplay, + MinProxBinSize: k.MinProxBinSize, + MinBinSize: k.MinBinSize, + MaxBinSize: k.MaxBinSize, + RetryInterval: k.RetryInterval, + RetryExponent: k.RetryExponent, + MaxRetries: k.MaxRetries, + } +} diff --git a/swarm/network/kademlia.go b/swarm/network/kademlia.go index 75725a0d8f..4f334f754f 100644 --- a/swarm/network/kademlia.go +++ b/swarm/network/kademlia.go @@ -411,7 +411,7 @@ func (k *Kademlia) eachConn(base []byte, o int, f func(*Peer, int, bool) bool) { } // EachAddr called with (base, po, f) is an iterator applying f to each known peer -// that has proximity order po or less as measured from the base +// that has proximity order o or less as measured from the base // if base is nil, kademlia base address is used // It returns peers in order deepest to shallowest func (k *Kademlia) EachAddr(base []byte, o int, f func(*BzzAddr, int, bool) bool) { @@ -444,6 +444,7 @@ func (k *Kademlia) NeighbourhoodDepth() (depth int) { // if there is altogether less than MinProxBinSize peers it returns 0 // caller must hold the lock func depthForPot(p *pot.Pot, minProxBinSize int, pivotAddr []byte) (depth int) { + log.Trace("pivot", "a", pivotAddr) if p.Size() <= minProxBinSize { return 0 } @@ -470,7 +471,8 @@ func depthForPot(p *pot.Pot, minProxBinSize int, pivotAddr []byte) (depth int) { } p.EachNeighbour(pivotAddr, Pof, f) - p.EachBin(pivotAddr, Pof, 0, func(po int, _ int, _ func(func(pot.Val, int) bool) bool) bool { + p.EachBin(pivotAddr, Pof, 0, func(po int, _ int, f func(func(pot.Val, int) bool) bool) bool { + log.Trace("eachbin", "addr", pivotAddr, "po", po) if po == depth { if maxDepth == depth { return false @@ -629,8 +631,8 @@ func NewPeerPotMap(kads []*Kademlia) map[string]*PeerPot { a := k.base // actual kademlia depth - depth := depthForPot(np, k.MinProxBinSize, a) - + depth := depthForPot(k.addrs, k.MinProxBinSize, a) + log.Trace("potmap", "k", k.BaseAddr(), "depth", depth) // all nn-peers var nns [][]byte @@ -652,7 +654,7 @@ func NewPeerPotMap(kads []*Kademlia) map[string]*PeerPot { return false }) - log.Trace(fmt.Sprintf("%x NNS: %s", kads[i].base[:4], LogAddrs(nns))) + log.Trace(fmt.Sprintf("%x PeerPotMap NNS: %s", kads[i].base[:4], LogAddrs(nns))) ppmap[common.Bytes2Hex(a)] = &PeerPot{ Kademlia: k, NNSet: nns, @@ -692,6 +694,7 @@ func (o *PeerPot) knowNeighbours() (got bool, n int, missing [][]byte) { // all bins (except self) are included (0 <= bin <= 255) depth := depthForPot(o.addrs, o.MinProxBinSize, o.base) o.eachAddr(nil, 255, func(p *BzzAddr, po int, nn bool) bool { + log.Info("eachaddr", "depth", depth, "po", po) if po < depth { return false } @@ -711,7 +714,7 @@ func (o *PeerPot) knowNeighbours() (got bool, n int, missing [][]byte) { if pm[pk] { gots++ } else { - log.Trace(fmt.Sprintf("%08x: known nearest neighbour %s not found", o.base, pk)) //(o.BaseAddr()[:4], pk[:8])) + log.Trace(fmt.Sprintf("%08x: known nearest neighbour %s not found", o.base, pk)) culprits = append(culprits, p) } } diff --git a/swarm/network/kademlia_test.go b/swarm/network/kademlia_test.go index c40a1c07cb..818e2892ae 100644 --- a/swarm/network/kademlia_test.go +++ b/swarm/network/kademlia_test.go @@ -41,12 +41,17 @@ func testKadPeerAddr(s string) *BzzAddr { return &BzzAddr{OAddr: a, UAddr: a} } -func newTestKademlia(b string) *Kademlia { +func newTestKademliaParams() *KadParams { params := NewKadParams() + // TODO why is this 1? params.MinBinSize = 1 params.MinProxBinSize = 2 + return params +} + +func newTestKademlia(b string) *Kademlia { base := pot.NewAddressFromString(b) - return NewKademlia(base, params) + return NewKademlia(base, newTestKademliaParams()) } func newTestKadPeer(k *Kademlia, s string, lightNode bool) *Peer { @@ -157,6 +162,58 @@ func TestNeighbourhoodDepth(t *testing.T) { testNum++ } +func TestHealth(t *testing.T) { + k := newTestKademlia("00000000") + assertHealth(t, k, false) + Register(k, "00001000") + log.Trace(k.String()) + assertHealth(t, k, false) + On(k, "00001000") + assertHealth(t, k, true) + Register(k, "00000100") + log.Trace(k.String()) + assertHealth(t, k, false) + On(k, "00000100") + assertHealth(t, k, true) + Register(k, "10000000") + log.Trace(k.String()) + assertHealth(t, k, false) + On(k, "10000000") + assertHealth(t, k, true) + Register(k, "00100000") + log.Trace(k.String()) + assertHealth(t, k, false) + On(k, "00100000") + assertHealth(t, k, true) + Register(k, "01000000") + log.Trace(k.String()) + assertHealth(t, k, false) + On(k, "01000000") + assertHealth(t, k, true) +} + +func assertHealth(t *testing.T, k *Kademlia, expectHealthy bool) { + kid := common.Bytes2Hex(k.BaseAddr()) + kads := []*Kademlia{k} + k.EachAddr(nil, 255, func(addr *BzzAddr, po int, _ bool) bool { + kads = append(kads, NewKademlia(addr.Address(), newTestKademliaParams())) + return true + }) + + pp := NewPeerPotMap(kads) + log.Trace("set", "pp", pp[kid].NNSet) + healthParams := pp[kid].Healthy() + + // definition of health, all conditions but be true: + // - we at least know one peer + // - we know all neighbors + // - we are connected to all known neighbors + health := healthParams.KnowNN && healthParams.GotNN && healthParams.CountKnowNN > 0 + if expectHealthy != health { + t.Fatalf("expected kademlia health %v, is %v\n%v", expectHealthy, health, k.String()) + } +} + func testSuggestPeer(k *Kademlia, expAddr string, expPo int, expWant bool) error { addr, o, want := k.SuggestPeer() log.Trace("suggestpeer return", "a", addr, "o", o, "want", want) @@ -179,6 +236,7 @@ func binStr(a *BzzAddr) string { return pot.ToBin(a.Address())[:8] } +// TODO explain why this bug occurred and how it should have been mitigated func TestSuggestPeerBug(t *testing.T) { // 2 row gap, unsaturated proxbin, no callables -> want PO 0 k := newTestKademlia("00000000") @@ -557,7 +615,7 @@ func testKademliaCase(t *testing.T, pivotAddr string, addrs ...string) { } h := pp.Healthy() - if !(h.GotNN && h.KnowNN) { + if !(h.GotNN && h.KnowNN && h.CountKnowNN > 0) { t.Fatalf("not healthy: %#v\n%v", h, k.String()) } } diff --git a/swarm/network/simulations/discovery/discovery_test.go b/swarm/network/simulations/discovery/discovery_test.go index 014c5e1f21..51e4430b70 100644 --- a/swarm/network/simulations/discovery/discovery_test.go +++ b/swarm/network/simulations/discovery/discovery_test.go @@ -31,6 +31,7 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" @@ -285,7 +286,7 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul if err := client.Call(&healthy, "hive_healthy", addrs); err != nil { return false, fmt.Errorf("error getting node health: %s", err) } - log.Debug(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v,\n\n%v", id, healthy.GotNN, healthy.KnowNN, healthy.Hive)) + log.Info(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v,\n\n%v", id, healthy.GotNN, healthy.KnowNN, healthy.Hive)) return healthy.KnowNN && healthy.GotNN, nil } @@ -399,13 +400,20 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt } healthy := &network.Health{} addr := id.String() - log.Error("before hive healthy call") if err := client.Call(&healthy, "hive_healthy", addrs); err != nil { return fmt.Errorf("error getting node health: %s", err) } - log.Info(fmt.Sprintf("NODE: %s, IS HEALTHY: %t", addr, healthy.GotNN && healthy.KnowNN)) - if !healthy.GotNN { + log.Info(fmt.Sprintf("NODE: %s, IS HEALTHY: %t", addr, healthy.GotNN && healthy.KnowNN && healthy.CountKnowNN > 0)) + var nodeStr string + if err := client.Call(&nodeStr, "hive_string"); err != nil { + return fmt.Errorf("error getting node string %s", err) + } + log.Info(nodeStr) + for _, a := range addrs { + log.Info(common.Bytes2Hex(a)) + } + if !healthy.GotNN || healthy.CountKnowNN == 0 { isHealthy = false break }