diff --git a/cmd/swarm/config.go b/cmd/swarm/config.go index a941b3757..32cd442a0 100644 --- a/cmd/swarm/config.go +++ b/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 diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index 06d5b0bb2..a4041eb3d 100644 --- a/cmd/swarm/main.go +++ b/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 diff --git a/p2p/simulations/adapters/exec.go b/p2p/simulations/adapters/exec.go index bd8bcbc85..6d1c13f3c 100644 --- a/p2p/simulations/adapters/exec.go +++ b/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()) diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index 991573c2d..c1cf23a17 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/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) + err := config.initDummyEnode() if err != nil { - return nil, fmt.Errorf("unable to generate ENR: %v", err) + return nil, 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{ P2P: p2p.Config{ diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go index e5e4d159c..ed4013cb7 100644 --- a/p2p/simulations/adapters/types.go +++ b/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) +} diff --git a/swarm/api/config.go b/swarm/api/config.go index 1fa6c4fdf..0a7100c57 100644 --- a/swarm/api/config.go +++ b/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 +} diff --git a/swarm/api/config_test.go b/swarm/api/config_test.go index bd7e1d870..a55da6f7b 100644 --- a/swarm/api/config_test.go +++ b/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 == "" { diff --git a/swarm/network/network.go b/swarm/network/network.go index 1f9108941..c5c7e9b2f 100644 --- a/swarm/network/network.go +++ b/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) +} diff --git a/swarm/network/simulation/node.go b/swarm/network/simulation/node.go index 1ab9ddfd0..2d618a29d 100644 --- a/swarm/network/simulation/node.go +++ b/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) diff --git a/swarm/network_test.go b/swarm/network_test.go index 3221b852b..97bdd07b1 100644 --- a/swarm/network_test.go +++ b/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 = "" diff --git a/swarm/swarm.go b/swarm/swarm.go index ae78ccd48..61813e23f 100644 --- a/swarm/swarm.go +++ b/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 { diff --git a/swarm/swarm_test.go b/swarm/swarm_test.go index d85eb9118..2a5b28513 100644 --- a/swarm/swarm_test.go +++ b/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 {