swarm, p2p: Prerequities for ENR replacing handshake (#19275)

* swarm/api, swarm/network, p2p/simulations: Prerequisites for handshake remove

* swarm, p2p: Add full sim node configs for protocoltester

* swarm/network: Make stream package pass tests

* swarm/network: Extract peer and addr types out of protocol file

* p2p, swarm: Make p2p/protocols tests pass + rename types.go

* swarm/network: Deactivate ExecAdapter test until binary ENR prep

* swarm/api: Remove comments

* swarm/network: Uncomment bootnode record load
This commit is contained in:
lash 2019-03-15 11:27:17 +01:00 committed by Viktor Trón
parent df488975bd
commit 4b4f03ca37
14 changed files with 370 additions and 125 deletions

View File

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"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/enode"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
@ -144,8 +145,11 @@ func newProtocol(pp *p2ptest.TestPeerPool) func(*p2p.Peer, p2p.MsgReadWriter) er
} }
func protocolTester(pp *p2ptest.TestPeerPool) *p2ptest.ProtocolTester { func protocolTester(pp *p2ptest.TestPeerPool) *p2ptest.ProtocolTester {
conf := adapters.RandomNodeConfig() prvkey, err := crypto.GenerateKey()
return p2ptest.NewProtocolTester(conf.ID, 2, newProtocol(pp)) if err != nil {
panic(err)
}
return p2ptest.NewProtocolTester(prvkey, 2, newProtocol(pp))
} }
func protoHandshakeExchange(id enode.ID, proto *protoHandshake) []p2ptest.Exchange { func protoHandshakeExchange(id enode.ID, proto *protoHandshake) []p2ptest.Exchange {
@ -260,9 +264,12 @@ func TestProtocolHook(t *testing.T) {
return peer.Run(handle) return peer.Run(handle)
} }
conf := adapters.RandomNodeConfig() prvkey, err := crypto.GenerateKey()
tester := p2ptest.NewProtocolTester(conf.ID, 2, runFunc) if err != nil {
err := tester.TestExchanges(p2ptest.Exchange{ panic(err)
}
tester := p2ptest.NewProtocolTester(prvkey, 2, runFunc)
err = tester.TestExchanges(p2ptest.Exchange{
Expects: []p2ptest.Expect{ Expects: []p2ptest.Expect{
{ {
Code: 0, Code: 0,

View File

@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"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/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/p2p/simulations/pipes" "github.com/ethereum/go-ethereum/p2p/simulations/pipes"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
@ -71,8 +72,13 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
s.mtx.Lock() s.mtx.Lock()
defer s.mtx.Unlock() defer s.mtx.Unlock()
// check a node with the ID doesn't already exist
id := config.ID id := config.ID
// verify that the node has a private key in the config
if config.PrivateKey == nil {
return nil, fmt.Errorf("node is missing private key: %s", id)
}
// check a node with the ID doesn't already exist
if _, exists := s.nodes[id]; exists { if _, exists := s.nodes[id]; exists {
return nil, fmt.Errorf("node already exists: %s", id) return nil, fmt.Errorf("node already exists: %s", id)
} }
@ -87,6 +93,24 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
} }
} }
// dialer in simulations based on ENR records
// doesn't work unless we explicitly set localhost record
ip := enr.IP(net.IPv4(127, 0, 0, 1))
config.Record.Set(&ip)
tcpPort := enr.TCP(0)
config.Record.Set(&tcpPort)
err := enode.SignV4(&config.Record, config.PrivateKey)
if err != nil {
return nil, fmt.Errorf("unable to generate ENR: %v", err)
}
nod, err := enode.New(enode.V4ID{}, &config.Record)
if err != nil {
return nil, fmt.Errorf("unable to create enode: %v", err)
}
log.Trace("simnode new", "record", config.Record)
config.node = nod
n, err := node.New(&node.Config{ n, err := node.New(&node.Config{
P2P: p2p.Config{ P2P: p2p.Config{
PrivateKey: config.PrivateKey, PrivateKey: config.PrivateKey,

View File

@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"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/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
@ -99,6 +100,12 @@ type NodeConfig struct {
// services registered by calling the RegisterService function) // services registered by calling the RegisterService function)
Services []string Services []string
// Enode
node *enode.Node
// ENR Record with entries to overwrite
Record enr.Record
// function to sanction or prevent suggesting a peer // function to sanction or prevent suggesting a peer
Reachable func(id enode.ID) bool Reachable func(id enode.ID) bool
@ -168,26 +175,27 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error {
// Node returns the node descriptor represented by the config. // Node returns the node descriptor represented by the config.
func (n *NodeConfig) Node() *enode.Node { func (n *NodeConfig) Node() *enode.Node {
return enode.NewV4(&n.PrivateKey.PublicKey, net.IP{127, 0, 0, 1}, int(n.Port), int(n.Port)) return n.node
} }
// RandomNodeConfig returns node configuration with a randomly generated ID and // RandomNodeConfig returns node configuration with a randomly generated ID and
// PrivateKey // PrivateKey
func RandomNodeConfig() *NodeConfig { func RandomNodeConfig() *NodeConfig {
key, err := crypto.GenerateKey() prvkey, err := crypto.GenerateKey()
if err != nil { if err != nil {
panic("unable to generate key") panic("unable to generate key")
} }
id := enode.PubkeyToIDV4(&key.PublicKey)
port, err := assignTCPPort() port, err := assignTCPPort()
if err != nil { if err != nil {
panic("unable to assign tcp port") panic("unable to assign tcp port")
} }
enodId := enode.PubkeyToIDV4(&prvkey.PublicKey)
return &NodeConfig{ return &NodeConfig{
ID: id, PrivateKey: prvkey,
Name: fmt.Sprintf("node_%s", id.String()), ID: enodId,
PrivateKey: key, Name: fmt.Sprintf("node_%s", enodId.String()),
Port: port, Port: port,
EnableMsgEvents: true, EnableMsgEvents: true,
} }

View File

@ -25,6 +25,7 @@ package testing
import ( import (
"bytes" "bytes"
"crypto/ecdsa"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -51,7 +52,7 @@ type ProtocolTester struct {
// NewProtocolTester constructs a new ProtocolTester // NewProtocolTester constructs a new ProtocolTester
// it takes as argument the pivot node id, the number of dummy peers and the // it takes as argument the pivot node id, the number of dummy peers and the
// protocol run function called on a peer connection by the p2p server // protocol run function called on a peer connection by the p2p server
func NewProtocolTester(id enode.ID, nodeCount int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester { func NewProtocolTester(prvkey *ecdsa.PrivateKey, nodeCount int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
services := adapters.Services{ services := adapters.Services{
"test": func(ctx *adapters.ServiceContext) (node.Service, error) { "test": func(ctx *adapters.ServiceContext) (node.Service, error) {
return &testNode{run}, nil return &testNode{run}, nil
@ -62,23 +63,30 @@ func NewProtocolTester(id enode.ID, nodeCount int, run func(*p2p.Peer, p2p.MsgRe
} }
adapter := adapters.NewSimAdapter(services) adapter := adapters.NewSimAdapter(services)
net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{}) net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{})
if _, err := net.NewNodeWithConfig(&adapters.NodeConfig{ nodeConfig := &adapters.NodeConfig{
ID: id, PrivateKey: prvkey,
EnableMsgEvents: true, EnableMsgEvents: true,
Services: []string{"test"}, Services: []string{"test"},
}); err != nil { }
if _, err := net.NewNodeWithConfig(nodeConfig); err != nil {
panic(err.Error()) panic(err.Error())
} }
if err := net.Start(id); err != nil { if err := net.Start(nodeConfig.ID); err != nil {
panic(err.Error()) panic(err.Error())
} }
node := net.GetNode(id).Node.(*adapters.SimNode) node := net.GetNode(nodeConfig.ID).Node.(*adapters.SimNode)
peers := make([]*adapters.NodeConfig, nodeCount) peers := make([]*adapters.NodeConfig, nodeCount)
nodes := make([]*enode.Node, nodeCount) nodes := make([]*enode.Node, nodeCount)
for i := 0; i < nodeCount; i++ { for i := 0; i < nodeCount; i++ {
peers[i] = adapters.RandomNodeConfig() peers[i] = adapters.RandomNodeConfig()
peers[i].Services = []string{"mock"} peers[i].Services = []string{"mock"}
if _, err := net.NewNodeWithConfig(peers[i]); err != nil {
panic(fmt.Sprintf("error initializing peer %v: %v", peers[i].ID, err))
}
if err := net.Start(peers[i].ID); err != nil {
panic(fmt.Sprintf("error starting peer %v: %v", peers[i].ID, err))
}
nodes[i] = peers[i].Node() nodes[i] = peers[i].Node()
} }
events := make(chan *p2p.PeerEvent, 1000) events := make(chan *p2p.PeerEvent, 1000)
@ -94,7 +102,7 @@ func NewProtocolTester(id enode.ID, nodeCount int, run func(*p2p.Peer, p2p.MsgRe
network: net, network: net,
} }
self.Connect(id, peers...) self.Connect(nodeConfig.ID, peers...)
return self return self
} }
@ -108,13 +116,6 @@ func (t *ProtocolTester) Stop() {
// p2p/simulations network connection with the in memory network adapter // p2p/simulations network connection with the in memory network adapter
func (t *ProtocolTester) Connect(selfID enode.ID, peers ...*adapters.NodeConfig) { func (t *ProtocolTester) Connect(selfID enode.ID, peers ...*adapters.NodeConfig) {
for _, peer := range peers { for _, peer := range peers {
log.Trace(fmt.Sprintf("start node %v", peer.ID))
if _, err := t.network.NewNodeWithConfig(peer); err != nil {
panic(fmt.Sprintf("error starting peer %v: %v", peer.ID, err))
}
if err := t.network.Start(peer.ID); err != nil {
panic(fmt.Sprintf("error starting peer %v: %v", peer.ID, err))
}
log.Trace(fmt.Sprintf("connect to %v", peer.ID)) log.Trace(fmt.Sprintf("connect to %v", peer.ID))
if err := t.network.Connect(selfID, peer.ID); err != nil { if err := t.network.Connect(selfID, peer.ID); err != nil {
panic(fmt.Sprintf("error connecting to peer %v: %v", peer.ID, err)) panic(fmt.Sprintf("error connecting to peer %v: %v", peer.ID, err))

View File

@ -24,6 +24,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/contracts/ens" "github.com/ethereum/go-ethereum/contracts/ens"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
@ -49,7 +50,6 @@ type Config struct {
*network.HiveParams *network.HiveParams
Swap *swap.LocalProfile Swap *swap.LocalProfile
Pss *pss.PssParams Pss *pss.PssParams
//*network.SyncParams
Contract common.Address Contract common.Address
EnsRoot common.Address EnsRoot common.Address
EnsAPIs []string EnsAPIs []string
@ -82,7 +82,6 @@ func NewConfig() (c *Config) {
LocalStoreParams: storage.NewDefaultLocalStoreParams(), LocalStoreParams: storage.NewDefaultLocalStoreParams(),
FileStoreParams: storage.NewFileStoreParams(), FileStoreParams: storage.NewFileStoreParams(),
HiveParams: network.NewHiveParams(), HiveParams: network.NewHiveParams(),
//SyncParams: network.NewDefaultSyncParams(),
Swap: swap.NewDefaultSwapParams(), Swap: swap.NewDefaultSwapParams(),
Pss: pss.NewPssParams(), Pss: pss.NewPssParams(),
ListenAddr: DefaultHTTPListenAddr, ListenAddr: DefaultHTTPListenAddr,
@ -117,7 +116,7 @@ func (c *Config) Init(prvKey *ecdsa.PrivateKey) {
pubkey := crypto.FromECDSAPub(&prvKey.PublicKey) pubkey := crypto.FromECDSAPub(&prvKey.PublicKey)
pubkeyhex := common.ToHex(pubkey) pubkeyhex := common.ToHex(pubkey)
keyhex := crypto.Keccak256Hash(pubkey).Hex() keyhex := hexutil.Encode(network.PrivateKeyToBzzKey(prvKey))
c.PublicKey = pubkeyhex c.PublicKey = pubkeyhex
c.BzzKey = keyhex c.BzzKey = keyhex

View File

@ -29,7 +29,10 @@ import (
*/ */
func TestDiscovery(t *testing.T) { func TestDiscovery(t *testing.T) {
params := NewHiveParams() params := NewHiveParams()
s, pp := newHiveTester(t, params, 1, nil) s, pp, err := newHiveTester(t, params, 1, nil)
if err != nil {
t.Fatal(err)
}
node := s.Nodes[0] node := s.Nodes[0]
raddr := NewAddr(node) raddr := NewAddr(node)
@ -40,7 +43,7 @@ func TestDiscovery(t *testing.T) {
defer pp.Stop() defer pp.Stop()
// send subPeersMsg to the peer // send subPeersMsg to the peer
err := s.TestExchanges(p2ptest.Exchange{ err = s.TestExchanges(p2ptest.Exchange{
Label: "outgoing subPeersMsg", Label: "outgoing subPeersMsg",
Expects: []p2ptest.Expect{ Expects: []p2ptest.Expect{
{ {

93
swarm/network/enr.go Normal file
View File

@ -0,0 +1,93 @@
package network
import (
"fmt"
"io"
"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/rlp"
"github.com/ethereum/go-ethereum/swarm/log"
)
// ENRAddrEntry is the entry type to store the bzz key in the enode
type ENRAddrEntry struct {
data []byte
}
func NewENRAddrEntry(addr []byte) *ENRAddrEntry {
return &ENRAddrEntry{
data: addr,
}
}
func (b ENRAddrEntry) Address() []byte {
return b.data
}
// ENRKey implements enr.Entry
func (b ENRAddrEntry) ENRKey() string {
return "bzzkey"
}
// EncodeRLP implements rlp.Encoder
func (b ENRAddrEntry) EncodeRLP(w io.Writer) error {
log.Debug("in encoderlp", "b", b, "p", fmt.Sprintf("%p", &b))
return rlp.Encode(w, &b.data)
}
// DecodeRLP implements rlp.Decoder
func (b *ENRAddrEntry) DecodeRLP(s *rlp.Stream) error {
byt, err := s.Bytes()
if err != nil {
return err
}
b.data = byt
log.Debug("in decoderlp", "b", b, "p", fmt.Sprintf("%p", &b))
return nil
}
type ENRLightNodeEntry bool
func (b ENRLightNodeEntry) ENRKey() string {
return "bzzlightnode"
}
type ENRBootNodeEntry bool
func (b ENRBootNodeEntry) ENRKey() string {
return "bzzbootnode"
}
func getENRBzzPeer(p *p2p.Peer, rw p2p.MsgReadWriter, spec *protocols.Spec) *BzzPeer {
var lightnode ENRLightNodeEntry
var bootnode ENRBootNodeEntry
// retrieve the ENR Record data
record := p.Node().Record()
record.Load(&lightnode)
record.Load(&bootnode)
// get the address; separate function as long as we need swarm/network:NewAddr() to call it
addr := getENRBzzAddr(p.Node())
// build the peer using the retrieved data
return &BzzPeer{
Peer: protocols.NewPeer(p, rw, spec),
LightNode: bool(lightnode),
BzzAddr: addr,
}
}
func getENRBzzAddr(nod *enode.Node) *BzzAddr {
var addr ENRAddrEntry
record := nod.Record()
record.Load(&addr)
return &BzzAddr{
OAddr: addr.data,
UAddr: []byte(nod.String()),
}
}

View File

@ -22,31 +22,43 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/crypto"
p2ptest "github.com/ethereum/go-ethereum/p2p/testing" p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
"github.com/ethereum/go-ethereum/swarm/state" "github.com/ethereum/go-ethereum/swarm/state"
) )
func newHiveTester(t *testing.T, params *HiveParams, n int, store state.Store) (*bzzTester, *Hive) { func newHiveTester(t *testing.T, params *HiveParams, n int, store state.Store) (*bzzTester, *Hive, error) {
// setup // setup
addr := RandomAddr() // tested peers peer address prvkey, err := crypto.GenerateKey()
to := NewKademlia(addr.OAddr, NewKadParams()) if err != nil {
return nil, nil, err
}
addr := PrivateKeyToBzzKey(prvkey)
to := NewKademlia(addr, NewKadParams())
pp := NewHive(params, to, store) // hive pp := NewHive(params, to, store) // hive
return newBzzBaseTester(t, n, addr, DiscoverySpec, pp.Run), pp bt, err := newBzzBaseTester(t, n, prvkey, DiscoverySpec, pp.Run)
if err != nil {
return nil, nil, err
}
return bt, pp, nil
} }
// TestRegisterAndConnect verifies that the protocol runs successfully // TestRegisterAndConnect verifies that the protocol runs successfully
// and that the peer connection exists afterwards // and that the peer connection exists afterwards
func TestRegisterAndConnect(t *testing.T) { func TestRegisterAndConnect(t *testing.T) {
params := NewHiveParams() params := NewHiveParams()
s, pp := newHiveTester(t, params, 1, nil) s, pp, err := newHiveTester(t, params, 1, nil)
if err != nil {
t.Fatal(err)
}
node := s.Nodes[0] node := s.Nodes[0]
raddr := NewAddr(node) raddr := NewAddr(node)
pp.Register(raddr) pp.Register(raddr)
// start the hive // start the hive
err := pp.Start(s.Server) err = pp.Start(s.Server)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -109,8 +121,10 @@ func TestHiveStatePersistance(t *testing.T) {
} }
params := NewHiveParams() params := NewHiveParams()
s, pp := newHiveTester(t, params, 5, store) s, pp, err := newHiveTester(t, params, 5, store)
if err != nil {
t.Fatal(err)
}
peers := make(map[string]bool) peers := make(map[string]bool)
for _, node := range s.Nodes { for _, node := range s.Nodes {
raddr := NewAddr(node) raddr := NewAddr(node)
@ -133,7 +147,10 @@ func TestHiveStatePersistance(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
s1, pp := newHiveTester(t, params, 0, persistedStore) s1, pp, err := newHiveTester(t, params, 0, persistedStore)
if err != nil {
t.Fatal(err)
}
// start the hive and check that we know of all expected peers // start the hive and check that we know of all expected peers
pp.Start(s1.Server) pp.Start(s1.Server)

70
swarm/network/network.go Normal file
View File

@ -0,0 +1,70 @@
package network
import (
"crypto/ecdsa"
"fmt"
"net"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p/enode"
)
// BzzAddr implements the PeerAddr interface
type BzzAddr struct {
OAddr []byte
UAddr []byte
}
// Address implements OverlayPeer interface to be used in Overlay.
func (a *BzzAddr) Address() []byte {
return a.OAddr
}
// Over returns the overlay address.
func (a *BzzAddr) Over() []byte {
return a.OAddr
}
// Under returns the underlay address.
func (a *BzzAddr) Under() []byte {
return a.UAddr
}
// ID returns the node identifier in the underlay.
func (a *BzzAddr) ID() enode.ID {
n, err := enode.ParseV4(string(a.UAddr))
if err != nil {
return enode.ID{}
}
return n.ID()
}
// Update updates the underlay address of a peer record
func (a *BzzAddr) Update(na *BzzAddr) *BzzAddr {
return &BzzAddr{a.OAddr, na.UAddr}
}
// String pretty prints the address
func (a *BzzAddr) String() string {
return fmt.Sprintf("%x <%s>", a.OAddr, a.UAddr)
}
// RandomAddr is a utility method generating an address from a public key
func RandomAddr() *BzzAddr {
key, err := crypto.GenerateKey()
if err != nil {
panic("unable to generate key")
}
node := enode.NewV4(&key.PublicKey, net.IP{127, 0, 0, 1}, 30303, 30303)
return NewAddr(node)
}
// NewAddr constucts a BzzAddr from a node record.
func NewAddr(node *enode.Node) *BzzAddr {
return &BzzAddr{OAddr: node.ID().Bytes(), UAddr: []byte(node.String())}
}
func PrivateKeyToBzzKey(prvKey *ecdsa.PrivateKey) []byte {
pubkeyBytes := crypto.FromECDSAPub(&prvKey.PublicKey)
return crypto.Keccak256Hash(pubkeyBytes).Bytes()
}

View File

@ -20,11 +20,9 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net"
"sync" "sync"
"time" "time"
"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/enode"
"github.com/ethereum/go-ethereum/p2p/protocols" "github.com/ethereum/go-ethereum/p2p/protocols"
@ -332,58 +330,3 @@ func (b *Bzz) GetOrCreateHandshake(peerID enode.ID) (*HandshakeMsg, bool) {
return handshake, found return handshake, found
} }
// BzzAddr implements the PeerAddr interface
type BzzAddr struct {
OAddr []byte
UAddr []byte
}
// Address implements OverlayPeer interface to be used in Overlay.
func (a *BzzAddr) Address() []byte {
return a.OAddr
}
// Over returns the overlay address.
func (a *BzzAddr) Over() []byte {
return a.OAddr
}
// Under returns the underlay address.
func (a *BzzAddr) Under() []byte {
return a.UAddr
}
// ID returns the node identifier in the underlay.
func (a *BzzAddr) ID() enode.ID {
n, err := enode.ParseV4(string(a.UAddr))
if err != nil {
return enode.ID{}
}
return n.ID()
}
// Update updates the underlay address of a peer record
func (a *BzzAddr) Update(na *BzzAddr) *BzzAddr {
return &BzzAddr{a.OAddr, na.UAddr}
}
// String pretty prints the address
func (a *BzzAddr) String() string {
return fmt.Sprintf("%x <%s>", a.OAddr, a.UAddr)
}
// RandomAddr is a utility method generating an address from a public key
func RandomAddr() *BzzAddr {
key, err := crypto.GenerateKey()
if err != nil {
panic("unable to generate key")
}
node := enode.NewV4(&key.PublicKey, net.IP{127, 0, 0, 1}, 30303, 30303)
return NewAddr(node)
}
// NewAddr constucts a BzzAddr from a node record.
func NewAddr(node *enode.Node) *BzzAddr {
return &BzzAddr{OAddr: node.ID().Bytes(), UAddr: []byte(node.String())}
}

View File

@ -17,15 +17,18 @@
package network package network
import ( import (
"crypto/ecdsa"
"flag" "flag"
"fmt" "fmt"
"os" "os"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"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/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/p2p/protocols" "github.com/ethereum/go-ethereum/p2p/protocols"
p2ptest "github.com/ethereum/go-ethereum/p2p/testing" p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
) )
@ -67,7 +70,7 @@ func HandshakeMsgExchange(lhs, rhs *HandshakeMsg, id enode.ID) []p2ptest.Exchang
} }
} }
func newBzzBaseTester(t *testing.T, n int, addr *BzzAddr, spec *protocols.Spec, run func(*BzzPeer) error) *bzzTester { func newBzzBaseTester(t *testing.T, n int, prvkey *ecdsa.PrivateKey, spec *protocols.Spec, run func(*BzzPeer) error) (*bzzTester, error) {
cs := make(map[string]chan bool) cs := make(map[string]chan bool)
srv := func(p *BzzPeer) error { srv := func(p *BzzPeer) error {
@ -83,9 +86,22 @@ func newBzzBaseTester(t *testing.T, n int, addr *BzzAddr, spec *protocols.Spec,
return srv(&BzzPeer{Peer: protocols.NewPeer(p, rw, spec), BzzAddr: NewAddr(p.Node())}) return srv(&BzzPeer{Peer: protocols.NewPeer(p, rw, spec), BzzAddr: NewAddr(p.Node())})
} }
s := p2ptest.NewProtocolTester(addr.ID(), n, protocol) s := p2ptest.NewProtocolTester(prvkey, n, protocol)
var record enr.Record
bzzKey := PrivateKeyToBzzKey(prvkey)
record.Set(NewENRAddrEntry(bzzKey))
err := enode.SignV4(&record, prvkey)
if err != nil {
return nil, fmt.Errorf("unable to generate ENR: %v", err)
}
nod, err := enode.New(enode.V4ID{}, &record)
if err != nil {
return nil, fmt.Errorf("unable to create enode: %v", err)
}
addr := getENRBzzAddr(nod)
for _, node := range s.Nodes { for _, node := range s.Nodes {
log.Warn("node", "node", node)
cs[node.ID().String()] = make(chan bool) cs[node.ID().String()] = make(chan bool)
} }
@ -93,7 +109,7 @@ func newBzzBaseTester(t *testing.T, n int, addr *BzzAddr, spec *protocols.Spec,
addr: addr, addr: addr,
ProtocolTester: s, ProtocolTester: s,
cs: cs, cs: cs,
} }, nil
} }
type bzzTester struct { type bzzTester struct {
@ -116,15 +132,28 @@ func newBzz(addr *BzzAddr, lightNode bool) *Bzz {
return bzz return bzz
} }
func newBzzHandshakeTester(n int, addr *BzzAddr, lightNode bool) *bzzTester { func newBzzHandshakeTester(n int, prvkey *ecdsa.PrivateKey, lightNode bool) (*bzzTester, error) {
var record enr.Record
bzzkey := PrivateKeyToBzzKey(prvkey)
record.Set(NewENRAddrEntry(bzzkey))
record.Set(ENRLightNodeEntry(lightNode))
err := enode.SignV4(&record, prvkey)
if err != nil {
return nil, err
}
nod, err := enode.New(enode.V4ID{}, &record)
addr := getENRBzzAddr(nod)
bzz := newBzz(addr, lightNode) bzz := newBzz(addr, lightNode)
pt := p2ptest.NewProtocolTester(addr.ID(), n, bzz.runBzz)
pt := p2ptest.NewProtocolTester(prvkey, n, bzz.runBzz)
return &bzzTester{ return &bzzTester{
addr: addr, addr: addr,
ProtocolTester: pt, ProtocolTester: pt,
bzz: bzz, bzz: bzz,
} }, nil
} }
// should test handshakes in one exchange? parallelisation // should test handshakes in one exchange? parallelisation
@ -165,12 +194,18 @@ func correctBzzHandshake(addr *BzzAddr, lightNode bool) *HandshakeMsg {
func TestBzzHandshakeNetworkIDMismatch(t *testing.T) { func TestBzzHandshakeNetworkIDMismatch(t *testing.T) {
lightNode := false lightNode := false
addr := RandomAddr() prvkey, err := crypto.GenerateKey()
s := newBzzHandshakeTester(1, addr, lightNode) if err != nil {
t.Fatal(err)
}
s, err := newBzzHandshakeTester(1, prvkey, lightNode)
if err != nil {
t.Fatal(err)
}
node := s.Nodes[0] node := s.Nodes[0]
err := s.testHandshake( err = s.testHandshake(
correctBzzHandshake(addr, lightNode), correctBzzHandshake(s.addr, lightNode),
&HandshakeMsg{Version: TestProtocolVersion, NetworkID: 321, Addr: NewAddr(node)}, &HandshakeMsg{Version: TestProtocolVersion, NetworkID: 321, Addr: NewAddr(node)},
&p2ptest.Disconnect{Peer: node.ID(), Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): network id mismatch 321 (!= 3)")}, &p2ptest.Disconnect{Peer: node.ID(), Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): network id mismatch 321 (!= 3)")},
) )
@ -182,12 +217,18 @@ func TestBzzHandshakeNetworkIDMismatch(t *testing.T) {
func TestBzzHandshakeVersionMismatch(t *testing.T) { func TestBzzHandshakeVersionMismatch(t *testing.T) {
lightNode := false lightNode := false
addr := RandomAddr() prvkey, err := crypto.GenerateKey()
s := newBzzHandshakeTester(1, addr, lightNode) if err != nil {
t.Fatal(err)
}
s, err := newBzzHandshakeTester(1, prvkey, lightNode)
if err != nil {
t.Fatal(err)
}
node := s.Nodes[0] node := s.Nodes[0]
err := s.testHandshake( err = s.testHandshake(
correctBzzHandshake(addr, lightNode), correctBzzHandshake(s.addr, lightNode),
&HandshakeMsg{Version: 0, NetworkID: TestProtocolNetworkID, Addr: NewAddr(node)}, &HandshakeMsg{Version: 0, NetworkID: TestProtocolNetworkID, Addr: NewAddr(node)},
&p2ptest.Disconnect{Peer: node.ID(), Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): version mismatch 0 (!= %d)", TestProtocolVersion)}, &p2ptest.Disconnect{Peer: node.ID(), Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): version mismatch 0 (!= %d)", TestProtocolVersion)},
) )
@ -199,12 +240,18 @@ func TestBzzHandshakeVersionMismatch(t *testing.T) {
func TestBzzHandshakeSuccess(t *testing.T) { func TestBzzHandshakeSuccess(t *testing.T) {
lightNode := false lightNode := false
addr := RandomAddr() prvkey, err := crypto.GenerateKey()
s := newBzzHandshakeTester(1, addr, lightNode) if err != nil {
t.Fatal(err)
}
s, err := newBzzHandshakeTester(1, prvkey, lightNode)
if err != nil {
t.Fatal(err)
}
node := s.Nodes[0] node := s.Nodes[0]
err := s.testHandshake( err = s.testHandshake(
correctBzzHandshake(addr, lightNode), correctBzzHandshake(s.addr, lightNode),
&HandshakeMsg{Version: TestProtocolVersion, NetworkID: TestProtocolNetworkID, Addr: NewAddr(node)}, &HandshakeMsg{Version: TestProtocolVersion, NetworkID: TestProtocolNetworkID, Addr: NewAddr(node)},
) )
@ -224,14 +271,20 @@ func TestBzzHandshakeLightNode(t *testing.T) {
for _, test := range lightNodeTests { for _, test := range lightNodeTests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
randomAddr := RandomAddr() prvkey, err := crypto.GenerateKey()
pt := newBzzHandshakeTester(1, randomAddr, false) if err != nil {
t.Fatal(err)
}
pt, err := newBzzHandshakeTester(1, prvkey, false)
if err != nil {
t.Fatal(err)
}
node := pt.Nodes[0] node := pt.Nodes[0]
addr := NewAddr(node) addr := NewAddr(node)
err := pt.testHandshake( err = pt.testHandshake(
correctBzzHandshake(randomAddr, false), correctBzzHandshake(pt.addr, false),
&HandshakeMsg{Version: TestProtocolVersion, NetworkID: TestProtocolNetworkID, Addr: addr, LightNode: test.lightNode}, &HandshakeMsg{Version: TestProtocolVersion, NetworkID: TestProtocolNetworkID, Addr: addr, LightNode: test.lightNode},
) )

View File

@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/simulations" "github.com/ethereum/go-ethereum/p2p/simulations"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/swarm/network"
) )
// NodeIDs returns NodeIDs for all nodes in the network. // NodeIDs returns NodeIDs for all nodes in the network.
@ -96,10 +97,28 @@ func (s *Simulation) AddNode(opts ...AddNodeOption) (id enode.ID, err error) {
if len(conf.Services) == 0 { if len(conf.Services) == 0 {
conf.Services = s.serviceNames conf.Services = s.serviceNames
} }
// add ENR records to the underlying node
// most importantly the bzz overlay address
//
// for now we have no way of setting bootnodes or lightnodes in sims
// so we just set them as false
// they should perhaps be possible to override them with AddNodeOption
bzzKey := network.PrivateKeyToBzzKey(conf.PrivateKey)
bzzAddr := network.NewENRAddrEntry(bzzKey)
var lightnode network.ENRLightNodeEntry
var bootnode network.ENRBootNodeEntry
conf.Record.Set(bzzAddr)
conf.Record.Set(&lightnode)
conf.Record.Set(&bootnode)
// Add the bzz address to the node config
node, err := s.Net.NewNodeWithConfig(conf) node, err := s.Net.NewNodeWithConfig(conf)
if err != nil { if err != nil {
return id, err return id, err
} }
return node.ID(), s.Net.Start(node.ID()) return node.ID(), s.Net.Start(node.ID())
} }

View File

@ -131,6 +131,7 @@ func BenchmarkDiscovery_128_4(b *testing.B) { benchmarkDiscovery(b, 128, 4) }
func BenchmarkDiscovery_256_4(b *testing.B) { benchmarkDiscovery(b, 256, 4) } func BenchmarkDiscovery_256_4(b *testing.B) { benchmarkDiscovery(b, 256, 4) }
func TestDiscoverySimulationExecAdapter(t *testing.T) { func TestDiscoverySimulationExecAdapter(t *testing.T) {
t.Skip("This is left broken pending ENR preparations for swarm binary. Execadapter is not currently in use, so a short pause won't hurt")
testDiscoverySimulationExecAdapter(t, *nodeCount, *initCount) testDiscoverySimulationExecAdapter(t, *nodeCount, *initCount)
} }

View File

@ -30,6 +30,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
@ -183,7 +184,13 @@ func newStreamerTester(registryOptions *RegistryOptions) (*p2ptest.ProtocolTeste
streamer.Close() streamer.Close()
removeDataDir() removeDataDir()
} }
protocolTester := p2ptest.NewProtocolTester(addr.ID(), 1, streamer.runProtocol) prvkey, err := crypto.GenerateKey()
if err != nil {
removeDataDir()
return nil, nil, nil, nil, err
}
protocolTester := p2ptest.NewProtocolTester(prvkey, 1, streamer.runProtocol)
err = waitForPeers(streamer, 10*time.Second, 1) err = waitForPeers(streamer, 10*time.Second, 1)
if err != nil { if err != nil {