Browse Source

cmd/swarm, p2p, swarm: Enable ENR in binary/execadapter (#19309)

* cmd/swarm, p2p, swarm: Enable ENR in binary/execadapter

* cmd/p2p/swarm: Remove comments + config.Enode nomarshal

* p2p/simulations: Remove superfluous error check

* p2p/simulation: Move init enode comment

* swarm/api: Check error in config test

* swarm, p2p/simulations, cmd/swarm: Use nodekey in binary record sign

* cmd/swarm: Make nodekey available for swarm api config
lash 6 years ago
parent
commit
09924cbcaa

+ 6 - 2
cmd/swarm/config.go

@@ -123,18 +123,22 @@ func buildConfig(ctx *cli.Context) (config *bzzapi.Config, err error) {
 }
 
 //finally, after the configuration build phase is finished, initialize
-func initSwarmNode(config *bzzapi.Config, stack *node.Node, ctx *cli.Context) {
+func initSwarmNode(config *bzzapi.Config, stack *node.Node, ctx *cli.Context, nodeconfig *node.Config) error {
 	//at this point, all vars should be set in the Config
 	//get the account for the provided swarm account
 	prvkey := getAccount(config.BzzAccount, ctx, stack)
 	//set the resolved config path (geth --datadir)
 	config.Path = expandPath(stack.InstanceDir())
 	//finally, initialize the configuration
-	config.Init(prvkey)
+	err := config.Init(prvkey, nodeconfig.NodeKey())
+	if err != nil {
+		return err
+	}
 	//configuration phase completed here
 	log.Debug("Starting Swarm with the following parameters:")
 	//after having created the config, print it to screen
 	log.Debug(printConfig(config))
+	return nil
 }
 
 //configFileOverride overrides the current config with the config file, if a config file has been provided

+ 4 - 1
cmd/swarm/main.go

@@ -298,7 +298,10 @@ func bzzd(ctx *cli.Context) error {
 
 	//a few steps need to be done after the config phase is completed,
 	//due to overriding behavior
-	initSwarmNode(bzzconfig, stack, ctx)
+	err = initSwarmNode(bzzconfig, stack, ctx, &cfg)
+	if err != nil {
+		return err
+	}
 	//register BZZ as node.Service in the ethereum node
 	registerBzzService(bzzconfig, stack)
 	//start the node

+ 12 - 0
p2p/simulations/adapters/exec.go

@@ -92,6 +92,10 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) {
 		return nil, fmt.Errorf("error creating node directory: %s", err)
 	}
 
+	err := config.initDummyEnode()
+	if err != nil {
+		return nil, err
+	}
 	// generate the config
 	conf := &execNodeConfig{
 		Stack: node.DefaultConfig,
@@ -407,6 +411,14 @@ func startExecNodeStack() (*node.Node, error) {
 	if err := json.Unmarshal([]byte(confEnv), &conf); err != nil {
 		return nil, fmt.Errorf("error decoding %s: %v", envNodeConfig, err)
 	}
+	// TODO verify that ListenAddr will contain the correct tcp addr
+	// if we should start using exec adapters with other host than local
+	nodeTcpConn, err := net.ResolveTCPAddr("tcp", conf.Stack.P2P.ListenAddr)
+	if err != nil {
+		conf.Node.initDummyEnode()
+	} else {
+		conf.Node.initEnode(nodeTcpConn.IP, nodeTcpConn.Port, nodeTcpConn.Port)
+	}
 	conf.Stack.P2P.PrivateKey = conf.Node.PrivateKey
 	conf.Stack.Logger = log.New("node.id", conf.Node.ID.String())
 

+ 2 - 16
p2p/simulations/adapters/inproc.go

@@ -28,7 +28,6 @@ import (
 	"github.com/ethereum/go-ethereum/node"
 	"github.com/ethereum/go-ethereum/p2p"
 	"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/rpc"
 )
@@ -93,23 +92,10 @@ 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)
+	err := config.initDummyEnode()
 	if err != nil {
-		return nil, fmt.Errorf("unable to create enode: %v", err)
+		return nil, err
 	}
-	log.Trace("simnode new", "record", config.Record)
-	config.node = nod
 
 	n, err := node.New(&node.Config{
 		P2P: p2p.Config{

+ 28 - 0
p2p/simulations/adapters/types.go

@@ -27,6 +27,7 @@ import (
 
 	"github.com/docker/docker/pkg/reexec"
 	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/node"
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p/enode"
@@ -265,3 +266,30 @@ func RegisterServices(services Services) {
 		os.Exit(0)
 	}
 }
+
+// adds the host part to the configuration's ENR, signs it
+// creates and  the corresponding enode object to the configuration
+func (n *NodeConfig) initEnode(ip net.IP, tcpport int, udpport int) error {
+	enrIp := enr.IP(ip)
+	n.Record.Set(&enrIp)
+	enrTcpPort := enr.TCP(tcpport)
+	n.Record.Set(&enrTcpPort)
+	enrUdpPort := enr.UDP(tcpport)
+	n.Record.Set(&enrUdpPort)
+
+	err := enode.SignV4(&n.Record, n.PrivateKey)
+	if err != nil {
+		return fmt.Errorf("unable to generate ENR: %v", err)
+	}
+	nod, err := enode.New(enode.V4ID{}, &n.Record)
+	if err != nil {
+		return fmt.Errorf("unable to create enode: %v", err)
+	}
+	log.Trace("simnode new", "record", n.Record)
+	n.node = nod
+	return nil
+}
+
+func (n *NodeConfig) initDummyEnode() error {
+	return n.initEnode(net.IPv4(127, 0, 0, 1), 0, 0)
+}

+ 44 - 18
swarm/api/config.go

@@ -29,7 +29,6 @@ import (
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/node"
 	"github.com/ethereum/go-ethereum/p2p/enode"
-	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/pss"
 	"github.com/ethereum/go-ethereum/swarm/services/swap"
@@ -58,7 +57,7 @@ type Config struct {
 	Port                 string
 	PublicKey            string
 	BzzKey               string
-	NodeID               string
+	Enode                *enode.Node `toml:"-"`
 	NetworkID            uint64
 	SwapEnabled          bool
 	SyncEnabled          bool
@@ -104,33 +103,38 @@ func NewConfig() (c *Config) {
 
 //some config params need to be initialized after the complete
 //config building phase is completed (e.g. due to overriding flags)
-func (c *Config) Init(prvKey *ecdsa.PrivateKey) {
+func (c *Config) Init(prvKey *ecdsa.PrivateKey, nodeKey *ecdsa.PrivateKey) error {
 
-	address := crypto.PubkeyToAddress(prvKey.PublicKey)
-	c.Path = filepath.Join(c.Path, "bzz-"+common.Bytes2Hex(address.Bytes()))
-	err := os.MkdirAll(c.Path, os.ModePerm)
+	// create swarm dir and record key
+	err := c.createAndSetPath(c.Path, prvKey)
 	if err != nil {
-		log.Error(fmt.Sprintf("Error creating root swarm data directory: %v", err))
-		return
+		return fmt.Errorf("Error creating root swarm data directory: %v", err)
+	}
+	c.setKey(prvKey)
+
+	// create the new enode record
+	// signed with the ephemeral node key
+	enodeParams := &network.EnodeParams{
+		PrivateKey: prvKey,
+		EnodeKey:   nodeKey,
+		Lightnode:  c.LightNodeEnabled,
+		Bootnode:   c.BootnodeMode,
+	}
+	c.Enode, err = network.NewEnode(enodeParams)
+	if err != nil {
+		return fmt.Errorf("Error creating enode: %v", err)
 	}
 
-	pubkey := crypto.FromECDSAPub(&prvKey.PublicKey)
-	pubkeyhex := common.ToHex(pubkey)
-	keyhex := hexutil.Encode(network.PrivateKeyToBzzKey(prvKey))
-
-	c.PublicKey = pubkeyhex
-	c.BzzKey = keyhex
-	c.NodeID = enode.PubkeyToIDV4(&prvKey.PublicKey).String()
-
+	// initialize components that depend on the swarm instance's private key
 	if c.SwapEnabled {
 		c.Swap.Init(c.Contract, prvKey)
 	}
 
-	c.privateKey = prvKey
 	c.LocalStoreParams.Init(c.Path)
-	c.LocalStoreParams.BaseKey = common.FromHex(keyhex)
+	c.LocalStoreParams.BaseKey = common.FromHex(c.BzzKey)
 
 	c.Pss = c.Pss.WithPrivateKey(c.privateKey)
+	return nil
 }
 
 func (c *Config) ShiftPrivateKey() (privKey *ecdsa.PrivateKey) {
@@ -140,3 +144,25 @@ func (c *Config) ShiftPrivateKey() (privKey *ecdsa.PrivateKey) {
 	}
 	return privKey
 }
+
+func (c *Config) setKey(prvKey *ecdsa.PrivateKey) {
+	bzzkeybytes := network.PrivateKeyToBzzKey(prvKey)
+	pubkey := crypto.FromECDSAPub(&prvKey.PublicKey)
+	pubkeyhex := hexutil.Encode(pubkey)
+	keyhex := hexutil.Encode(bzzkeybytes)
+
+	c.privateKey = prvKey
+	c.PublicKey = pubkeyhex
+	c.BzzKey = keyhex
+}
+
+func (c *Config) createAndSetPath(datadirPath string, prvKey *ecdsa.PrivateKey) error {
+	address := crypto.PubkeyToAddress(prvKey.PublicKey)
+	bzzdirPath := filepath.Join(datadirPath, "bzz-"+common.Bytes2Hex(address.Bytes()))
+	err := os.MkdirAll(bzzdirPath, os.ModePerm)
+	if err != nil {
+		return err
+	}
+	c.Path = bzzdirPath
+	return nil
+}

+ 9 - 1
swarm/api/config_test.go

@@ -27,11 +27,16 @@ import (
 func TestConfig(t *testing.T) {
 
 	var hexprvkey = "65138b2aa745041b372153550584587da326ab440576b2a1191dd95cee30039c"
+	var hexnodekey = "75138b2aa745041b372153550584587da326ab440576b2a1191dd95cee30039c"
 
 	prvkey, err := crypto.HexToECDSA(hexprvkey)
 	if err != nil {
 		t.Fatalf("failed to load private key: %v", err)
 	}
+	nodekey, err := crypto.HexToECDSA(hexnodekey)
+	if err != nil {
+		t.Fatalf("failed to load private key: %v", err)
+	}
 
 	one := NewConfig()
 	two := NewConfig()
@@ -41,7 +46,10 @@ func TestConfig(t *testing.T) {
 		t.Fatal("Two default configs are not equal")
 	}
 
-	one.Init(prvkey)
+	err = one.Init(prvkey, nodekey)
+	if err != nil {
+		t.Fatal(err)
+	}
 
 	//the init function should set the following fields
 	if one.BzzKey == "" {

+ 35 - 0
swarm/network/network.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/p2p/enode"
+	"github.com/ethereum/go-ethereum/p2p/enr"
 )
 
 // BzzAddr implements the PeerAddr interface
@@ -68,3 +69,37 @@ func PrivateKeyToBzzKey(prvKey *ecdsa.PrivateKey) []byte {
 	pubkeyBytes := crypto.FromECDSAPub(&prvKey.PublicKey)
 	return crypto.Keccak256Hash(pubkeyBytes).Bytes()
 }
+
+type EnodeParams struct {
+	PrivateKey *ecdsa.PrivateKey
+	EnodeKey   *ecdsa.PrivateKey
+	Lightnode  bool
+	Bootnode   bool
+}
+
+func NewEnodeRecord(params *EnodeParams) (*enr.Record, error) {
+
+	if params.PrivateKey == nil {
+		return nil, fmt.Errorf("all param private keys must be defined")
+	}
+
+	bzzkeybytes := PrivateKeyToBzzKey(params.PrivateKey)
+
+	var record enr.Record
+	record.Set(NewENRAddrEntry(bzzkeybytes))
+	record.Set(ENRLightNodeEntry(params.Lightnode))
+	record.Set(ENRBootNodeEntry(params.Bootnode))
+	return &record, nil
+}
+
+func NewEnode(params *EnodeParams) (*enode.Node, error) {
+	record, err := NewEnodeRecord(params)
+	if err != nil {
+		return nil, err
+	}
+	err = enode.SignV4(record, params.EnodeKey)
+	if err != nil {
+		return nil, fmt.Errorf("ENR create fail: %v", err)
+	}
+	return enode.New(enode.V4ID{}, record)
+}

+ 9 - 9
swarm/network/simulation/node.go

@@ -102,16 +102,16 @@ func (s *Simulation) AddNode(opts ...AddNodeOption) (id enode.ID, err error) {
 	// 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
+	// so we just let them be set to 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)
+	enodeParams := &network.EnodeParams{
+		PrivateKey: conf.PrivateKey,
+	}
+	record, err := network.NewEnodeRecord(enodeParams)
+	if err != nil {
+		return enode.ID{}, err
+	}
+	conf.Record = *record
 
 	// Add the bzz address to the node config
 	node, err := s.Net.NewNodeWithConfig(conf)

+ 5 - 1
swarm/network_test.go

@@ -311,8 +311,12 @@ func testSwarmNetwork(t *testing.T, o *testSwarmNetworkOptions, steps ...testSwa
 			if err != nil {
 				return nil, cleanup, err
 			}
+			nodekey, err := crypto.GenerateKey()
+			if err != nil {
+				return nil, cleanup, err
+			}
 
-			config.Init(privkey)
+			config.Init(privkey, nodekey)
 			config.DeliverySkipCheck = o.SkipCheck
 			config.Port = ""
 

+ 1 - 5
swarm/swarm.go

@@ -36,7 +36,6 @@ import (
 	"github.com/ethereum/go-ethereum/ethclient"
 	"github.com/ethereum/go-ethereum/metrics"
 	"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/params"
 	"github.com/ethereum/go-ethereum/rpc"
@@ -171,10 +170,7 @@ func NewSwarm(config *api.Config, mockStore *mock.NodeStore) (self *Swarm, err e
 		self.accountingMetrics = protocols.SetupAccountingMetrics(10*time.Second, filepath.Join(config.Path, "metrics.db"))
 	}
 
-	var nodeID enode.ID
-	if err := nodeID.UnmarshalText([]byte(config.NodeID)); err != nil {
-		return nil, err
-	}
+	nodeID := config.Enode.ID()
 
 	syncing := stream.SyncingAutoSubscribe
 	if !config.SyncEnabled || config.LightNodeEnabled {

+ 10 - 2
swarm/swarm_test.go

@@ -170,8 +170,12 @@ func TestNewSwarm(t *testing.T) {
 			if err != nil {
 				t.Fatal(err)
 			}
+			nodekey, err := crypto.GenerateKey()
+			if err != nil {
+				t.Fatal(err)
+			}
 
-			config.Init(privkey)
+			config.Init(privkey, nodekey)
 
 			if tc.configure != nil {
 				tc.configure(config)
@@ -307,8 +311,12 @@ func TestLocalStoreAndRetrieve(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
+	nodekey, err := crypto.GenerateKey()
+	if err != nil {
+		t.Fatal(err)
+	}
 
-	config.Init(privkey)
+	config.Init(privkey, nodekey)
 
 	swarm, err := NewSwarm(config, nil)
 	if err != nil {