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 (
"bytes"
"context"
"crypto/ecdsa"
"crypto/rand"
"fmt"
"net"
"strings"
"testing"
"time"
"github.com/ethereum/go-ethereum/crypto"
"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/simulations/adapters"
p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
"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
// to another peer with a given depth and kademlia
// TestSubpeersMsg runs testSubpeersMsg multiple times
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
params := NewHiveParams()
// setup
// setup the hive
prvkey, err := crypto.GenerateKey()
if err != nil {
t.Fatal(err)
}
addr := PrivateKeyToBzzKey(prvkey)
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
// 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 {
t.Fatal(err)
}
// also store the actual bzzAddr into the slice
bzzAddrs = append(bzzAddrs, a)
}
s, err := newBzzBaseTester(t, numOfTestNodes, prvkey, DiscoverySpec, hive.Run)
// create a channel. We will wait later...
waitC := make(chan struct{})
// create a special bzzBaseTester in which we can associate `enode.ID` to the `bzzAddr` we created above
s, err := newPreconnectedBzzBaseTester(t, waitC, bzzAddrs, nodeCount, prvkey, DiscoverySpec, hive.Run)
if err != nil {
t.Fatal(err)
}
// the control node is the only one from the ProtocolTester
control := s.Nodes[numOfTestNodes-1]
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// start the hive
hive.Start(s.Server)
defer hive.Stop()
// we need to wait until the control node is actually connected to our hive
WAIT_PIVOT:
for {
select {
case <-ctx.Done():
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
}
}
}
// ...so we wait here until all connections have been established
<-waitC
// choose a control node
control := s.Nodes[0]
// 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())
for _, p := range s.BzzAddrs {
for _, p := range bzzAddrs {
if !bytes.Equal(p.Over(), controlBzz) {
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()
// now we need to identify which peers are expected
// iterate the hive's connection and only add peers below testDepth
// ...to identify which peers are expected to be advertized
// iterate the hive's connection and only add peers below controlDepth
var expectedPeers []*BzzAddr
hive.EachConn(controlBzz, 255, func(p *Peer, po int) bool {
if po < controlDepth {
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
})
// this is the hive's depth, which will be sent first to the control node initiating the test exchanges
hiveDepth := hive.NeighbourhoodDepth()
// the test exchange is as follows:
// 1. Trigger a subPeersMsg from control to our hive
// 2. Hive will respond with peersMsg with the set of expected peers
// if the controlDepth is 0, nothing will happen, so in this case artificially set it to 2
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{
Label: "incoming subPeersMsg",
Expects: []p2ptest.Expect{
@ -195,27 +211,31 @@ WAIT_PIVOT:
if err != nil {
t.Fatal(err)
}
close(waitC)
}
// add the BzzAddr to the hive
func registerBzzAddr(po int, hive *Hive, on bool) {
a := pot.RandomAddressAt(pot.NewAddressFromBytes(hive.BaseAddr()), po)
bzzAddr := &BzzAddr{OAddr: a.Bytes(), UAddr: a.Bytes()}
if on {
// actually connect
peer := newDiscPeer(bzzAddr, a.String(), hive)
hive.On(peer)
} else {
// only add to address book
hive.Register(bzzAddr)
func registerBzzAddr(hive *Hive) (*BzzAddr, error) {
addr := pot.RandomAddress()
pKey, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
if err != nil {
return nil, err
}
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,
// we need to create the discovery peer objects for the additional kademlia
// nodes manually
func newDiscPeer(bzzAddr *BzzAddr, name string, hive *Hive) *Peer {
p2pPeer := p2p.NewPeer(adapters.RandomNodeConfig().ID, name, nil)
func newDiscPeer(bzzAddr *BzzAddr, id enode.ID, hive *Hive) *Peer {
p2pPeer := p2p.NewPeer(id, id.String(), nil)
return NewPeer(&BzzPeer{
Peer: protocols.NewPeer(p2pPeer, &dummyMsgRW{}, DiscoverySpec),
BzzAddr: bzzAddr,

View file

@ -21,6 +21,7 @@ import (
"flag"
"fmt"
"os"
"sync"
"testing"
"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)
bzzAddrs := make(map[enode.ID]*BzzAddr)
srv := func(p *BzzPeer) error {
defer func() {
@ -83,13 +84,11 @@ func newBzzBaseTester(t *testing.T, n int, prvkey *ecdsa.PrivateKey, spec *proto
return run(p)
}
protocol := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
bzzAddr := NewAddr(p.Node())
bzzAddrs[p.Node().ID()] = bzzAddr
return srv(&BzzPeer{Peer: protocols.NewPeer(p, rw, spec), BzzAddr: bzzAddr})
}
return cs, srv
}
s := p2ptest.NewProtocolTester(prvkey, n, protocol)
// get a BzzAddr for the hive
func getBzzAddr(prvkey *ecdsa.PrivateKey) (*BzzAddr, error) {
var record enr.Record
bzzKey := PrivateKeyToBzzKey(prvkey)
record.Set(NewENRAddrEntry(bzzKey))
@ -101,7 +100,65 @@ func newBzzBaseTester(t *testing.T, n int, prvkey *ecdsa.PrivateKey, spec *proto
if err != nil {
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 {
log.Warn("node", "node", node)
@ -112,16 +169,14 @@ func newBzzBaseTester(t *testing.T, n int, prvkey *ecdsa.PrivateKey, spec *proto
addr: addr,
ProtocolTester: s,
cs: cs,
BzzAddrs: bzzAddrs,
}, nil
}
type bzzTester struct {
*p2ptest.ProtocolTester
BzzAddrs map[enode.ID]*BzzAddr
addr *BzzAddr
cs map[string]chan bool
bzz *Bzz
addr *BzzAddr
cs map[string]chan bool
bzz *Bzz
}
func newBzz(addr *BzzAddr, lightNode bool) *Bzz {