diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index bdfbf1c732..48c45f99a4 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -28,6 +28,7 @@ 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" ) @@ -87,6 +88,23 @@ 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) + } + config.node = nod + n, err := node.New(&node.Config{ P2P: p2p.Config{ PrivateKey: config.PrivateKey, diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go index 31856b76dd..e5e4d159cb 100644 --- a/p2p/simulations/adapters/types.go +++ b/p2p/simulations/adapters/types.go @@ -30,6 +30,7 @@ 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/rpc" ) @@ -99,6 +100,12 @@ type NodeConfig struct { // services registered by calling the RegisterService function) Services []string + // Enode + node *enode.Node + + // ENR Record with entries to overwrite + Record enr.Record + // function to sanction or prevent suggesting a peer 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. 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 // PrivateKey func RandomNodeConfig() *NodeConfig { - key, err := crypto.GenerateKey() + prvkey, err := crypto.GenerateKey() if err != nil { panic("unable to generate key") } - id := enode.PubkeyToIDV4(&key.PublicKey) port, err := assignTCPPort() if err != nil { panic("unable to assign tcp port") } + + enodId := enode.PubkeyToIDV4(&prvkey.PublicKey) return &NodeConfig{ - ID: id, - Name: fmt.Sprintf("node_%s", id.String()), - PrivateKey: key, + PrivateKey: prvkey, + ID: enodId, + Name: fmt.Sprintf("node_%s", enodId.String()), Port: port, EnableMsgEvents: true, } diff --git a/swarm/api/config.go b/swarm/api/config.go index b8de16f5f0..b4448f9073 100644 --- a/swarm/api/config.go +++ b/swarm/api/config.go @@ -24,6 +24,7 @@ import ( "time" "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/crypto" "github.com/ethereum/go-ethereum/node" @@ -117,7 +118,8 @@ func (c *Config) Init(prvKey *ecdsa.PrivateKey) { pubkey := crypto.FromECDSAPub(&prvKey.PublicKey) pubkeyhex := common.ToHex(pubkey) - keyhex := crypto.Keccak256Hash(pubkey).Hex() + //keyhex := crypto.Keccak256Hash(pubkey).Hex() + keyhex := hexutil.Encode(PrivateKeyToBzzKey(prvKey)) c.PublicKey = pubkeyhex c.BzzKey = keyhex @@ -134,6 +136,11 @@ func (c *Config) Init(prvKey *ecdsa.PrivateKey) { c.Pss = c.Pss.WithPrivateKey(c.privateKey) } +func PrivateKeyToBzzKey(prvKey *ecdsa.PrivateKey) []byte { + pubkeyBytes := crypto.FromECDSAPub(&prvKey.PublicKey) + return crypto.Keccak256Hash(pubkeyBytes).Bytes() +} + func (c *Config) ShiftPrivateKey() (privKey *ecdsa.PrivateKey) { if c.privateKey != nil { privKey = c.privateKey diff --git a/swarm/network/enr.go b/swarm/network/enr.go new file mode 100644 index 0000000000..38e7e6707f --- /dev/null +++ b/swarm/network/enr.go @@ -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()), + } +} diff --git a/swarm/network/simulation/node.go b/swarm/network/simulation/node.go index 4c3834de30..16f754e196 100644 --- a/swarm/network/simulation/node.go +++ b/swarm/network/simulation/node.go @@ -28,6 +28,8 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/simulations" "github.com/ethereum/go-ethereum/p2p/simulations/adapters" + bzzapi "github.com/ethereum/go-ethereum/swarm/api" + "github.com/ethereum/go-ethereum/swarm/network" ) // NodeIDs returns NodeIDs for all nodes in the network. @@ -96,10 +98,28 @@ func (s *Simulation) AddNode(opts ...AddNodeOption) (id enode.ID, err error) { if len(conf.Services) == 0 { 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 := bzzapi.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) if err != nil { return id, err } + return node.ID(), s.Net.Start(node.ID()) }