swarm/network: fixed dynamic test, repeat tests

This commit is contained in:
Fabio Barone 2019-03-22 16:17:01 -05:00
parent f3d7d684eb
commit df57c5875b
2 changed files with 141 additions and 66 deletions

View file

@ -18,18 +18,18 @@ package network
import ( import (
"bytes" "bytes"
"context"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand" "crypto/rand"
"fmt" "fmt"
"net" "net"
"strings"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/protocols" "github.com/ethereum/go-ethereum/p2p/protocols"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
p2ptest "github.com/ethereum/go-ethereum/p2p/testing" p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
"github.com/ethereum/go-ethereum/swarm/pot" "github.com/ethereum/go-ethereum/swarm/pot"
) )
@ -71,86 +71,102 @@ func TestDiscovery(t *testing.T) {
} }
} }
// TestSubpeersMsg tests that the correct set of peers is suggested // TestSubpeersMsg runs testSubpeersMsg multiple times
// to another peer with a given depth and kademlia
func TestSubpeersMsg(t *testing.T) { func TestSubpeersMsg(t *testing.T) {
repetitions := 10
for r := 0; r < repetitions; r++ {
t.Run(fmt.Sprintf("Test Run number %d", r), testSubpeersMsg)
}
}
// testSubpeersMsg tests that the correct set of peers is suggested
// to another peer with a given depth and kademlia
func testSubpeersMsg(t *testing.T) {
// construct ProtocolTester and hive // construct ProtocolTester and hive
params := NewHiveParams() params := NewHiveParams()
// setup // setup the hive
prvkey, err := crypto.GenerateKey() prvkey, err := crypto.GenerateKey()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
addr := PrivateKeyToBzzKey(prvkey) addr := PrivateKeyToBzzKey(prvkey)
to := NewKademlia(addr, NewKadParams()) to := NewKademlia(addr, NewKadParams())
hive := NewHive(params, to, nil) // hive // create the hive
hive := NewHive(params, to, nil)
numOfTestNodes := 12 // we will use a set of preconstructed addresses
var bzzAddrs []*BzzAddr
s, err := newBzzBaseTester(t, numOfTestNodes, prvkey, DiscoverySpec, hive.Run) // define a number of nodes for the test
nodeCount := 12
// for every of these nodes, add to the hive's connections...
for i := 0; i < nodeCount; i++ {
// ...create a BzzAddr and connect it in the hive (`hive.On(p)`)
a, err := registerBzzAddr(hive)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// also store the actual bzzAddr into the slice
bzzAddrs = append(bzzAddrs, a)
}
// the control node is the only one from the ProtocolTester // create a channel. We will wait later...
control := s.Nodes[numOfTestNodes-1] waitC := make(chan struct{})
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) // create a special bzzBaseTester in which we can associate `enode.ID` to the `bzzAddr` we created above
defer cancel() s, err := newPreconnectedBzzBaseTester(t, waitC, bzzAddrs, nodeCount, prvkey, DiscoverySpec, hive.Run)
if err != nil {
t.Fatal(err)
}
// start the hive // start the hive
hive.Start(s.Server) hive.Start(s.Server)
defer hive.Stop() defer hive.Stop()
// we need to wait until the control node is actually connected to our hive // ...so we wait here until all connections have been established
WAIT_PIVOT: <-waitC
for {
select { // choose a control node
case <-ctx.Done(): control := s.Nodes[0]
t.Fatal("Timed out waiting for the control node to connect")
case <-time.After(100 * time.Millisecond):
if len(hive.peers) == len(s.BzzAddrs) {
break WAIT_PIVOT
}
}
}
// get BzzAddr of the control // get BzzAddr of the control
controlBzz := s.BzzAddrs[control.ID()].Over() controlBzz := hive.peers[control.ID()].Over()
// build a control kademlia for the control node from the address pool
// we use this so we can identify the actual `controlDepth` of the control node
controlKad := NewKademlia(controlBzz, NewKadParams()) controlKad := NewKademlia(controlBzz, NewKadParams())
for _, p := range s.BzzAddrs { for _, p := range bzzAddrs {
if !bytes.Equal(p.Over(), controlBzz) { if !bytes.Equal(p.Over(), controlBzz) {
controlKad.On(NewPeer(&BzzPeer{nil, p, time.Now(), false}, controlKad)) controlKad.On(NewPeer(&BzzPeer{nil, p, time.Now(), false}, controlKad))
} }
} }
// to be fully correct, even the pivot's hive should be added
controlKad.On(NewPeer(&BzzPeer{nil, &BzzAddr{OAddr: hive.BaseAddr(), UAddr: hive.BaseAddr()}, time.Now(), false}, controlKad))
// now we can evaluate the depth of the control node, which we need...
controlDepth := controlKad.NeighbourhoodDepth() controlDepth := controlKad.NeighbourhoodDepth()
// now we need to identify which peers are expected // ...to identify which peers are expected to be advertized
// iterate the hive's connection and only add peers below testDepth // iterate the hive's connection and only add peers below controlDepth
var expectedPeers []*BzzAddr var expectedPeers []*BzzAddr
hive.EachConn(controlBzz, 255, func(p *Peer, po int) bool { hive.EachConn(controlBzz, 255, func(p *Peer, po int) bool {
if po < controlDepth { if po < controlDepth {
return false return false
} }
// don't add the control node itself to expectedPeers;
// the control node was not added as
if !bytes.Equal(p.BzzAddr.Over(), controlBzz) {
expectedPeers = append(expectedPeers, p.BzzAddr) expectedPeers = append(expectedPeers, p.BzzAddr)
}
return true return true
}) })
// this is the hive's depth, which will be sent first to the control node initiating the test exchanges
hiveDepth := hive.NeighbourhoodDepth() hiveDepth := hive.NeighbourhoodDepth()
// the test exchange is as follows: // if the controlDepth is 0, nothing will happen, so in this case artificially set it to 2
// 1. Trigger a subPeersMsg from control to our hive
// 2. Hive will respond with peersMsg with the set of expected peers
if controlDepth == 0 { if controlDepth == 0 {
controlDepth = 1 controlDepth = 2
} }
// the test exchange is as follows:
// 1. Wait for a `subPeersMsg` advertizing the hive's depth from our hive to our control node
// 2. Trigger a `suPeersMsg` from the control node advertizing its own depth
// 3. Hive will respond with peersMsg with the set of expected peers
err = s.TestExchanges(p2ptest.Exchange{ err = s.TestExchanges(p2ptest.Exchange{
Label: "incoming subPeersMsg", Label: "incoming subPeersMsg",
Expects: []p2ptest.Expect{ Expects: []p2ptest.Expect{
@ -195,27 +211,31 @@ WAIT_PIVOT:
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
close(waitC)
} }
// add the BzzAddr to the hive // add the BzzAddr to the hive
func registerBzzAddr(po int, hive *Hive, on bool) { func registerBzzAddr(hive *Hive) (*BzzAddr, error) {
a := pot.RandomAddressAt(pot.NewAddressFromBytes(hive.BaseAddr()), po) addr := pot.RandomAddress()
bzzAddr := &BzzAddr{OAddr: a.Bytes(), UAddr: a.Bytes()} pKey, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
if on { if err != nil {
// actually connect return nil, err
peer := newDiscPeer(bzzAddr, a.String(), hive)
hive.On(peer)
} else {
// only add to address book
hive.Register(bzzAddr)
} }
pubKey := pKey.PublicKey
nod := enode.NewV4(&pubKey, net.IPv4(127, 0, 0, 1), 0, 0)
bzzAddr := &BzzAddr{OAddr: addr.Bytes(), UAddr: []byte(nod.String())}
// actually connect
peer := newDiscPeer(bzzAddr, nod.ID(), hive)
hive.On(peer)
return bzzAddr, nil
} }
// as we are not creating a real node via the protocol, // as we are not creating a real node via the protocol,
// we need to create the discovery peer objects for the additional kademlia // we need to create the discovery peer objects for the additional kademlia
// nodes manually // nodes manually
func newDiscPeer(bzzAddr *BzzAddr, name string, hive *Hive) *Peer { func newDiscPeer(bzzAddr *BzzAddr, id enode.ID, hive *Hive) *Peer {
p2pPeer := p2p.NewPeer(adapters.RandomNodeConfig().ID, name, nil) p2pPeer := p2p.NewPeer(id, id.String(), nil)
return NewPeer(&BzzPeer{ return NewPeer(&BzzPeer{
Peer: protocols.NewPeer(p2pPeer, &dummyMsgRW{}, DiscoverySpec), Peer: protocols.NewPeer(p2pPeer, &dummyMsgRW{}, DiscoverySpec),
BzzAddr: bzzAddr, BzzAddr: bzzAddr,

View file

@ -21,6 +21,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"os" "os"
"sync"
"testing" "testing"
"time" "time"
@ -70,9 +71,9 @@ func HandshakeMsgExchange(lhs, rhs *HandshakeMsg, id enode.ID) []p2ptest.Exchang
} }
} }
func newBzzBaseTester(t *testing.T, n int, prvkey *ecdsa.PrivateKey, spec *protocols.Spec, run func(*BzzPeer) error) (*bzzTester, error) { // get some protocol entities
func getProtocolAttrs(run func(*BzzPeer) error) (map[string]chan bool, func(p *BzzPeer) error) {
cs := make(map[string]chan bool) cs := make(map[string]chan bool)
bzzAddrs := make(map[enode.ID]*BzzAddr)
srv := func(p *BzzPeer) error { srv := func(p *BzzPeer) error {
defer func() { defer func() {
@ -83,13 +84,11 @@ func newBzzBaseTester(t *testing.T, n int, prvkey *ecdsa.PrivateKey, spec *proto
return run(p) return run(p)
} }
protocol := func(p *p2p.Peer, rw p2p.MsgReadWriter) error { return cs, srv
bzzAddr := NewAddr(p.Node()) }
bzzAddrs[p.Node().ID()] = bzzAddr
return srv(&BzzPeer{Peer: protocols.NewPeer(p, rw, spec), BzzAddr: bzzAddr})
}
s := p2ptest.NewProtocolTester(prvkey, n, protocol) // get a BzzAddr for the hive
func getBzzAddr(prvkey *ecdsa.PrivateKey) (*BzzAddr, error) {
var record enr.Record var record enr.Record
bzzKey := PrivateKeyToBzzKey(prvkey) bzzKey := PrivateKeyToBzzKey(prvkey)
record.Set(NewENRAddrEntry(bzzKey)) record.Set(NewENRAddrEntry(bzzKey))
@ -101,7 +100,65 @@ func newBzzBaseTester(t *testing.T, n int, prvkey *ecdsa.PrivateKey, spec *proto
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to create enode: %v", err) return nil, fmt.Errorf("unable to create enode: %v", err)
} }
addr := getENRBzzAddr(nod) return getENRBzzAddr(nod), nil
}
// this is a custom version of the bzzBaseTester, which we can use to
// associate preconstructed BzzAddr addresses to the `enode.ID` created in the `ProtocolTester`
// this allows control over the address space in tests needing the protocol agnostic `ProtocolTester`
func newPreconnectedBzzBaseTester(t *testing.T, waitC chan struct{}, preConns []*BzzAddr, n int, prvkey *ecdsa.PrivateKey, spec *protocols.Spec, run func(*BzzPeer) error) (*bzzTester, error) {
var lock sync.Mutex
cs, srv := getProtocolAttrs(run)
// in this custom version of the protocol, we can associate the preconstructed BzzAddr to `enode.ID`s
protocol := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
lock.Lock()
// take one address from the "stack"
n := len(preConns) - 1
// associate it to bzzAddr, which is used further down to create the BzzPeer
bzzAddr := preConns[n]
// pop the address from the "stack"
preConns = preConns[:n]
// if there are no more addresses left, inform the caller
if len(preConns) == 0 {
waitC <- struct{}{}
}
lock.Unlock()
// run the protocol, create the protocol node
return srv(&BzzPeer{Peer: protocols.NewPeer(p, rw, spec), BzzAddr: bzzAddr})
}
s := p2ptest.NewProtocolTester(prvkey, n, protocol)
for _, node := range s.Nodes {
log.Warn("node", "node", node)
cs[node.ID().String()] = make(chan bool)
}
return &bzzTester{
// this version of the bzzTester assumes that the hive has been built outside of this function already
addr: nil, // ...so no need to create a new address
ProtocolTester: s,
cs: cs,
}, nil
}
// standard bzzTester
func newBzzBaseTester(t *testing.T, n int, prvkey *ecdsa.PrivateKey, spec *protocols.Spec, run func(*BzzPeer) error) (*bzzTester, error) {
cs, srv := getProtocolAttrs(run)
protocol := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
return srv(&BzzPeer{Peer: protocols.NewPeer(p, rw, spec), BzzAddr: NewAddr(p.Node())})
}
s := p2ptest.NewProtocolTester(prvkey, n, protocol)
addr, err := getBzzAddr(prvkey)
if err != nil {
t.Fatal(err)
}
for _, node := range s.Nodes { for _, node := range s.Nodes {
log.Warn("node", "node", node) log.Warn("node", "node", node)
@ -112,13 +169,11 @@ func newBzzBaseTester(t *testing.T, n int, prvkey *ecdsa.PrivateKey, spec *proto
addr: addr, addr: addr,
ProtocolTester: s, ProtocolTester: s,
cs: cs, cs: cs,
BzzAddrs: bzzAddrs,
}, nil }, nil
} }
type bzzTester struct { type bzzTester struct {
*p2ptest.ProtocolTester *p2ptest.ProtocolTester
BzzAddrs map[enode.ID]*BzzAddr
addr *BzzAddr addr *BzzAddr
cs map[string]chan bool cs map[string]chan bool
bzz *Bzz bzz *Bzz