Update Discv5 to the Latest Version (#3392)

* update workspace

* change to new version

* gaz

* set keys

* try more things

* finally fixed all tests

* fix bootnode

* Update beacon-chain/p2p/discovery.go

Co-Authored-By: Preston Van Loon <preston@prysmaticlabs.com>

* preston's and raul's review

* add http server

* add tool

* add image

* change comment

* add multiaddr comment

* lint

* cosmetic changes

* fix docker

* remove dep

* preston's requested changes
This commit is contained in:
Nishant Das
2019-09-07 00:50:20 +05:30
committed by GitHub
parent 56a395a297
commit 171e5007c5
12 changed files with 310 additions and 116 deletions

View File

@@ -192,7 +192,7 @@ protobuf_deps()
go_repository( go_repository(
name = "com_github_ethereum_go_ethereum", name = "com_github_ethereum_go_ethereum",
commit = "981f27aaf9bdce45391d0cd8bb522df514e0b566", commit = "8839d2f3b900530fbab26154740e8bded3932a86",
importpath = "github.com/ethereum/go-ethereum", importpath = "github.com/ethereum/go-ethereum",
# Note: go-ethereum is not bazel-friendly with regards to cgo. We have a # Note: go-ethereum is not bazel-friendly with regards to cgo. We have a
# a fork that has resolved these issues by disabling HID/USB support and # a fork that has resolved these issues by disabling HID/USB support and

View File

@@ -29,7 +29,9 @@ go_library(
"//shared/iputils:go_default_library", "//shared/iputils:go_default_library",
"@com_github_btcsuite_btcd//btcec:go_default_library", "@com_github_btcsuite_btcd//btcec:go_default_library",
"@com_github_ethereum_go_ethereum//crypto:go_default_library", "@com_github_ethereum_go_ethereum//crypto:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/discv5:go_default_library", "@com_github_ethereum_go_ethereum//p2p/discover:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enode:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enr:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_ipfs_go_ipfs_addr//:go_default_library", "@com_github_ipfs_go_ipfs_addr//:go_default_library",
"@com_github_karlseguin_ccache//:go_default_library", "@com_github_karlseguin_ccache//:go_default_library",
@@ -59,6 +61,7 @@ go_test(
"service_test.go", "service_test.go",
], ],
embed = [":go_default_library"], embed = [":go_default_library"],
flaky = True,
tags = ["block-network"], tags = ["block-network"],
deps = [ deps = [
"//beacon-chain/p2p/testing:go_default_library", "//beacon-chain/p2p/testing:go_default_library",
@@ -66,7 +69,8 @@ go_test(
"//shared/iputils:go_default_library", "//shared/iputils:go_default_library",
"//shared/testutil:go_default_library", "//shared/testutil:go_default_library",
"@com_github_ethereum_go_ethereum//crypto:go_default_library", "@com_github_ethereum_go_ethereum//crypto:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/discv5:go_default_library", "@com_github_ethereum_go_ethereum//p2p/discover:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enode:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_libp2p_go_libp2p//:go_default_library", "@com_github_libp2p_go_libp2p//:go_default_library",
"@com_github_libp2p_go_libp2p_core//host:go_default_library", "@com_github_libp2p_go_libp2p_core//host:go_default_library",

View File

@@ -4,9 +4,10 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"fmt" "fmt"
"net" "net"
"time"
"github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
iaddr "github.com/ipfs/go-ipfs-addr" iaddr "github.com/ipfs/go-ipfs-addr"
"github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peer"
ma "github.com/multiformats/go-multiaddr" ma "github.com/multiformats/go-multiaddr"
@@ -16,48 +17,69 @@ import (
// Listener defines the discovery V5 network interface that is used // Listener defines the discovery V5 network interface that is used
// to communicate with other peers. // to communicate with other peers.
type Listener interface { type Listener interface {
Self() *discv5.Node Self() *enode.Node
Close() Close()
Lookup(discv5.NodeID) []*discv5.Node Lookup(enode.ID) []*enode.Node
ReadRandomNodes([]*discv5.Node) int ReadRandomNodes([]*enode.Node) int
SetFallbackNodes([]*discv5.Node) error Resolve(*enode.Node) *enode.Node
Resolve(discv5.NodeID) *discv5.Node LookupRandom() []*enode.Node
RegisterTopic(discv5.Topic, <-chan struct{}) Ping(*enode.Node) error
SearchTopic(discv5.Topic, <-chan time.Duration, chan<- *discv5.Node, chan<- bool) RequestENR(*enode.Node) (*enode.Node, error)
} }
func createListener(ipAddr net.IP, port int, privKey *ecdsa.PrivateKey) *discv5.Network { func createListener(ipAddr net.IP, privKey *ecdsa.PrivateKey, cfg *Config) *discover.UDPv5 {
udpAddr := &net.UDPAddr{ udpAddr := &net.UDPAddr{
IP: ipAddr, IP: ipAddr,
Port: port, Port: int(cfg.Port),
} }
conn, err := net.ListenUDP("udp4", udpAddr) conn, err := net.ListenUDP("udp4", udpAddr)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
localNode, err := createLocalNode(privKey, ipAddr, int(cfg.Port))
if err != nil {
log.Fatal(err)
}
dv5Cfg := discover.Config{
PrivateKey: privKey,
}
if cfg.BootstrapNodeAddr != "" {
bootNode, err := enode.Parse(enode.ValidSchemes, cfg.BootstrapNodeAddr)
if err != nil {
log.Fatal(err)
}
dv5Cfg.Bootnodes = []*enode.Node{bootNode}
}
network, err := discv5.ListenUDP(privKey, conn, "", nil) network, err := discover.ListenV5(conn, localNode, dv5Cfg)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
return network return network
} }
func startDiscoveryV5(addr net.IP, privKey *ecdsa.PrivateKey, cfg *Config) (*discv5.Network, error) { func createLocalNode(privKey *ecdsa.PrivateKey, ipAddr net.IP, port int) (*enode.LocalNode, error) {
listener := createListener(addr, int(cfg.Port), privKey) db, err := enode.OpenDB("")
bootNode, err := discv5.ParseNode(cfg.BootstrapNodeAddr)
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "could not open node's peer database")
}
if err := listener.SetFallbackNodes([]*discv5.Node{bootNode}); err != nil {
return nil, err
} }
localNode := enode.NewLocalNode(db, privKey)
ipEntry := enr.IP(ipAddr)
udpEntry := enr.UDP(port)
localNode.Set(ipEntry)
localNode.Set(udpEntry)
return localNode, nil
}
func startDiscoveryV5(addr net.IP, privKey *ecdsa.PrivateKey, cfg *Config) (*discover.UDPv5, error) {
listener := createListener(addr, privKey, cfg)
node := listener.Self() node := listener.Self()
log.Infof("Started Discovery: %s", node.String()) log.Infof("Started Discovery: %s", node.ID())
return listener, nil return listener, nil
} }
func convertToMultiAddr(nodes []*discv5.Node) []ma.Multiaddr { func convertToMultiAddr(nodes []*enode.Node) []ma.Multiaddr {
var multiAddrs []ma.Multiaddr var multiAddrs []ma.Multiaddr
for _, node := range nodes { for _, node := range nodes {
multiAddr, err := convertToSingleMultiAddr(node) multiAddr, err := convertToSingleMultiAddr(node)
@@ -70,21 +92,21 @@ func convertToMultiAddr(nodes []*discv5.Node) []ma.Multiaddr {
return multiAddrs return multiAddrs
} }
func convertToSingleMultiAddr(node *discv5.Node) (ma.Multiaddr, error) { func convertToSingleMultiAddr(node *enode.Node) (ma.Multiaddr, error) {
ip4 := node.IP.To4() ip4 := node.IP().To4()
if ip4 == nil { if ip4 == nil {
return nil, errors.New("node doesn't have an ip4 address") return nil, errors.New("node doesn't have an ip4 address")
} }
pubkey, err := node.ID.Pubkey() pubkey := node.Pubkey()
if err != nil {
return nil, errors.Wrap(err, "could not get pubkey from node ID")
}
assertedKey := convertToInterfacePubkey(pubkey) assertedKey := convertToInterfacePubkey(pubkey)
id, err := peer.IDFromPublicKey(assertedKey) id, err := peer.IDFromPublicKey(assertedKey)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get peer id") return nil, errors.Wrap(err, "could not get peer id")
} }
multiAddrString := fmt.Sprintf("/ip4/%s/tcp/%d/p2p/%s", ip4.String(), node.TCP, id) // we use the udp port for now, since all udp and tcp connections occur from the same
// port. This will be changed to the node's TCP port in the future, when we allow a
// beacon node to provide separate TCP and UDP ports.
multiAddrString := fmt.Sprintf("/ip4/%s/tcp/%d/p2p/%s", ip4.String(), node.UDP(), id)
multiAddr, err := ma.NewMultiaddr(multiAddrString) multiAddr, err := ma.NewMultiaddr(multiAddrString)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get multiaddr") return nil, errors.Wrap(err, "could not get multiaddr")

View File

@@ -2,20 +2,21 @@ package p2p
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand"
"fmt" "fmt"
"net" "net"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/host"
"github.com/prysmaticlabs/prysm/shared/iputils" "github.com/prysmaticlabs/prysm/shared/iputils"
"github.com/prysmaticlabs/prysm/shared/testutil" "github.com/prysmaticlabs/prysm/shared/testutil"
logTest "github.com/sirupsen/logrus/hooks/test" logTest "github.com/sirupsen/logrus/hooks/test"
) )
var discoveryWaitTime = 1 * time.Second
func createAddrAndPrivKey(t *testing.T) (net.IP, *ecdsa.PrivateKey) { func createAddrAndPrivKey(t *testing.T) (net.IP, *ecdsa.PrivateKey) {
ip, err := iputils.ExternalIPv4() ip, err := iputils.ExternalIPv4()
if err != nil { if err != nil {
@@ -32,20 +33,17 @@ func createAddrAndPrivKey(t *testing.T) (net.IP, *ecdsa.PrivateKey) {
func TestCreateListener(t *testing.T) { func TestCreateListener(t *testing.T) {
port := 1024 port := 1024
ipAddr, pkey := createAddrAndPrivKey(t) ipAddr, pkey := createAddrAndPrivKey(t)
listener := createListener(ipAddr, port, pkey) listener := createListener(ipAddr, pkey, &Config{Port: uint(port)})
defer listener.Close() defer listener.Close()
if !listener.Self().IP.Equal(ipAddr) { if !listener.Self().IP().Equal(ipAddr) {
t.Errorf("Ip address is not the expected type, wanted %s but got %s", ipAddr.String(), listener.Self().IP.String()) t.Errorf("Ip address is not the expected type, wanted %s but got %s", ipAddr.String(), listener.Self().IP().String())
} }
if port != int(listener.Self().UDP) { if port != int(listener.Self().UDP()) {
t.Errorf("In correct port number, wanted %d but got %d", port, listener.Self().UDP) t.Errorf("In correct port number, wanted %d but got %d", port, listener.Self().UDP())
}
pubkey, err := listener.Self().ID.Pubkey()
if err != nil {
t.Error(err)
} }
pubkey := listener.Self().Pubkey()
XisSame := pkey.PublicKey.X.Cmp(pubkey.X) == 0 XisSame := pkey.PublicKey.X.Cmp(pubkey.X) == 0
YisSame := pkey.PublicKey.Y.Cmp(pubkey.Y) == 0 YisSame := pkey.PublicKey.Y.Cmp(pubkey.Y) == 0
@@ -57,20 +55,19 @@ func TestCreateListener(t *testing.T) {
func TestStartDiscV5_DiscoverAllPeers(t *testing.T) { func TestStartDiscV5_DiscoverAllPeers(t *testing.T) {
port := 2000 port := 2000
ipAddr, pkey := createAddrAndPrivKey(t) ipAddr, pkey := createAddrAndPrivKey(t)
bootListener := createListener(ipAddr, port, pkey) bootListener := createListener(ipAddr, pkey, &Config{Port: uint(port)})
defer bootListener.Close() defer bootListener.Close()
bootNode := bootListener.Self() bootNode := bootListener.Self()
cfg := &Config{ cfg := &Config{
BootstrapNodeAddr: bootNode.String(), BootstrapNodeAddr: bootNode.String(),
Encoding: "ssz", Encoding: "ssz",
} }
var listeners []*discv5.Network var listeners []*discover.UDPv5
for i := 1; i <= 5; i++ { for i := 1; i <= 5; i++ {
port = 2000 + i port = 3000 + i
cfg.UDPPort = uint(port) cfg.Port = uint(port)
ipAddr, pkey := createAddrAndPrivKey(t) ipAddr, pkey := createAddrAndPrivKey(t)
listener, err := startDiscoveryV5(ipAddr, pkey, cfg) listener, err := startDiscoveryV5(ipAddr, pkey, cfg)
if err != nil { if err != nil {
@@ -80,13 +77,13 @@ func TestStartDiscV5_DiscoverAllPeers(t *testing.T) {
} }
// Wait for the nodes to have their local routing tables to be populated with the other nodes // Wait for the nodes to have their local routing tables to be populated with the other nodes
time.Sleep(100 * time.Millisecond) time.Sleep(discoveryWaitTime)
lastListener := listeners[len(listeners)-1] lastListener := listeners[len(listeners)-1]
nodes := lastListener.Lookup(bootNode.ID) nodes := lastListener.Lookup(bootNode.ID())
if len(nodes) != 6 { if len(nodes) < 4 {
t.Errorf("The node's local table doesn't have the expected number of nodes. "+ t.Errorf("The node's local table doesn't have the expected number of nodes. "+
"Expected %d but got %d", 6, len(nodes)) "Expected more than or equal to %d but got %d", 4, len(nodes))
} }
// Close all ports // Close all ports
@@ -98,24 +95,21 @@ func TestStartDiscV5_DiscoverAllPeers(t *testing.T) {
func TestMultiAddrsConversion_InvalidIPAddr(t *testing.T) { func TestMultiAddrsConversion_InvalidIPAddr(t *testing.T) {
hook := logTest.NewGlobal() hook := logTest.NewGlobal()
ipAddr := net.IPv6zero ipAddr := net.IPv6zero
pkey, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader) _, pkey := createAddrAndPrivKey(t)
node, err := createLocalNode(pkey, ipAddr, 0)
if err != nil { if err != nil {
t.Fatalf("Could not generate key %v", err) t.Fatal(err)
} }
nodeID := discv5.PubkeyID(&pkey.PublicKey) _ = convertToMultiAddr([]*enode.Node{node.Node()})
node := discv5.NewNode(nodeID, ipAddr, 0, 0)
_ = convertToMultiAddr([]*discv5.Node{node})
testutil.AssertLogsContain(t, hook, "node doesn't have an ip4 address") testutil.AssertLogsContain(t, hook, "node doesn't have an ip4 address")
} }
func TestMultiAddrConversion_OK(t *testing.T) { func TestMultiAddrConversion_OK(t *testing.T) {
hook := logTest.NewGlobal() hook := logTest.NewGlobal()
port := 1024
ipAddr, pkey := createAddrAndPrivKey(t) ipAddr, pkey := createAddrAndPrivKey(t)
listener := createListener(ipAddr, port, pkey) listener := createListener(ipAddr, pkey, &Config{})
_ = convertToMultiAddr([]*discv5.Node{listener.Self()}) _ = convertToMultiAddr([]*enode.Node{listener.Self()})
testutil.AssertLogsDoNotContain(t, hook, "Node doesn't have an ip4 address") testutil.AssertLogsDoNotContain(t, hook, "Node doesn't have an ip4 address")
testutil.AssertLogsDoNotContain(t, hook, "Invalid port, the tcp port of the node is a reserved port") testutil.AssertLogsDoNotContain(t, hook, "Invalid port, the tcp port of the node is a reserved port")
testutil.AssertLogsDoNotContain(t, hook, "Could not get multiaddr") testutil.AssertLogsDoNotContain(t, hook, "Could not get multiaddr")

View File

@@ -6,7 +6,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/karlseguin/ccache" "github.com/karlseguin/ccache"
"github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/host"
@@ -92,7 +92,6 @@ func (s *Service) Start() {
if s.cfg.BootstrapNodeAddr != "" && !s.cfg.NoDiscovery { if s.cfg.BootstrapNodeAddr != "" && !s.cfg.NoDiscovery {
ipAddr := ipAddr(s.cfg) ipAddr := ipAddr(s.cfg)
listener, err := startDiscoveryV5(ipAddr, s.privKey, s.cfg) listener, err := startDiscoveryV5(ipAddr, s.privKey, s.cfg)
if err != nil { if err != nil {
log.WithError(err).Error("Failed to start discovery") log.WithError(err).Error("Failed to start discovery")
@@ -184,13 +183,12 @@ func (s *Service) Disconnect(pid peer.ID) error {
// listen for new nodes watches for new nodes in the network and adds them to the peerstore. // listen for new nodes watches for new nodes in the network and adds them to the peerstore.
func (s *Service) listenForNewNodes() { func (s *Service) listenForNewNodes() {
nodes := make([]*discv5.Node, 10)
ticker := time.NewTicker(pollingPeriod) ticker := time.NewTicker(pollingPeriod)
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
num := s.dv5Listener.ReadRandomNodes(nodes) nodes := s.dv5Listener.LookupRandom()
multiAddresses := convertToMultiAddr(nodes[:num]) multiAddresses := convertToMultiAddr(nodes)
s.connectWithAllPeers(multiAddresses) s.connectWithAllPeers(multiAddresses)
case <-s.ctx.Done(): case <-s.ctx.Done():
log.Debug("p2p context is closed, exiting routine") log.Debug("p2p context is closed, exiting routine")
@@ -221,7 +219,7 @@ func (s *Service) connectWithAllPeers(multiAddrs []ma.Multiaddr) {
} }
func (s *Service) addBootNodeToExclusionList() error { func (s *Service) addBootNodeToExclusionList() error {
bootNode, err := discv5.ParseNode(s.cfg.BootstrapNodeAddr) bootNode, err := enode.Parse(enode.ValidSchemes, s.cfg.BootstrapNodeAddr)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -8,7 +8,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/enode"
libp2p "github.com/libp2p/go-libp2p" libp2p "github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peer"
@@ -19,35 +20,35 @@ import (
type mockListener struct{} type mockListener struct{}
func (m *mockListener) Self() *discv5.Node { func (mockListener) Self() *enode.Node {
panic("implement me") panic("implement me")
} }
func (m *mockListener) Close() { func (mockListener) Close() {
//no-op //no-op
} }
func (m *mockListener) Lookup(discv5.NodeID) []*discv5.Node { func (mockListener) Lookup(enode.ID) []*enode.Node {
panic("implement me") panic("implement me")
} }
func (m *mockListener) ReadRandomNodes([]*discv5.Node) int { func (mockListener) ReadRandomNodes([]*enode.Node) int {
panic("implement me") panic("implement me")
} }
func (m *mockListener) SetFallbackNodes([]*discv5.Node) error { func (mockListener) Resolve(*enode.Node) *enode.Node {
panic("implement me") panic("implement me")
} }
func (m *mockListener) Resolve(discv5.NodeID) *discv5.Node { func (mockListener) LookupRandom() []*enode.Node {
panic("implement me") panic("implement me")
} }
func (m *mockListener) RegisterTopic(discv5.Topic, <-chan struct{}) { func (mockListener) Ping(*enode.Node) error {
panic("implement me") panic("implement me")
} }
func (m *mockListener) SearchTopic(discv5.Topic, <-chan time.Duration, chan<- *discv5.Node, chan<- bool) { func (mockListener) RequestENR(*enode.Node) (*enode.Node, error) {
panic("implement me") panic("implement me")
} }
@@ -121,24 +122,26 @@ func TestService_Status_NotRunning(t *testing.T) {
func TestListenForNewNodes(t *testing.T) { func TestListenForNewNodes(t *testing.T) {
// setup bootnode // setup bootnode
cfg := &Config{}
port := 2000 port := 2000
cfg.Port = uint(port)
_, pkey := createAddrAndPrivKey(t) _, pkey := createAddrAndPrivKey(t)
ipAddr := net.ParseIP("127.0.0.1") ipAddr := net.ParseIP("127.0.0.1")
bootListener := createListener(ipAddr, port, pkey) bootListener := createListener(ipAddr, pkey, cfg)
defer bootListener.Close() defer bootListener.Close()
bootNode := bootListener.Self() bootNode := bootListener.Self()
cfg := &Config{ cfg = &Config{
BootstrapNodeAddr: bootNode.String(), BootstrapNodeAddr: bootNode.String(),
Encoding: "ssz", Encoding: "ssz",
} }
var listeners []*discv5.Network var listeners []*discover.UDPv5
var hosts []host.Host var hosts []host.Host
// setup other nodes // setup other nodes
for i := 1; i <= 5; i++ { for i := 1; i <= 5; i++ {
listener, h := createPeer(t, cfg, port+i) listener, h := createPeer(t, cfg, port+i)
listeners = append(listeners, listener.(*discv5.Network)) listeners = append(listeners, listener.(*discover.UDPv5))
hosts = append(hosts, h) hosts = append(hosts, h)
} }
@@ -160,7 +163,7 @@ func TestListenForNewNodes(t *testing.T) {
s.Start() s.Start()
defer s.Stop() defer s.Stop()
time.Sleep(2 * time.Second) time.Sleep(4 * time.Second)
peers := s.host.Network().Peers() peers := s.host.Network().Peers()
if len(peers) != 5 { if len(peers) != 5 {
t.Errorf("Not all peers added to peerstore, wanted %d but got %d", 5, len(peers)) t.Errorf("Not all peers added to peerstore, wanted %d but got %d", 5, len(peers))

View File

@@ -11,8 +11,11 @@ go_library(
deps = [ deps = [
"//shared/version:go_default_library", "//shared/version:go_default_library",
"@com_github_btcsuite_btcd//btcec:go_default_library", "@com_github_btcsuite_btcd//btcec:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/discv5:go_default_library", "@com_github_ethereum_go_ethereum//p2p/discover:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enode:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enr:go_default_library",
"@com_github_libp2p_go_libp2p_core//crypto:go_default_library", "@com_github_libp2p_go_libp2p_core//crypto:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library",
"@org_uber_go_automaxprocs//:go_default_library", "@org_uber_go_automaxprocs//:go_default_library",
], ],
@@ -32,8 +35,11 @@ go_image(
deps = [ deps = [
"//shared/version:go_default_library", "//shared/version:go_default_library",
"@com_github_btcsuite_btcd//btcec:go_default_library", "@com_github_btcsuite_btcd//btcec:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/discv5:go_default_library", "@com_github_ethereum_go_ethereum//p2p/discover:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enode:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enr:go_default_library",
"@com_github_libp2p_go_libp2p_core//crypto:go_default_library", "@com_github_libp2p_go_libp2p_core//crypto:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library",
"@org_uber_go_automaxprocs//:go_default_library", "@org_uber_go_automaxprocs//:go_default_library",
], ],
@@ -64,10 +70,12 @@ go_test(
name = "go_default_test", name = "go_default_test",
srcs = ["bootnode_test.go"], srcs = ["bootnode_test.go"],
embed = [":go_default_library"], embed = [":go_default_library"],
flaky = True,
deps = [ deps = [
"//shared/iputils:go_default_library", "//shared/iputils:go_default_library",
"@com_github_btcsuite_btcd//btcec:go_default_library", "@com_github_btcsuite_btcd//btcec:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/discv5:go_default_library", "@com_github_ethereum_go_ethereum//p2p/discover:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enode:go_default_library",
"@com_github_libp2p_go_libp2p_core//crypto:go_default_library", "@com_github_libp2p_go_libp2p_core//crypto:go_default_library",
"@org_uber_go_automaxprocs//:go_default_library", "@org_uber_go_automaxprocs//:go_default_library",
], ],

View File

@@ -17,8 +17,11 @@ import (
"net" "net"
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/crypto"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/shared/version" "github.com/prysmaticlabs/prysm/shared/version"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
_ "go.uber.org/automaxprocs" _ "go.uber.org/automaxprocs"
@@ -28,7 +31,7 @@ var (
debug = flag.Bool("debug", false, "Enable debug logging") debug = flag.Bool("debug", false, "Enable debug logging")
privateKey = flag.String("private", "", "Private key to use for peer ID") privateKey = flag.String("private", "", "Private key to use for peer ID")
port = flag.Int("port", 4000, "Port to listen for connections") port = flag.Int("port", 4000, "Port to listen for connections")
externalIP = flag.String("external-ip", "0.0.0.0", "External IP for the bootnode") externalIP = flag.String("external-ip", "127.0.0.1", "External IP for the bootnode")
log = logrus.WithField("prefix", "bootnode") log = logrus.WithField("prefix", "bootnode")
) )
@@ -41,37 +44,57 @@ func main() {
if *debug { if *debug {
logrus.SetLevel(logrus.DebugLevel) logrus.SetLevel(logrus.DebugLevel)
} }
cfg := discover.Config{
privKey := extractPrivateKey() PrivateKey: extractPrivateKey(),
listener := createListener(*externalIP, *port, privKey) }
listener := createListener(*externalIP, *port, cfg)
node := listener.Self() node := listener.Self()
log.Infof("Running bootnode, url: %s", node.String()) log.Infof("Running bootnode: %s", node.String())
select {} select {}
} }
func createListener(ipAddr string, port int, privKey *ecdsa.PrivateKey) *discv5.Network { func createListener(ipAddr string, port int, cfg discover.Config) *discover.UDPv5 {
ip := net.ParseIP(ipAddr) ip := net.ParseIP(ipAddr)
if ip.To4() == nil { if ip.To4() == nil {
log.Fatalf("IPV4 address not provided instead %s was provided", ipAddr) log.Fatalf("IPV4 address not provided instead %s was provided", ipAddr)
} }
udpAddr := &net.UDPAddr{ udpAddr := &net.UDPAddr{
IP: net.ParseIP(ipAddr), IP: ip,
Port: port, Port: port,
} }
conn, err := net.ListenUDP("udp4", udpAddr) conn, err := net.ListenUDP("udp4", udpAddr)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
localNode, err := createLocalNode(cfg.PrivateKey, ip, port)
if err != nil {
log.Fatal(err)
}
network, err := discv5.ListenUDP(privKey, conn, "", nil) network, err := discover.ListenV5(conn, localNode, cfg)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
return network return network
} }
func createLocalNode(privKey *ecdsa.PrivateKey, ipAddr net.IP, port int) (*enode.LocalNode, error) {
db, err := enode.OpenDB("")
if err != nil {
return nil, errors.Wrap(err, "Could not open node's peer database")
}
localNode := enode.NewLocalNode(db, privKey)
ipEntry := enr.IP(ipAddr)
udpEntry := enr.UDP(port)
localNode.Set(ipEntry)
localNode.Set(udpEntry)
return localNode, nil
}
func extractPrivateKey() *ecdsa.PrivateKey { func extractPrivateKey() *ecdsa.PrivateKey {
var privKey *ecdsa.PrivateKey var privKey *ecdsa.PrivateKey
if *privateKey != "" { if *privateKey != "" {

View File

@@ -4,9 +4,11 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand" "crypto/rand"
"testing" "testing"
"time"
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/crypto"
"github.com/prysmaticlabs/prysm/shared/iputils" "github.com/prysmaticlabs/prysm/shared/iputils"
_ "go.uber.org/automaxprocs" _ "go.uber.org/automaxprocs"
@@ -17,44 +19,43 @@ func TestBootnode_OK(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
privKey := extractPrivateKey() cfg := discover.Config{
listener := createListener(ipAddr, 4000, privKey) PrivateKey: extractPrivateKey(),
}
listener := createListener(ipAddr, 4000, cfg)
defer listener.Close() defer listener.Close()
privKey = extractPrivateKey() cfg.PrivateKey = extractPrivateKey()
listener2 := createListener(ipAddr, 4001, privKey) bootNode, err := enode.Parse(enode.ValidSchemes, listener.Self().String())
defer listener.Close()
err = listener.SetFallbackNodes([]*discv5.Node{listener2.Self()})
if err != nil {
t.Fatal(err)
}
err = listener2.SetFallbackNodes([]*discv5.Node{listener.Self()})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
cfg.Bootnodes = []*enode.Node{bootNode}
listener2 := createListener(ipAddr, 4001, cfg)
defer listener2.Close()
// test that both the nodes have the other peer stored in their local table. // test that both the nodes have the other peer stored in their local table.
listenerNode := listener.Self() listenerNode := listener.Self()
listenerNode2 := listener2.Self() listenerNode2 := listener2.Self()
nodes := listener.Lookup(listenerNode2.ID) time.Sleep(1 * time.Second)
if len(nodes) != 2 {
t.Errorf("Length of nodes stored in table is not expected. Wanted %d but got %d", 2, len(nodes)) nodes := listener.Lookup(listenerNode2.ID())
if len(nodes) == 0 {
t.Fatalf("Length of nodes stored in table is not expected. Wanted to be more than %d but got %d", 0, len(nodes))
} }
if nodes[0].ID != listenerNode2.ID { if nodes[0].ID() != listenerNode2.ID() {
t.Errorf("Wanted node ID of %s but got %s", listenerNode2.ID, nodes[1].ID) t.Errorf("Wanted node ID of %s but got %s", listenerNode2.ID(), nodes[1].ID())
} }
nodes = listener2.Lookup(listenerNode.ID) nodes = listener2.Lookup(listenerNode.ID())
if len(nodes) != 2 { if len(nodes) == 0 {
t.Errorf("Length of nodes stored in table is not expected. Wanted %d but got %d", 2, len(nodes)) t.Errorf("Length of nodes stored in table is not expected. Wanted to be more than %d but got %d", 0, len(nodes))
} }
if nodes[0].ID != listenerNode.ID { if nodes[0].ID() != listenerNode.ID() {
t.Errorf("Wanted node ID of %s but got %s", listenerNode.ID, nodes[1].ID) t.Errorf("Wanted node ID of %s but got %s", listenerNode.ID(), nodes[1].ID())
} }
} }

View File

@@ -0,0 +1,61 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
load("@io_bazel_rules_docker//go:image.bzl", "go_image")
load("@io_bazel_rules_docker//container:container.bzl", "container_bundle")
load("@io_bazel_rules_docker//contrib:push-all.bzl", "docker_push")
go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "github.com/prysmaticlabs/prysm/tools/enr-calculator",
visibility = ["//visibility:private"],
deps = [
"@com_github_btcsuite_btcd//btcec:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enode:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enr:go_default_library",
"@com_github_libp2p_go_libp2p_core//crypto:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_uber_go_automaxprocs//:go_default_library",
],
)
go_binary(
name = "enr-calculator",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
go_image(
name = "image",
srcs = ["main.go"],
goarch = "amd64",
goos = "linux",
importpath = "github.com/prysmaticlabs/prysm/tools/enr-calculator",
pure = "on",
race = "off",
static = "on",
tags = ["manual"],
visibility = ["//visibility:private"],
deps = [
"@com_github_btcsuite_btcd//btcec:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enode:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enr:go_default_library",
"@com_github_libp2p_go_libp2p_core//crypto:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_uber_go_automaxprocs//:go_default_library",
],
)
container_bundle(
name = "image_bundle",
images = {
"gcr.io/prysmaticlabs/prysm/enr-calculator:latest": ":image",
"gcr.io/prysmaticlabs/prysm/enr-calculator:{DOCKER_TAG}": ":image",
},
tags = ["manual"],
)
docker_push(
name = "push_images",
bundle = ":image_bundle",
tags = ["manual"],
)

View File

@@ -0,0 +1,16 @@
# ENR Calculator
To generate the ENR of a node
```
bazel run //tools/enr-calculator:enr-calculator -- --private CAISIJXSWjkbgprwuo01QCRegULoNIOZ0yTl1fLz5N0SsJCS --ipAddress 127.0.0.1 --port 2000
```
This will deterministically generate an ENR from given inputs of private key, ip address and udp port.
Output of the above command:
```
INFO[0000] enr:-IS4QKk3gX9EqxA3x83AbCiyAnSuPDMvK52dC50Hm1XGDd5tEyQhM3VcJL-4b8kDg5APz_povv0Syqk0nancoNW-cq0BgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQM1E5yUsp9vDQj1tv3ZWXCvaFBvrPNdz8KPI1NhxfQWzIN1ZHCCB9A
```

View File

@@ -0,0 +1,64 @@
// This binary is a simple rest API endpoint to calculate
// the ENR value of a node given its private key,ip address and port.
package main
import (
"crypto/ecdsa"
"flag"
"net"
"github.com/btcsuite/btcd/btcec"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/libp2p/go-libp2p-core/crypto"
log "github.com/sirupsen/logrus"
_ "go.uber.org/automaxprocs"
)
var (
privateKey = flag.String("private", "", "Base-64 encoded Private key to use for calculation of ENR")
port = flag.Int("port", 0, "Port to use for calculation of ENR")
ipAddr = flag.String("ipAddress", "", "IP to use in calculation of ENR")
)
func main() {
flag.Parse()
if len(*privateKey) == 0 {
log.Fatal("No private key given")
}
decodedKey, err := crypto.ConfigDecodeKey(*privateKey)
if err != nil {
log.Fatalf("Unable to decode private key: %v\n", err)
}
privatekey, err := crypto.UnmarshalPrivateKey(decodedKey)
if err != nil {
log.Fatalf("Unable to unmarshal private key: %v\n", err)
}
ecdsaPrivKey := (*ecdsa.PrivateKey)((*btcec.PrivateKey)(privatekey.(*crypto.Secp256k1PrivateKey)))
if net.ParseIP(*ipAddr).To4() == nil {
log.Fatalf("Invalid ipv4 address given: %v\n", err)
}
if *port == 0 {
log.Fatalf("Invalid udp port given: %v\n", err)
return
}
db, err := enode.OpenDB("")
defer db.Close()
if err != nil {
log.Fatalf("Could not open node's peer database: %v\n", err)
return
}
localNode := enode.NewLocalNode(db, ecdsaPrivKey)
ipEntry := enr.IP(net.ParseIP(*ipAddr))
udpEntry := enr.UDP(*port)
localNode.Set(ipEntry)
localNode.Set(udpEntry)
log.Info(localNode.Node().String())
}