Browse Source

all: new p2p node representation (#17643)

Package p2p/enode provides a generalized representation of p2p nodes
which can contain arbitrary information in key/value pairs. It is also
the new home for the node database. The "v4" identity scheme is also
moved here from p2p/enr to remove the dependency on Ethereum crypto from
that package.

Record signature handling is changed significantly. The identity scheme
registry is removed and acceptable schemes must be passed to any method
that needs identity. This means records must now be validated explicitly
after decoding.

The enode API is designed to make signature handling easy and safe: most
APIs around the codebase work with enode.Node, which is a wrapper around
a valid record. Going from enr.Record to enode.Node requires a valid
signature.

* p2p/discover: port to p2p/enode

This ports the discovery code to the new node representation in
p2p/enode. The wire protocol is unchanged, this can be considered a
refactoring change. The Kademlia table can now deal with nodes using an
arbitrary identity scheme. This requires a few incompatible API changes:

  - Table.Lookup is not available anymore. It used to take a public key
    as argument because v4 protocol requires one. Its replacement is
    LookupRandom.
  - Table.Resolve takes *enode.Node instead of NodeID. This is also for
    v4 protocol compatibility because nodes cannot be looked up by ID
    alone.
  - Types Node and NodeID are gone. Further commits in the series will be
    fixes all over the the codebase to deal with those removals.

* p2p: port to p2p/enode and discovery changes

This adapts package p2p to the changes in p2p/discover. All uses of
discover.Node and discover.NodeID are replaced by their equivalents from
p2p/enode.

New API is added to retrieve the enode.Node instance of a peer. The
behavior of Server.Self with discovery disabled is improved. It now
tries much harder to report a working IP address, falling back to
127.0.0.1 if no suitable address can be determined through other means.
These changes were needed for tests of other packages later in the
series.

* p2p/simulations, p2p/testing: port to p2p/enode

No surprises here, mostly replacements of discover.Node, discover.NodeID
with their new equivalents. The 'interesting' API changes are:

 - testing.ProtocolSession tracks complete nodes, not just their IDs.
 - adapters.NodeConfig has a new method to create a complete node.

These changes were needed to make swarm tests work.

Note that the NodeID change makes the code incompatible with old
simulation snapshots.

* whisper/whisperv5, whisper/whisperv6: port to p2p/enode

This port was easy because whisper uses []byte for node IDs and
URL strings in the API.

* eth: port to p2p/enode

Again, easy to port because eth uses strings for node IDs and doesn't
care about node information in any way.

* les: port to p2p/enode

Apart from replacing discover.NodeID with enode.ID, most changes are in
the server pool code. It now deals with complete nodes instead
of (Pubkey, IP, Port) triples. The database format is unchanged for now,
but we should probably change it to use the node database later.

* node: port to p2p/enode

This change simply replaces discover.Node and discover.NodeID with their
new equivalents.

* swarm/network: port to p2p/enode

Swarm has its own node address representation, BzzAddr, containing both
an overlay address (the hash of a secp256k1 public key) and an underlay
address (enode:// URL).

There are no changes to the BzzAddr format in this commit, but certain
operations such as creating a BzzAddr from a node ID are now impossible
because node IDs aren't public keys anymore.

Most swarm-related changes in the series remove uses of
NewAddrFromNodeID, replacing it with NewAddr which takes a complete node
as argument. ToOverlayAddr is removed because we can just use the node
ID directly.
Felix Lange 7 years ago
parent
commit
30cd5c1854
100 changed files with 2913 additions and 2630 deletions
  1. 2 1
      cmd/bootnode/main.go
  2. 5 3
      cmd/faucet/faucet.go
  3. 2 2
      cmd/p2psim/main.go
  4. 3 3
      cmd/swarm/main.go
  5. 7 6
      cmd/swarm/run_test.go
  6. 3 3
      cmd/utils/flags.go
  7. 5 5
      cmd/wnode/main.go
  8. 2 2
      eth/handler.go
  9. 2 2
      eth/helper_test.go
  10. 2 2
      eth/sync.go
  11. 3 3
      eth/sync_test.go
  12. 2 2
      les/commons.go
  13. 2 3
      les/handler.go
  14. 3 3
      les/helper_test.go
  15. 0 4
      les/peer.go
  16. 5 8
      les/protocol.go
  17. 80 41
      les/serverpool.go
  18. 5 5
      node/api.go
  19. 6 6
      node/config.go
  20. 50 52
      p2p/dial.go
  21. 226 217
      p2p/dial_test.go
  22. 47 375
      p2p/discover/node.go
  23. 108 116
      p2p/discover/table.go
  24. 244 316
      p2p/discover/table_test.go
  25. 167 0
      p2p/discover/table_util_test.go
  26. 72 50
      p2p/discover/udp.go
  27. 43 39
      p2p/discover/udp_test.go
  28. 86 40
      p2p/enode/idscheme.go
  29. 74 0
      p2p/enode/idscheme_test.go
  30. 248 0
      p2p/enode/node.go
  31. 62 0
      p2p/enode/node_test.go
  32. 69 90
      p2p/enode/nodedb.go
  33. 105 113
      p2p/enode/nodedb_test.go
  34. 194 0
      p2p/enode/urlv4.go
  35. 49 141
      p2p/enode/urlv4_test.go
  36. 95 73
      p2p/enr/enr.go
  37. 45 79
      p2p/enr/enr_test.go
  38. 0 26
      p2p/enr/entries.go
  39. 0 36
      p2p/enr/idscheme_test.go
  40. 3 3
      p2p/message.go
  41. 22 14
      p2p/peer.go
  42. 2 2
      p2p/peer_test.go
  43. 2 2
      p2p/protocol.go
  44. 22 22
      p2p/protocols/protocol_test.go
  45. 24 31
      p2p/rlpx.go
  46. 20 23
      p2p/rlpx_test.go
  47. 116 81
      p2p/server.go
  48. 66 64
      p2p/server_test.go
  49. 2 2
      p2p/simulations/adapters/docker.go
  50. 5 5
      p2p/simulations/adapters/exec.go
  51. 14 14
      p2p/simulations/adapters/inproc.go
  52. 11 8
      p2p/simulations/adapters/types.go
  53. 3 3
      p2p/simulations/examples/ping-pong.go
  54. 5 3
      p2p/simulations/http.go
  55. 8 8
      p2p/simulations/http_test.go
  56. 4 4
      p2p/simulations/mocker.go
  57. 2 2
      p2p/simulations/mocker_test.go
  58. 32 32
      p2p/simulations/network.go
  59. 5 5
      p2p/simulations/network_test.go
  60. 7 7
      p2p/simulations/simulation.go
  61. 6 6
      p2p/testing/peerpool.go
  62. 17 17
      p2p/testing/protocolsession.go
  63. 6 6
      p2p/testing/protocoltester.go
  64. 2 2
      swarm/api/config.go
  65. 2 2
      swarm/network/discovery.go
  66. 3 3
      swarm/network/discovery_test.go
  67. 19 19
      swarm/network/fetcher.go
  68. 9 9
      swarm/network/fetcher_test.go
  69. 32 8
      swarm/network/hive.go
  70. 10 7
      swarm/network/hive_test.go
  71. 11 12
      swarm/network/networkid_test.go
  72. 32 53
      swarm/network/protocol.go
  73. 23 27
      swarm/network/protocol_test.go
  74. 7 9
      swarm/network/simulation/bucket.go
  75. 10 10
      swarm/network/simulation/connect.go
  76. 5 5
      swarm/network/simulation/connect_test.go
  77. 4 5
      swarm/network/simulation/events.go
  78. 1 1
      swarm/network/simulation/example_test.go
  79. 5 6
      swarm/network/simulation/kademlia.go
  80. 1 1
      swarm/network/simulation/kademlia_test.go
  81. 26 26
      swarm/network/simulation/node.go
  82. 3 3
      swarm/network/simulation/node_test.go
  83. 4 4
      swarm/network/simulation/service.go
  84. 4 4
      swarm/network/simulation/simulation.go
  85. 16 20
      swarm/network/simulations/discovery/discovery_test.go
  86. 0 0
      swarm/network/simulations/discovery/jsonsnapshot.txt
  87. 1 0
      swarm/network/simulations/discovery/snapshot.json
  88. 7 7
      swarm/network/simulations/overlay.go
  89. 3 3
      swarm/network/simulations/overlay_test.go
  90. 4 4
      swarm/network/stream/common_test.go
  91. 3 3
      swarm/network/stream/delivery.go
  92. 24 25
      swarm/network/stream/delivery_test.go
  93. 7 8
      swarm/network/stream/intervals_test.go
  94. 3 3
      swarm/network/stream/messages.go
  95. 17 19
      swarm/network/stream/snapshot_retrieval_test.go
  96. 22 25
      swarm/network/stream/snapshot_sync_test.go
  97. 18 18
      swarm/network/stream/stream.go
  98. 39 39
      swarm/network/stream/streamer_test.go
  99. 1 1
      swarm/network/stream/syncer.go
  100. 8 8
      swarm/network/stream/syncer_test.go

+ 2 - 1
cmd/bootnode/main.go

@@ -29,6 +29,7 @@ import (
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/p2p/discover"
 	"github.com/ethereum/go-ethereum/p2p/discover"
 	"github.com/ethereum/go-ethereum/p2p/discv5"
 	"github.com/ethereum/go-ethereum/p2p/discv5"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/nat"
 	"github.com/ethereum/go-ethereum/p2p/nat"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 )
 )
@@ -85,7 +86,7 @@ func main() {
 	}
 	}
 
 
 	if *writeAddr {
 	if *writeAddr {
-		fmt.Printf("%v\n", discover.PubkeyID(&nodeKey.PublicKey))
+		fmt.Printf("%v\n", enode.PubkeyToIDV4(&nodeKey.PublicKey))
 		os.Exit(0)
 		os.Exit(0)
 	}
 	}
 
 

+ 5 - 3
cmd/faucet/faucet.go

@@ -54,8 +54,8 @@ import (
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"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/discover"
 	"github.com/ethereum/go-ethereum/p2p/discv5"
 	"github.com/ethereum/go-ethereum/p2p/discv5"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/nat"
 	"github.com/ethereum/go-ethereum/p2p/nat"
 	"github.com/ethereum/go-ethereum/params"
 	"github.com/ethereum/go-ethereum/params"
 	"golang.org/x/net/websocket"
 	"golang.org/x/net/websocket"
@@ -255,8 +255,10 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
 		return nil, err
 		return nil, err
 	}
 	}
 	for _, boot := range enodes {
 	for _, boot := range enodes {
-		old, _ := discover.ParseNode(boot.String())
-		stack.Server().AddPeer(old)
+		old, err := enode.ParseV4(boot.String())
+		if err != nil {
+			stack.Server().AddPeer(old)
+		}
 	}
 	}
 	// Attach to the client and retrieve and interesting metadatas
 	// Attach to the client and retrieve and interesting metadatas
 	api, err := stack.Attach()
 	api, err := stack.Attach()

+ 2 - 2
cmd/p2psim/main.go

@@ -47,7 +47,7 @@ import (
 
 
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"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/rpc"
 	"github.com/ethereum/go-ethereum/rpc"
@@ -285,7 +285,7 @@ func createNode(ctx *cli.Context) error {
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		config.ID = discover.PubkeyID(&privKey.PublicKey)
+		config.ID = enode.PubkeyToIDV4(&privKey.PublicKey)
 		config.PrivateKey = privKey
 		config.PrivateKey = privKey
 	}
 	}
 	if services := ctx.String("services"); services != "" {
 	if services := ctx.String("services"); services != "" {

+ 3 - 3
cmd/swarm/main.go

@@ -38,7 +38,7 @@ import (
 	"github.com/ethereum/go-ethereum/internal/debug"
 	"github.com/ethereum/go-ethereum/internal/debug"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/node"
 	"github.com/ethereum/go-ethereum/node"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/swarm"
 	"github.com/ethereum/go-ethereum/swarm"
 	bzzapi "github.com/ethereum/go-ethereum/swarm/api"
 	bzzapi "github.com/ethereum/go-ethereum/swarm/api"
 	swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics"
 	swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics"
@@ -788,10 +788,10 @@ func setSwarmBootstrapNodes(ctx *cli.Context, cfg *node.Config) {
 		return
 		return
 	}
 	}
 
 
-	cfg.P2P.BootstrapNodes = []*discover.Node{}
+	cfg.P2P.BootstrapNodes = []*enode.Node{}
 
 
 	for _, url := range SwarmBootnodes {
 	for _, url := range SwarmBootnodes {
-		node, err := discover.ParseNode(url)
+		node, err := enode.ParseV4(url)
 		if err != nil {
 		if err != nil {
 			log.Error("Bootstrap URL invalid", "enode", url, "err", err)
 			log.Error("Bootstrap URL invalid", "enode", url, "err", err)
 		}
 		}

+ 7 - 6
cmd/swarm/run_test.go

@@ -234,6 +234,7 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
 	// start the node
 	// start the node
 	node.Cmd = runSwarm(t,
 	node.Cmd = runSwarm(t,
 		"--port", p2pPort,
 		"--port", p2pPort,
+		"--nat", "extip:127.0.0.1",
 		"--nodiscover",
 		"--nodiscover",
 		"--datadir", dir,
 		"--datadir", dir,
 		"--ipcpath", conf.IPCPath,
 		"--ipcpath", conf.IPCPath,
@@ -241,7 +242,7 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
 		"--bzzaccount", bzzaccount,
 		"--bzzaccount", bzzaccount,
 		"--bzznetworkid", "321",
 		"--bzznetworkid", "321",
 		"--bzzport", httpPort,
 		"--bzzport", httpPort,
-		"--verbosity", "6",
+		"--verbosity", "3",
 	)
 	)
 	node.Cmd.InputLine(testPassphrase)
 	node.Cmd.InputLine(testPassphrase)
 	defer func() {
 	defer func() {
@@ -284,8 +285,8 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
 	if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
 	if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s", nodeInfo.ID, p2pPort)
-
+	node.Enode = nodeInfo.Enode
+	node.IpcPath = conf.IPCPath
 	return node
 	return node
 }
 }
 
 
@@ -309,6 +310,7 @@ func newTestNode(t *testing.T, dir string) *testNode {
 	// start the node
 	// start the node
 	node.Cmd = runSwarm(t,
 	node.Cmd = runSwarm(t,
 		"--port", p2pPort,
 		"--port", p2pPort,
+		"--nat", "extip:127.0.0.1",
 		"--nodiscover",
 		"--nodiscover",
 		"--datadir", dir,
 		"--datadir", dir,
 		"--ipcpath", conf.IPCPath,
 		"--ipcpath", conf.IPCPath,
@@ -316,7 +318,7 @@ func newTestNode(t *testing.T, dir string) *testNode {
 		"--bzzaccount", account.Address.String(),
 		"--bzzaccount", account.Address.String(),
 		"--bzznetworkid", "321",
 		"--bzznetworkid", "321",
 		"--bzzport", httpPort,
 		"--bzzport", httpPort,
-		"--verbosity", "6",
+		"--verbosity", "3",
 	)
 	)
 	node.Cmd.InputLine(testPassphrase)
 	node.Cmd.InputLine(testPassphrase)
 	defer func() {
 	defer func() {
@@ -359,9 +361,8 @@ func newTestNode(t *testing.T, dir string) *testNode {
 	if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
 	if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s", nodeInfo.ID, p2pPort)
+	node.Enode = nodeInfo.Enode
 	node.IpcPath = conf.IPCPath
 	node.IpcPath = conf.IPCPath
-
 	return node
 	return node
 }
 }
 
 

+ 3 - 3
cmd/utils/flags.go

@@ -51,8 +51,8 @@ import (
 	"github.com/ethereum/go-ethereum/metrics/influxdb"
 	"github.com/ethereum/go-ethereum/metrics/influxdb"
 	"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/discover"
 	"github.com/ethereum/go-ethereum/p2p/discv5"
 	"github.com/ethereum/go-ethereum/p2p/discv5"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/nat"
 	"github.com/ethereum/go-ethereum/p2p/nat"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 	"github.com/ethereum/go-ethereum/params"
 	"github.com/ethereum/go-ethereum/params"
@@ -692,9 +692,9 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
 		return // already set, don't apply defaults.
 		return // already set, don't apply defaults.
 	}
 	}
 
 
-	cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls))
+	cfg.BootstrapNodes = make([]*enode.Node, 0, len(urls))
 	for _, url := range urls {
 	for _, url := range urls {
-		node, err := discover.ParseNode(url)
+		node, err := enode.ParseV4(url)
 		if err != nil {
 		if err != nil {
 			log.Crit("Bootstrap URL invalid", "enode", url, "err", err)
 			log.Crit("Bootstrap URL invalid", "enode", url, "err", err)
 		}
 		}

+ 5 - 5
cmd/wnode/main.go

@@ -41,7 +41,7 @@ import (
 	"github.com/ethereum/go-ethereum/crypto"
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/nat"
 	"github.com/ethereum/go-ethereum/p2p/nat"
 	"github.com/ethereum/go-ethereum/whisper/mailserver"
 	"github.com/ethereum/go-ethereum/whisper/mailserver"
 	whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
 	whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
@@ -175,7 +175,7 @@ func initialize() {
 	log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
 	log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
 
 
 	done = make(chan struct{})
 	done = make(chan struct{})
-	var peers []*discover.Node
+	var peers []*enode.Node
 	var err error
 	var err error
 
 
 	if *generateKey {
 	if *generateKey {
@@ -203,7 +203,7 @@ func initialize() {
 		if len(*argEnode) == 0 {
 		if len(*argEnode) == 0 {
 			argEnode = scanLineA("Please enter the peer's enode: ")
 			argEnode = scanLineA("Please enter the peer's enode: ")
 		}
 		}
-		peer := discover.MustParseNode(*argEnode)
+		peer := enode.MustParseV4(*argEnode)
 		peers = append(peers, peer)
 		peers = append(peers, peer)
 	}
 	}
 
 
@@ -747,11 +747,11 @@ func requestExpiredMessagesLoop() {
 }
 }
 
 
 func extractIDFromEnode(s string) []byte {
 func extractIDFromEnode(s string) []byte {
-	n, err := discover.ParseNode(s)
+	n, err := enode.ParseV4(s)
 	if err != nil {
 	if err != nil {
 		utils.Fatalf("Failed to parse enode: %s", err)
 		utils.Fatalf("Failed to parse enode: %s", err)
 	}
 	}
-	return n.ID[:]
+	return n.ID().Bytes()
 }
 }
 
 
 // obfuscateBloom adds 16 random bits to the bloom
 // obfuscateBloom adds 16 random bits to the bloom

+ 2 - 2
eth/handler.go

@@ -37,7 +37,7 @@ import (
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/event"
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/params"
 	"github.com/ethereum/go-ethereum/params"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
 )
 )
@@ -147,7 +147,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
 			NodeInfo: func() interface{} {
 			NodeInfo: func() interface{} {
 				return manager.NodeInfo()
 				return manager.NodeInfo()
 			},
 			},
-			PeerInfo: func(id discover.NodeID) interface{} {
+			PeerInfo: func(id enode.ID) interface{} {
 				if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {
 				if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {
 					return p.Info()
 					return p.Info()
 				}
 				}

+ 2 - 2
eth/helper_test.go

@@ -37,7 +37,7 @@ import (
 	"github.com/ethereum/go-ethereum/ethdb"
 	"github.com/ethereum/go-ethereum/ethdb"
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/params"
 	"github.com/ethereum/go-ethereum/params"
 )
 )
 
 
@@ -148,7 +148,7 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
 	app, net := p2p.MsgPipe()
 	app, net := p2p.MsgPipe()
 
 
 	// Generate a random id and create the peer
 	// Generate a random id and create the peer
-	var id discover.NodeID
+	var id enode.ID
 	rand.Read(id[:])
 	rand.Read(id[:])
 
 
 	peer := pm.newPeer(version, p2p.NewPeer(id, name, nil), net)
 	peer := pm.newPeer(version, p2p.NewPeer(id, name, nil), net)

+ 2 - 2
eth/sync.go

@@ -25,7 +25,7 @@ import (
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/eth/downloader"
 	"github.com/ethereum/go-ethereum/eth/downloader"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 )
 
 
 const (
 const (
@@ -64,7 +64,7 @@ func (pm *ProtocolManager) syncTransactions(p *peer) {
 // the transactions in small packs to one peer at a time.
 // the transactions in small packs to one peer at a time.
 func (pm *ProtocolManager) txsyncLoop() {
 func (pm *ProtocolManager) txsyncLoop() {
 	var (
 	var (
-		pending = make(map[discover.NodeID]*txsync)
+		pending = make(map[enode.ID]*txsync)
 		sending = false               // whether a send is active
 		sending = false               // whether a send is active
 		pack    = new(txsync)         // the pack that is being sent
 		pack    = new(txsync)         // the pack that is being sent
 		done    = make(chan error, 1) // result of the send
 		done    = make(chan error, 1) // result of the send

+ 3 - 3
eth/sync_test.go

@@ -23,7 +23,7 @@ import (
 
 
 	"github.com/ethereum/go-ethereum/eth/downloader"
 	"github.com/ethereum/go-ethereum/eth/downloader"
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 )
 
 
 // Tests that fast sync gets disabled as soon as a real block is successfully
 // Tests that fast sync gets disabled as soon as a real block is successfully
@@ -42,8 +42,8 @@ func TestFastSyncDisabling(t *testing.T) {
 	// Sync up the two peers
 	// Sync up the two peers
 	io1, io2 := p2p.MsgPipe()
 	io1, io2 := p2p.MsgPipe()
 
 
-	go pmFull.handle(pmFull.newPeer(63, p2p.NewPeer(discover.NodeID{}, "empty", nil), io2))
-	go pmEmpty.handle(pmEmpty.newPeer(63, p2p.NewPeer(discover.NodeID{}, "full", nil), io1))
+	go pmFull.handle(pmFull.newPeer(63, p2p.NewPeer(enode.ID{}, "empty", nil), io2))
+	go pmEmpty.handle(pmEmpty.newPeer(63, p2p.NewPeer(enode.ID{}, "full", nil), io1))
 
 
 	time.Sleep(250 * time.Millisecond)
 	time.Sleep(250 * time.Millisecond)
 	pmEmpty.synchronise(pmEmpty.peers.BestPeer())
 	pmEmpty.synchronise(pmEmpty.peers.BestPeer())

+ 2 - 2
les/commons.go

@@ -26,7 +26,7 @@ import (
 	"github.com/ethereum/go-ethereum/ethdb"
 	"github.com/ethereum/go-ethereum/ethdb"
 	"github.com/ethereum/go-ethereum/light"
 	"github.com/ethereum/go-ethereum/light"
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/params"
 	"github.com/ethereum/go-ethereum/params"
 )
 )
 
 
@@ -63,7 +63,7 @@ func (c *lesCommons) makeProtocols(versions []uint) []p2p.Protocol {
 			Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
 			Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
 				return c.protocolManager.runPeer(version, p, rw)
 				return c.protocolManager.runPeer(version, p, rw)
 			},
 			},
-			PeerInfo: func(id discover.NodeID) interface{} {
+			PeerInfo: func(id enode.ID) interface{} {
 				if p := c.protocolManager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {
 				if p := c.protocolManager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {
 					return p.Info()
 					return p.Info()
 				}
 				}

+ 2 - 3
les/handler.go

@@ -213,8 +213,7 @@ func (pm *ProtocolManager) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWrit
 	var entry *poolEntry
 	var entry *poolEntry
 	peer := pm.newPeer(int(version), pm.networkId, p, rw)
 	peer := pm.newPeer(int(version), pm.networkId, p, rw)
 	if pm.serverPool != nil {
 	if pm.serverPool != nil {
-		addr := p.RemoteAddr().(*net.TCPAddr)
-		entry = pm.serverPool.connect(peer, addr.IP, uint16(addr.Port))
+		entry = pm.serverPool.connect(peer, peer.Node())
 	}
 	}
 	peer.poolEntry = entry
 	peer.poolEntry = entry
 	select {
 	select {
@@ -382,7 +381,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
 		}
 		}
 
 
 		if p.requestAnnounceType == announceTypeSigned {
 		if p.requestAnnounceType == announceTypeSigned {
-			if err := req.checkSignature(p.pubKey); err != nil {
+			if err := req.checkSignature(p.ID()); err != nil {
 				p.Log().Trace("Invalid announcement signature", "err", err)
 				p.Log().Trace("Invalid announcement signature", "err", err)
 				return err
 				return err
 			}
 			}

+ 3 - 3
les/helper_test.go

@@ -38,7 +38,7 @@ import (
 	"github.com/ethereum/go-ethereum/les/flowcontrol"
 	"github.com/ethereum/go-ethereum/les/flowcontrol"
 	"github.com/ethereum/go-ethereum/light"
 	"github.com/ethereum/go-ethereum/light"
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/params"
 	"github.com/ethereum/go-ethereum/params"
 )
 )
 
 
@@ -221,7 +221,7 @@ func newTestPeer(t *testing.T, name string, version int, pm *ProtocolManager, sh
 	app, net := p2p.MsgPipe()
 	app, net := p2p.MsgPipe()
 
 
 	// Generate a random id and create the peer
 	// Generate a random id and create the peer
-	var id discover.NodeID
+	var id enode.ID
 	rand.Read(id[:])
 	rand.Read(id[:])
 
 
 	peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
 	peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
@@ -258,7 +258,7 @@ func newTestPeerPair(name string, version int, pm, pm2 *ProtocolManager) (*peer,
 	app, net := p2p.MsgPipe()
 	app, net := p2p.MsgPipe()
 
 
 	// Generate a random id and create the peer
 	// Generate a random id and create the peer
-	var id discover.NodeID
+	var id enode.ID
 	rand.Read(id[:])
 	rand.Read(id[:])
 
 
 	peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
 	peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)

+ 0 - 4
les/peer.go

@@ -18,7 +18,6 @@
 package les
 package les
 
 
 import (
 import (
-	"crypto/ecdsa"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"math/big"
 	"math/big"
@@ -51,7 +50,6 @@ const (
 
 
 type peer struct {
 type peer struct {
 	*p2p.Peer
 	*p2p.Peer
-	pubKey *ecdsa.PublicKey
 
 
 	rw p2p.MsgReadWriter
 	rw p2p.MsgReadWriter
 
 
@@ -80,11 +78,9 @@ type peer struct {
 
 
 func newPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
 func newPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
 	id := p.ID()
 	id := p.ID()
-	pubKey, _ := id.Pubkey()
 
 
 	return &peer{
 	return &peer{
 		Peer:        p,
 		Peer:        p,
-		pubKey:      pubKey,
 		rw:          rw,
 		rw:          rw,
 		version:     version,
 		version:     version,
 		network:     network,
 		network:     network,

+ 5 - 8
les/protocol.go

@@ -18,9 +18,7 @@
 package les
 package les
 
 
 import (
 import (
-	"bytes"
 	"crypto/ecdsa"
 	"crypto/ecdsa"
-	"crypto/elliptic"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
@@ -30,7 +28,7 @@ import (
 	"github.com/ethereum/go-ethereum/core"
 	"github.com/ethereum/go-ethereum/core"
 	"github.com/ethereum/go-ethereum/core/rawdb"
 	"github.com/ethereum/go-ethereum/core/rawdb"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto"
-	"github.com/ethereum/go-ethereum/crypto/secp256k1"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
 )
 )
 
 
@@ -148,21 +146,20 @@ func (a *announceData) sign(privKey *ecdsa.PrivateKey) {
 }
 }
 
 
 // checkSignature verifies if the block announcement has a valid signature by the given pubKey
 // checkSignature verifies if the block announcement has a valid signature by the given pubKey
-func (a *announceData) checkSignature(pubKey *ecdsa.PublicKey) error {
+func (a *announceData) checkSignature(id enode.ID) error {
 	var sig []byte
 	var sig []byte
 	if err := a.Update.decode().get("sign", &sig); err != nil {
 	if err := a.Update.decode().get("sign", &sig); err != nil {
 		return err
 		return err
 	}
 	}
 	rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td})
 	rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td})
-	recPubkey, err := secp256k1.RecoverPubkey(crypto.Keccak256(rlp), sig)
+	recPubkey, err := crypto.SigToPub(crypto.Keccak256(rlp), sig)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	pbytes := elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y)
-	if bytes.Equal(pbytes, recPubkey) {
+	if id == enode.PubkeyToIDV4(recPubkey) {
 		return nil
 		return nil
 	}
 	}
-	return errors.New("Wrong signature")
+	return errors.New("wrong signature")
 }
 }
 
 
 type blockInfo struct {
 type blockInfo struct {

+ 80 - 41
les/serverpool.go

@@ -18,6 +18,7 @@
 package les
 package les
 
 
 import (
 import (
+	"crypto/ecdsa"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"math"
 	"math"
@@ -28,11 +29,12 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/ethereum/go-ethereum/common/mclock"
 	"github.com/ethereum/go-ethereum/common/mclock"
+	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/ethdb"
 	"github.com/ethereum/go-ethereum/ethdb"
 	"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/discover"
 	"github.com/ethereum/go-ethereum/p2p/discv5"
 	"github.com/ethereum/go-ethereum/p2p/discv5"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
 )
 )
 
 
@@ -90,8 +92,7 @@ const (
 // connReq represents a request for peer connection.
 // connReq represents a request for peer connection.
 type connReq struct {
 type connReq struct {
 	p      *peer
 	p      *peer
-	ip     net.IP
-	port   uint16
+	node   *enode.Node
 	result chan *poolEntry
 	result chan *poolEntry
 }
 }
 
 
@@ -122,10 +123,10 @@ type serverPool struct {
 	topic discv5.Topic
 	topic discv5.Topic
 
 
 	discSetPeriod chan time.Duration
 	discSetPeriod chan time.Duration
-	discNodes     chan *discv5.Node
+	discNodes     chan *enode.Node
 	discLookups   chan bool
 	discLookups   chan bool
 
 
-	entries              map[discover.NodeID]*poolEntry
+	entries              map[enode.ID]*poolEntry
 	timeout, enableRetry chan *poolEntry
 	timeout, enableRetry chan *poolEntry
 	adjustStats          chan poolStatAdjust
 	adjustStats          chan poolStatAdjust
 
 
@@ -145,7 +146,7 @@ func newServerPool(db ethdb.Database, quit chan struct{}, wg *sync.WaitGroup) *s
 		db:           db,
 		db:           db,
 		quit:         quit,
 		quit:         quit,
 		wg:           wg,
 		wg:           wg,
-		entries:      make(map[discover.NodeID]*poolEntry),
+		entries:      make(map[enode.ID]*poolEntry),
 		timeout:      make(chan *poolEntry, 1),
 		timeout:      make(chan *poolEntry, 1),
 		adjustStats:  make(chan poolStatAdjust, 100),
 		adjustStats:  make(chan poolStatAdjust, 100),
 		enableRetry:  make(chan *poolEntry, 1),
 		enableRetry:  make(chan *poolEntry, 1),
@@ -170,22 +171,38 @@ func (pool *serverPool) start(server *p2p.Server, topic discv5.Topic) {
 
 
 	if pool.server.DiscV5 != nil {
 	if pool.server.DiscV5 != nil {
 		pool.discSetPeriod = make(chan time.Duration, 1)
 		pool.discSetPeriod = make(chan time.Duration, 1)
-		pool.discNodes = make(chan *discv5.Node, 100)
+		pool.discNodes = make(chan *enode.Node, 100)
 		pool.discLookups = make(chan bool, 100)
 		pool.discLookups = make(chan bool, 100)
-		go pool.server.DiscV5.SearchTopic(pool.topic, pool.discSetPeriod, pool.discNodes, pool.discLookups)
+		go pool.discoverNodes()
 	}
 	}
 	pool.checkDial()
 	pool.checkDial()
 	go pool.eventLoop()
 	go pool.eventLoop()
 }
 }
 
 
+// discoverNodes wraps SearchTopic, converting result nodes to enode.Node.
+func (pool *serverPool) discoverNodes() {
+	ch := make(chan *discv5.Node)
+	go func() {
+		pool.server.DiscV5.SearchTopic(pool.topic, pool.discSetPeriod, ch, pool.discLookups)
+		close(ch)
+	}()
+	for n := range ch {
+		pubkey, err := decodePubkey64(n.ID[:])
+		if err != nil {
+			continue
+		}
+		pool.discNodes <- enode.NewV4(pubkey, n.IP, int(n.TCP), int(n.UDP))
+	}
+}
+
 // connect should be called upon any incoming connection. If the connection has been
 // connect should be called upon any incoming connection. If the connection has been
 // dialed by the server pool recently, the appropriate pool entry is returned.
 // dialed by the server pool recently, the appropriate pool entry is returned.
 // Otherwise, the connection should be rejected.
 // Otherwise, the connection should be rejected.
 // Note that whenever a connection has been accepted and a pool entry has been returned,
 // Note that whenever a connection has been accepted and a pool entry has been returned,
 // disconnect should also always be called.
 // disconnect should also always be called.
-func (pool *serverPool) connect(p *peer, ip net.IP, port uint16) *poolEntry {
+func (pool *serverPool) connect(p *peer, node *enode.Node) *poolEntry {
 	log.Debug("Connect new entry", "enode", p.id)
 	log.Debug("Connect new entry", "enode", p.id)
-	req := &connReq{p: p, ip: ip, port: port, result: make(chan *poolEntry, 1)}
+	req := &connReq{p: p, node: node, result: make(chan *poolEntry, 1)}
 	select {
 	select {
 	case pool.connCh <- req:
 	case pool.connCh <- req:
 	case <-pool.quit:
 	case <-pool.quit:
@@ -196,7 +213,7 @@ func (pool *serverPool) connect(p *peer, ip net.IP, port uint16) *poolEntry {
 
 
 // registered should be called after a successful handshake
 // registered should be called after a successful handshake
 func (pool *serverPool) registered(entry *poolEntry) {
 func (pool *serverPool) registered(entry *poolEntry) {
-	log.Debug("Registered new entry", "enode", entry.id)
+	log.Debug("Registered new entry", "enode", entry.node.ID())
 	req := &registerReq{entry: entry, done: make(chan struct{})}
 	req := &registerReq{entry: entry, done: make(chan struct{})}
 	select {
 	select {
 	case pool.registerCh <- req:
 	case pool.registerCh <- req:
@@ -216,7 +233,7 @@ func (pool *serverPool) disconnect(entry *poolEntry) {
 		stopped = true
 		stopped = true
 	default:
 	default:
 	}
 	}
-	log.Debug("Disconnected old entry", "enode", entry.id)
+	log.Debug("Disconnected old entry", "enode", entry.node.ID())
 	req := &disconnReq{entry: entry, stopped: stopped, done: make(chan struct{})}
 	req := &disconnReq{entry: entry, stopped: stopped, done: make(chan struct{})}
 
 
 	// Block until disconnection request is served.
 	// Block until disconnection request is served.
@@ -320,7 +337,7 @@ func (pool *serverPool) eventLoop() {
 			}
 			}
 
 
 		case node := <-pool.discNodes:
 		case node := <-pool.discNodes:
-			entry := pool.findOrNewNode(discover.NodeID(node.ID), node.IP, node.TCP)
+			entry := pool.findOrNewNode(node)
 			pool.updateCheckDial(entry)
 			pool.updateCheckDial(entry)
 
 
 		case conv := <-pool.discLookups:
 		case conv := <-pool.discLookups:
@@ -341,7 +358,7 @@ func (pool *serverPool) eventLoop() {
 			// Handle peer connection requests.
 			// Handle peer connection requests.
 			entry := pool.entries[req.p.ID()]
 			entry := pool.entries[req.p.ID()]
 			if entry == nil {
 			if entry == nil {
-				entry = pool.findOrNewNode(req.p.ID(), req.ip, req.port)
+				entry = pool.findOrNewNode(req.node)
 			}
 			}
 			if entry.state == psConnected || entry.state == psRegistered {
 			if entry.state == psConnected || entry.state == psRegistered {
 				req.result <- nil
 				req.result <- nil
@@ -351,8 +368,8 @@ func (pool *serverPool) eventLoop() {
 			entry.peer = req.p
 			entry.peer = req.p
 			entry.state = psConnected
 			entry.state = psConnected
 			addr := &poolEntryAddress{
 			addr := &poolEntryAddress{
-				ip:       req.ip,
-				port:     req.port,
+				ip:       req.node.IP(),
+				port:     uint16(req.node.TCP()),
 				lastSeen: mclock.Now(),
 				lastSeen: mclock.Now(),
 			}
 			}
 			entry.lastConnected = addr
 			entry.lastConnected = addr
@@ -401,18 +418,18 @@ func (pool *serverPool) eventLoop() {
 	}
 	}
 }
 }
 
 
-func (pool *serverPool) findOrNewNode(id discover.NodeID, ip net.IP, port uint16) *poolEntry {
+func (pool *serverPool) findOrNewNode(node *enode.Node) *poolEntry {
 	now := mclock.Now()
 	now := mclock.Now()
-	entry := pool.entries[id]
+	entry := pool.entries[node.ID()]
 	if entry == nil {
 	if entry == nil {
-		log.Debug("Discovered new entry", "id", id)
+		log.Debug("Discovered new entry", "id", node.ID())
 		entry = &poolEntry{
 		entry = &poolEntry{
-			id:         id,
+			node:       node,
 			addr:       make(map[string]*poolEntryAddress),
 			addr:       make(map[string]*poolEntryAddress),
 			addrSelect: *newWeightedRandomSelect(),
 			addrSelect: *newWeightedRandomSelect(),
 			shortRetry: shortRetryCnt,
 			shortRetry: shortRetryCnt,
 		}
 		}
-		pool.entries[id] = entry
+		pool.entries[node.ID()] = entry
 		// initialize previously unknown peers with good statistics to give a chance to prove themselves
 		// initialize previously unknown peers with good statistics to give a chance to prove themselves
 		entry.connectStats.add(1, initStatsWeight)
 		entry.connectStats.add(1, initStatsWeight)
 		entry.delayStats.add(0, initStatsWeight)
 		entry.delayStats.add(0, initStatsWeight)
@@ -420,10 +437,7 @@ func (pool *serverPool) findOrNewNode(id discover.NodeID, ip net.IP, port uint16
 		entry.timeoutStats.add(0, initStatsWeight)
 		entry.timeoutStats.add(0, initStatsWeight)
 	}
 	}
 	entry.lastDiscovered = now
 	entry.lastDiscovered = now
-	addr := &poolEntryAddress{
-		ip:   ip,
-		port: port,
-	}
+	addr := &poolEntryAddress{ip: node.IP(), port: uint16(node.TCP())}
 	if a, ok := entry.addr[addr.strKey()]; ok {
 	if a, ok := entry.addr[addr.strKey()]; ok {
 		addr = a
 		addr = a
 	} else {
 	} else {
@@ -450,12 +464,12 @@ func (pool *serverPool) loadNodes() {
 		return
 		return
 	}
 	}
 	for _, e := range list {
 	for _, e := range list {
-		log.Debug("Loaded server stats", "id", e.id, "fails", e.lastConnected.fails,
+		log.Debug("Loaded server stats", "id", e.node.ID(), "fails", e.lastConnected.fails,
 			"conn", fmt.Sprintf("%v/%v", e.connectStats.avg, e.connectStats.weight),
 			"conn", fmt.Sprintf("%v/%v", e.connectStats.avg, e.connectStats.weight),
 			"delay", fmt.Sprintf("%v/%v", time.Duration(e.delayStats.avg), e.delayStats.weight),
 			"delay", fmt.Sprintf("%v/%v", time.Duration(e.delayStats.avg), e.delayStats.weight),
 			"response", fmt.Sprintf("%v/%v", time.Duration(e.responseStats.avg), e.responseStats.weight),
 			"response", fmt.Sprintf("%v/%v", time.Duration(e.responseStats.avg), e.responseStats.weight),
 			"timeout", fmt.Sprintf("%v/%v", e.timeoutStats.avg, e.timeoutStats.weight))
 			"timeout", fmt.Sprintf("%v/%v", e.timeoutStats.avg, e.timeoutStats.weight))
-		pool.entries[e.id] = e
+		pool.entries[e.node.ID()] = e
 		pool.knownQueue.setLatest(e)
 		pool.knownQueue.setLatest(e)
 		pool.knownSelect.update((*knownEntry)(e))
 		pool.knownSelect.update((*knownEntry)(e))
 	}
 	}
@@ -481,7 +495,7 @@ func (pool *serverPool) removeEntry(entry *poolEntry) {
 	pool.newSelect.remove((*discoveredEntry)(entry))
 	pool.newSelect.remove((*discoveredEntry)(entry))
 	pool.knownSelect.remove((*knownEntry)(entry))
 	pool.knownSelect.remove((*knownEntry)(entry))
 	entry.removed = true
 	entry.removed = true
-	delete(pool.entries, entry.id)
+	delete(pool.entries, entry.node.ID())
 }
 }
 
 
 // setRetryDial starts the timer which will enable dialing a certain node again
 // setRetryDial starts the timer which will enable dialing a certain node again
@@ -559,10 +573,10 @@ func (pool *serverPool) dial(entry *poolEntry, knownSelected bool) {
 		pool.newSelected++
 		pool.newSelected++
 	}
 	}
 	addr := entry.addrSelect.choose().(*poolEntryAddress)
 	addr := entry.addrSelect.choose().(*poolEntryAddress)
-	log.Debug("Dialing new peer", "lesaddr", entry.id.String()+"@"+addr.strKey(), "set", len(entry.addr), "known", knownSelected)
+	log.Debug("Dialing new peer", "lesaddr", entry.node.ID().String()+"@"+addr.strKey(), "set", len(entry.addr), "known", knownSelected)
 	entry.dialed = addr
 	entry.dialed = addr
 	go func() {
 	go func() {
-		pool.server.AddPeer(discover.NewNode(entry.id, addr.ip, addr.port, addr.port))
+		pool.server.AddPeer(entry.node)
 		select {
 		select {
 		case <-pool.quit:
 		case <-pool.quit:
 		case <-time.After(dialTimeout):
 		case <-time.After(dialTimeout):
@@ -580,7 +594,7 @@ func (pool *serverPool) checkDialTimeout(entry *poolEntry) {
 	if entry.state != psDialed {
 	if entry.state != psDialed {
 		return
 		return
 	}
 	}
-	log.Debug("Dial timeout", "lesaddr", entry.id.String()+"@"+entry.dialed.strKey())
+	log.Debug("Dial timeout", "lesaddr", entry.node.ID().String()+"@"+entry.dialed.strKey())
 	entry.state = psNotConnected
 	entry.state = psNotConnected
 	if entry.knownSelected {
 	if entry.knownSelected {
 		pool.knownSelected--
 		pool.knownSelected--
@@ -602,8 +616,9 @@ const (
 // poolEntry represents a server node and stores its current state and statistics.
 // poolEntry represents a server node and stores its current state and statistics.
 type poolEntry struct {
 type poolEntry struct {
 	peer                  *peer
 	peer                  *peer
-	id                    discover.NodeID
+	pubkey                [64]byte // secp256k1 key of the node
 	addr                  map[string]*poolEntryAddress
 	addr                  map[string]*poolEntryAddress
+	node                  *enode.Node
 	lastConnected, dialed *poolEntryAddress
 	lastConnected, dialed *poolEntryAddress
 	addrSelect            weightedRandomSelect
 	addrSelect            weightedRandomSelect
 
 
@@ -620,23 +635,39 @@ type poolEntry struct {
 	shortRetry   int
 	shortRetry   int
 }
 }
 
 
+// poolEntryEnc is the RLP encoding of poolEntry.
+type poolEntryEnc struct {
+	Pubkey                     []byte
+	IP                         net.IP
+	Port                       uint16
+	Fails                      uint
+	CStat, DStat, RStat, TStat poolStats
+}
+
 func (e *poolEntry) EncodeRLP(w io.Writer) error {
 func (e *poolEntry) EncodeRLP(w io.Writer) error {
-	return rlp.Encode(w, []interface{}{e.id, e.lastConnected.ip, e.lastConnected.port, e.lastConnected.fails, &e.connectStats, &e.delayStats, &e.responseStats, &e.timeoutStats})
+	return rlp.Encode(w, &poolEntryEnc{
+		Pubkey: encodePubkey64(e.node.Pubkey()),
+		IP:     e.lastConnected.ip,
+		Port:   e.lastConnected.port,
+		Fails:  e.lastConnected.fails,
+		CStat:  e.connectStats,
+		DStat:  e.delayStats,
+		RStat:  e.responseStats,
+		TStat:  e.timeoutStats,
+	})
 }
 }
 
 
 func (e *poolEntry) DecodeRLP(s *rlp.Stream) error {
 func (e *poolEntry) DecodeRLP(s *rlp.Stream) error {
-	var entry struct {
-		ID                         discover.NodeID
-		IP                         net.IP
-		Port                       uint16
-		Fails                      uint
-		CStat, DStat, RStat, TStat poolStats
-	}
+	var entry poolEntryEnc
 	if err := s.Decode(&entry); err != nil {
 	if err := s.Decode(&entry); err != nil {
 		return err
 		return err
 	}
 	}
+	pubkey, err := decodePubkey64(entry.Pubkey)
+	if err != nil {
+		return err
+	}
 	addr := &poolEntryAddress{ip: entry.IP, port: entry.Port, fails: entry.Fails, lastSeen: mclock.Now()}
 	addr := &poolEntryAddress{ip: entry.IP, port: entry.Port, fails: entry.Fails, lastSeen: mclock.Now()}
-	e.id = entry.ID
+	e.node = enode.NewV4(pubkey, entry.IP, int(entry.Port), int(entry.Port))
 	e.addr = make(map[string]*poolEntryAddress)
 	e.addr = make(map[string]*poolEntryAddress)
 	e.addr[addr.strKey()] = addr
 	e.addr[addr.strKey()] = addr
 	e.addrSelect = *newWeightedRandomSelect()
 	e.addrSelect = *newWeightedRandomSelect()
@@ -651,6 +682,14 @@ func (e *poolEntry) DecodeRLP(s *rlp.Stream) error {
 	return nil
 	return nil
 }
 }
 
 
+func encodePubkey64(pub *ecdsa.PublicKey) []byte {
+	return crypto.FromECDSAPub(pub)[:1]
+}
+
+func decodePubkey64(b []byte) (*ecdsa.PublicKey, error) {
+	return crypto.UnmarshalPubkey(append([]byte{0x04}, b...))
+}
+
 // discoveredEntry implements wrsItem
 // discoveredEntry implements wrsItem
 type discoveredEntry poolEntry
 type discoveredEntry poolEntry
 
 

+ 5 - 5
node/api.go

@@ -26,7 +26,7 @@ import (
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/metrics"
 	"github.com/ethereum/go-ethereum/metrics"
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/rpc"
 	"github.com/ethereum/go-ethereum/rpc"
 )
 )
 
 
@@ -51,7 +51,7 @@ func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) {
 		return false, ErrNodeStopped
 		return false, ErrNodeStopped
 	}
 	}
 	// Try to add the url as a static peer and return
 	// Try to add the url as a static peer and return
-	node, err := discover.ParseNode(url)
+	node, err := enode.ParseV4(url)
 	if err != nil {
 	if err != nil {
 		return false, fmt.Errorf("invalid enode: %v", err)
 		return false, fmt.Errorf("invalid enode: %v", err)
 	}
 	}
@@ -67,7 +67,7 @@ func (api *PrivateAdminAPI) RemovePeer(url string) (bool, error) {
 		return false, ErrNodeStopped
 		return false, ErrNodeStopped
 	}
 	}
 	// Try to remove the url as a static peer and return
 	// Try to remove the url as a static peer and return
-	node, err := discover.ParseNode(url)
+	node, err := enode.ParseV4(url)
 	if err != nil {
 	if err != nil {
 		return false, fmt.Errorf("invalid enode: %v", err)
 		return false, fmt.Errorf("invalid enode: %v", err)
 	}
 	}
@@ -82,7 +82,7 @@ func (api *PrivateAdminAPI) AddTrustedPeer(url string) (bool, error) {
 	if server == nil {
 	if server == nil {
 		return false, ErrNodeStopped
 		return false, ErrNodeStopped
 	}
 	}
-	node, err := discover.ParseNode(url)
+	node, err := enode.ParseV4(url)
 	if err != nil {
 	if err != nil {
 		return false, fmt.Errorf("invalid enode: %v", err)
 		return false, fmt.Errorf("invalid enode: %v", err)
 	}
 	}
@@ -98,7 +98,7 @@ func (api *PrivateAdminAPI) RemoveTrustedPeer(url string) (bool, error) {
 	if server == nil {
 	if server == nil {
 		return false, ErrNodeStopped
 		return false, ErrNodeStopped
 	}
 	}
-	node, err := discover.ParseNode(url)
+	node, err := enode.ParseV4(url)
 	if err != nil {
 	if err != nil {
 		return false, fmt.Errorf("invalid enode: %v", err)
 		return false, fmt.Errorf("invalid enode: %v", err)
 	}
 	}

+ 6 - 6
node/config.go

@@ -32,7 +32,7 @@ import (
 	"github.com/ethereum/go-ethereum/crypto"
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/rpc"
 	"github.com/ethereum/go-ethereum/rpc"
 )
 )
 
 
@@ -336,18 +336,18 @@ func (c *Config) NodeKey() *ecdsa.PrivateKey {
 }
 }
 
 
 // StaticNodes returns a list of node enode URLs configured as static nodes.
 // StaticNodes returns a list of node enode URLs configured as static nodes.
-func (c *Config) StaticNodes() []*discover.Node {
+func (c *Config) StaticNodes() []*enode.Node {
 	return c.parsePersistentNodes(c.ResolvePath(datadirStaticNodes))
 	return c.parsePersistentNodes(c.ResolvePath(datadirStaticNodes))
 }
 }
 
 
 // TrustedNodes returns a list of node enode URLs configured as trusted nodes.
 // TrustedNodes returns a list of node enode URLs configured as trusted nodes.
-func (c *Config) TrustedNodes() []*discover.Node {
+func (c *Config) TrustedNodes() []*enode.Node {
 	return c.parsePersistentNodes(c.ResolvePath(datadirTrustedNodes))
 	return c.parsePersistentNodes(c.ResolvePath(datadirTrustedNodes))
 }
 }
 
 
 // parsePersistentNodes parses a list of discovery node URLs loaded from a .json
 // parsePersistentNodes parses a list of discovery node URLs loaded from a .json
 // file from within the data directory.
 // file from within the data directory.
-func (c *Config) parsePersistentNodes(path string) []*discover.Node {
+func (c *Config) parsePersistentNodes(path string) []*enode.Node {
 	// Short circuit if no node config is present
 	// Short circuit if no node config is present
 	if c.DataDir == "" {
 	if c.DataDir == "" {
 		return nil
 		return nil
@@ -362,12 +362,12 @@ func (c *Config) parsePersistentNodes(path string) []*discover.Node {
 		return nil
 		return nil
 	}
 	}
 	// Interpret the list as a discovery node array
 	// Interpret the list as a discovery node array
-	var nodes []*discover.Node
+	var nodes []*enode.Node
 	for _, url := range nodelist {
 	for _, url := range nodelist {
 		if url == "" {
 		if url == "" {
 			continue
 			continue
 		}
 		}
-		node, err := discover.ParseNode(url)
+		node, err := enode.ParseV4(url)
 		if err != nil {
 		if err != nil {
 			log.Error(fmt.Sprintf("Node URL %s: %v\n", url, err))
 			log.Error(fmt.Sprintf("Node URL %s: %v\n", url, err))
 			continue
 			continue

+ 50 - 52
p2p/dial.go

@@ -18,14 +18,13 @@ package p2p
 
 
 import (
 import (
 	"container/heap"
 	"container/heap"
-	"crypto/rand"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"net"
 	"net"
 	"time"
 	"time"
 
 
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 )
 )
 
 
@@ -50,7 +49,7 @@ const (
 // NodeDialer is used to connect to nodes in the network, typically by using
 // NodeDialer is used to connect to nodes in the network, typically by using
 // an underlying net.Dialer but also using net.Pipe in tests
 // an underlying net.Dialer but also using net.Pipe in tests
 type NodeDialer interface {
 type NodeDialer interface {
-	Dial(*discover.Node) (net.Conn, error)
+	Dial(*enode.Node) (net.Conn, error)
 }
 }
 
 
 // TCPDialer implements the NodeDialer interface by using a net.Dialer to
 // TCPDialer implements the NodeDialer interface by using a net.Dialer to
@@ -60,8 +59,8 @@ type TCPDialer struct {
 }
 }
 
 
 // Dial creates a TCP connection to the node
 // Dial creates a TCP connection to the node
-func (t TCPDialer) Dial(dest *discover.Node) (net.Conn, error) {
-	addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)}
+func (t TCPDialer) Dial(dest *enode.Node) (net.Conn, error) {
+	addr := &net.TCPAddr{IP: dest.IP(), Port: dest.TCP()}
 	return t.Dialer.Dial("tcp", addr.String())
 	return t.Dialer.Dial("tcp", addr.String())
 }
 }
 
 
@@ -74,22 +73,22 @@ type dialstate struct {
 	netrestrict *netutil.Netlist
 	netrestrict *netutil.Netlist
 
 
 	lookupRunning bool
 	lookupRunning bool
-	dialing       map[discover.NodeID]connFlag
-	lookupBuf     []*discover.Node // current discovery lookup results
-	randomNodes   []*discover.Node // filled from Table
-	static        map[discover.NodeID]*dialTask
+	dialing       map[enode.ID]connFlag
+	lookupBuf     []*enode.Node // current discovery lookup results
+	randomNodes   []*enode.Node // filled from Table
+	static        map[enode.ID]*dialTask
 	hist          *dialHistory
 	hist          *dialHistory
 
 
-	start     time.Time        // time when the dialer was first used
-	bootnodes []*discover.Node // default dials when there are no peers
+	start     time.Time     // time when the dialer was first used
+	bootnodes []*enode.Node // default dials when there are no peers
 }
 }
 
 
 type discoverTable interface {
 type discoverTable interface {
-	Self() *discover.Node
+	Self() *enode.Node
 	Close()
 	Close()
-	Resolve(target discover.NodeID) *discover.Node
-	Lookup(target discover.NodeID) []*discover.Node
-	ReadRandomNodes([]*discover.Node) int
+	Resolve(*enode.Node) *enode.Node
+	LookupRandom() []*enode.Node
+	ReadRandomNodes([]*enode.Node) int
 }
 }
 
 
 // the dial history remembers recent dials.
 // the dial history remembers recent dials.
@@ -97,7 +96,7 @@ type dialHistory []pastDial
 
 
 // pastDial is an entry in the dial history.
 // pastDial is an entry in the dial history.
 type pastDial struct {
 type pastDial struct {
-	id  discover.NodeID
+	id  enode.ID
 	exp time.Time
 	exp time.Time
 }
 }
 
 
@@ -109,7 +108,7 @@ type task interface {
 // fields cannot be accessed while the task is running.
 // fields cannot be accessed while the task is running.
 type dialTask struct {
 type dialTask struct {
 	flags        connFlag
 	flags        connFlag
-	dest         *discover.Node
+	dest         *enode.Node
 	lastResolved time.Time
 	lastResolved time.Time
 	resolveDelay time.Duration
 	resolveDelay time.Duration
 }
 }
@@ -118,7 +117,7 @@ type dialTask struct {
 // Only one discoverTask is active at any time.
 // Only one discoverTask is active at any time.
 // discoverTask.Do performs a random lookup.
 // discoverTask.Do performs a random lookup.
 type discoverTask struct {
 type discoverTask struct {
-	results []*discover.Node
+	results []*enode.Node
 }
 }
 
 
 // A waitExpireTask is generated if there are no other tasks
 // A waitExpireTask is generated if there are no other tasks
@@ -127,15 +126,15 @@ type waitExpireTask struct {
 	time.Duration
 	time.Duration
 }
 }
 
 
-func newDialState(static []*discover.Node, bootnodes []*discover.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate {
+func newDialState(static []*enode.Node, bootnodes []*enode.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate {
 	s := &dialstate{
 	s := &dialstate{
 		maxDynDials: maxdyn,
 		maxDynDials: maxdyn,
 		ntab:        ntab,
 		ntab:        ntab,
 		netrestrict: netrestrict,
 		netrestrict: netrestrict,
-		static:      make(map[discover.NodeID]*dialTask),
-		dialing:     make(map[discover.NodeID]connFlag),
-		bootnodes:   make([]*discover.Node, len(bootnodes)),
-		randomNodes: make([]*discover.Node, maxdyn/2),
+		static:      make(map[enode.ID]*dialTask),
+		dialing:     make(map[enode.ID]connFlag),
+		bootnodes:   make([]*enode.Node, len(bootnodes)),
+		randomNodes: make([]*enode.Node, maxdyn/2),
 		hist:        new(dialHistory),
 		hist:        new(dialHistory),
 	}
 	}
 	copy(s.bootnodes, bootnodes)
 	copy(s.bootnodes, bootnodes)
@@ -145,32 +144,32 @@ func newDialState(static []*discover.Node, bootnodes []*discover.Node, ntab disc
 	return s
 	return s
 }
 }
 
 
-func (s *dialstate) addStatic(n *discover.Node) {
+func (s *dialstate) addStatic(n *enode.Node) {
 	// This overwrites the task instead of updating an existing
 	// This overwrites the task instead of updating an existing
 	// entry, giving users the opportunity to force a resolve operation.
 	// entry, giving users the opportunity to force a resolve operation.
-	s.static[n.ID] = &dialTask{flags: staticDialedConn, dest: n}
+	s.static[n.ID()] = &dialTask{flags: staticDialedConn, dest: n}
 }
 }
 
 
-func (s *dialstate) removeStatic(n *discover.Node) {
+func (s *dialstate) removeStatic(n *enode.Node) {
 	// This removes a task so future attempts to connect will not be made.
 	// This removes a task so future attempts to connect will not be made.
-	delete(s.static, n.ID)
+	delete(s.static, n.ID())
 	// This removes a previous dial timestamp so that application
 	// This removes a previous dial timestamp so that application
 	// can force a server to reconnect with chosen peer immediately.
 	// can force a server to reconnect with chosen peer immediately.
-	s.hist.remove(n.ID)
+	s.hist.remove(n.ID())
 }
 }
 
 
-func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task {
+func (s *dialstate) newTasks(nRunning int, peers map[enode.ID]*Peer, now time.Time) []task {
 	if s.start.IsZero() {
 	if s.start.IsZero() {
 		s.start = now
 		s.start = now
 	}
 	}
 
 
 	var newtasks []task
 	var newtasks []task
-	addDial := func(flag connFlag, n *discover.Node) bool {
+	addDial := func(flag connFlag, n *enode.Node) bool {
 		if err := s.checkDial(n, peers); err != nil {
 		if err := s.checkDial(n, peers); err != nil {
-			log.Trace("Skipping dial candidate", "id", n.ID, "addr", &net.TCPAddr{IP: n.IP, Port: int(n.TCP)}, "err", err)
+			log.Trace("Skipping dial candidate", "id", n.ID(), "addr", &net.TCPAddr{IP: n.IP(), Port: n.TCP()}, "err", err)
 			return false
 			return false
 		}
 		}
-		s.dialing[n.ID] = flag
+		s.dialing[n.ID()] = flag
 		newtasks = append(newtasks, &dialTask{flags: flag, dest: n})
 		newtasks = append(newtasks, &dialTask{flags: flag, dest: n})
 		return true
 		return true
 	}
 	}
@@ -196,8 +195,8 @@ func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now
 		err := s.checkDial(t.dest, peers)
 		err := s.checkDial(t.dest, peers)
 		switch err {
 		switch err {
 		case errNotWhitelisted, errSelf:
 		case errNotWhitelisted, errSelf:
-			log.Warn("Removing static dial candidate", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP, Port: int(t.dest.TCP)}, "err", err)
-			delete(s.static, t.dest.ID)
+			log.Warn("Removing static dial candidate", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP(), Port: t.dest.TCP()}, "err", err)
+			delete(s.static, t.dest.ID())
 		case nil:
 		case nil:
 			s.dialing[id] = t.flags
 			s.dialing[id] = t.flags
 			newtasks = append(newtasks, t)
 			newtasks = append(newtasks, t)
@@ -260,18 +259,18 @@ var (
 	errNotWhitelisted   = errors.New("not contained in netrestrict whitelist")
 	errNotWhitelisted   = errors.New("not contained in netrestrict whitelist")
 )
 )
 
 
-func (s *dialstate) checkDial(n *discover.Node, peers map[discover.NodeID]*Peer) error {
-	_, dialing := s.dialing[n.ID]
+func (s *dialstate) checkDial(n *enode.Node, peers map[enode.ID]*Peer) error {
+	_, dialing := s.dialing[n.ID()]
 	switch {
 	switch {
 	case dialing:
 	case dialing:
 		return errAlreadyDialing
 		return errAlreadyDialing
-	case peers[n.ID] != nil:
+	case peers[n.ID()] != nil:
 		return errAlreadyConnected
 		return errAlreadyConnected
-	case s.ntab != nil && n.ID == s.ntab.Self().ID:
+	case s.ntab != nil && n.ID() == s.ntab.Self().ID():
 		return errSelf
 		return errSelf
-	case s.netrestrict != nil && !s.netrestrict.Contains(n.IP):
+	case s.netrestrict != nil && !s.netrestrict.Contains(n.IP()):
 		return errNotWhitelisted
 		return errNotWhitelisted
-	case s.hist.contains(n.ID):
+	case s.hist.contains(n.ID()):
 		return errRecentlyDialed
 		return errRecentlyDialed
 	}
 	}
 	return nil
 	return nil
@@ -280,8 +279,8 @@ func (s *dialstate) checkDial(n *discover.Node, peers map[discover.NodeID]*Peer)
 func (s *dialstate) taskDone(t task, now time.Time) {
 func (s *dialstate) taskDone(t task, now time.Time) {
 	switch t := t.(type) {
 	switch t := t.(type) {
 	case *dialTask:
 	case *dialTask:
-		s.hist.add(t.dest.ID, now.Add(dialHistoryExpiration))
-		delete(s.dialing, t.dest.ID)
+		s.hist.add(t.dest.ID(), now.Add(dialHistoryExpiration))
+		delete(s.dialing, t.dest.ID())
 	case *discoverTask:
 	case *discoverTask:
 		s.lookupRunning = false
 		s.lookupRunning = false
 		s.lookupBuf = append(s.lookupBuf, t.results...)
 		s.lookupBuf = append(s.lookupBuf, t.results...)
@@ -323,7 +322,7 @@ func (t *dialTask) resolve(srv *Server) bool {
 	if time.Since(t.lastResolved) < t.resolveDelay {
 	if time.Since(t.lastResolved) < t.resolveDelay {
 		return false
 		return false
 	}
 	}
-	resolved := srv.ntab.Resolve(t.dest.ID)
+	resolved := srv.ntab.Resolve(t.dest)
 	t.lastResolved = time.Now()
 	t.lastResolved = time.Now()
 	if resolved == nil {
 	if resolved == nil {
 		t.resolveDelay *= 2
 		t.resolveDelay *= 2
@@ -336,7 +335,7 @@ func (t *dialTask) resolve(srv *Server) bool {
 	// The node was found.
 	// The node was found.
 	t.resolveDelay = initialResolveDelay
 	t.resolveDelay = initialResolveDelay
 	t.dest = resolved
 	t.dest = resolved
-	log.Debug("Resolved node", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP, Port: int(t.dest.TCP)})
+	log.Debug("Resolved node", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP(), Port: t.dest.TCP()})
 	return true
 	return true
 }
 }
 
 
@@ -345,7 +344,7 @@ type dialError struct {
 }
 }
 
 
 // dial performs the actual connection attempt.
 // dial performs the actual connection attempt.
-func (t *dialTask) dial(srv *Server, dest *discover.Node) error {
+func (t *dialTask) dial(srv *Server, dest *enode.Node) error {
 	fd, err := srv.Dialer.Dial(dest)
 	fd, err := srv.Dialer.Dial(dest)
 	if err != nil {
 	if err != nil {
 		return &dialError{err}
 		return &dialError{err}
@@ -355,7 +354,8 @@ func (t *dialTask) dial(srv *Server, dest *discover.Node) error {
 }
 }
 
 
 func (t *dialTask) String() string {
 func (t *dialTask) String() string {
-	return fmt.Sprintf("%v %x %v:%d", t.flags, t.dest.ID[:8], t.dest.IP, t.dest.TCP)
+	id := t.dest.ID()
+	return fmt.Sprintf("%v %x %v:%d", t.flags, id[:8], t.dest.IP(), t.dest.TCP())
 }
 }
 
 
 func (t *discoverTask) Do(srv *Server) {
 func (t *discoverTask) Do(srv *Server) {
@@ -367,9 +367,7 @@ func (t *discoverTask) Do(srv *Server) {
 		time.Sleep(next.Sub(now))
 		time.Sleep(next.Sub(now))
 	}
 	}
 	srv.lastLookup = time.Now()
 	srv.lastLookup = time.Now()
-	var target discover.NodeID
-	rand.Read(target[:])
-	t.results = srv.ntab.Lookup(target)
+	t.results = srv.ntab.LookupRandom()
 }
 }
 
 
 func (t *discoverTask) String() string {
 func (t *discoverTask) String() string {
@@ -391,11 +389,11 @@ func (t waitExpireTask) String() string {
 func (h dialHistory) min() pastDial {
 func (h dialHistory) min() pastDial {
 	return h[0]
 	return h[0]
 }
 }
-func (h *dialHistory) add(id discover.NodeID, exp time.Time) {
+func (h *dialHistory) add(id enode.ID, exp time.Time) {
 	heap.Push(h, pastDial{id, exp})
 	heap.Push(h, pastDial{id, exp})
 
 
 }
 }
-func (h *dialHistory) remove(id discover.NodeID) bool {
+func (h *dialHistory) remove(id enode.ID) bool {
 	for i, v := range *h {
 	for i, v := range *h {
 		if v.id == id {
 		if v.id == id {
 			heap.Remove(h, i)
 			heap.Remove(h, i)
@@ -404,7 +402,7 @@ func (h *dialHistory) remove(id discover.NodeID) bool {
 	}
 	}
 	return false
 	return false
 }
 }
-func (h dialHistory) contains(id discover.NodeID) bool {
+func (h dialHistory) contains(id enode.ID) bool {
 	for _, v := range h {
 	for _, v := range h {
 		if v.id == id {
 		if v.id == id {
 			return true
 			return true

+ 226 - 217
p2p/dial_test.go

@@ -24,7 +24,8 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/davecgh/go-spew/spew"
 	"github.com/davecgh/go-spew/spew"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
+	"github.com/ethereum/go-ethereum/p2p/enr"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 )
 )
 
 
@@ -48,10 +49,10 @@ func runDialTest(t *testing.T, test dialtest) {
 		vtime   time.Time
 		vtime   time.Time
 		running int
 		running int
 	)
 	)
-	pm := func(ps []*Peer) map[discover.NodeID]*Peer {
-		m := make(map[discover.NodeID]*Peer)
+	pm := func(ps []*Peer) map[enode.ID]*Peer {
+		m := make(map[enode.ID]*Peer)
 		for _, p := range ps {
 		for _, p := range ps {
-			m[p.rw.id] = p
+			m[p.ID()] = p
 		}
 		}
 		return m
 		return m
 	}
 	}
@@ -69,6 +70,7 @@ func runDialTest(t *testing.T, test dialtest) {
 			t.Errorf("round %d: new tasks mismatch:\ngot %v\nwant %v\nstate: %v\nrunning: %v\n",
 			t.Errorf("round %d: new tasks mismatch:\ngot %v\nwant %v\nstate: %v\nrunning: %v\n",
 				i, spew.Sdump(new), spew.Sdump(round.new), spew.Sdump(test.init), spew.Sdump(running))
 				i, spew.Sdump(new), spew.Sdump(round.new), spew.Sdump(test.init), spew.Sdump(running))
 		}
 		}
+		t.Log("tasks:", spew.Sdump(new))
 
 
 		// Time advances by 16 seconds on every round.
 		// Time advances by 16 seconds on every round.
 		vtime = vtime.Add(16 * time.Second)
 		vtime = vtime.Add(16 * time.Second)
@@ -76,13 +78,13 @@ func runDialTest(t *testing.T, test dialtest) {
 	}
 	}
 }
 }
 
 
-type fakeTable []*discover.Node
+type fakeTable []*enode.Node
 
 
-func (t fakeTable) Self() *discover.Node                     { return new(discover.Node) }
-func (t fakeTable) Close()                                   {}
-func (t fakeTable) Lookup(discover.NodeID) []*discover.Node  { return nil }
-func (t fakeTable) Resolve(discover.NodeID) *discover.Node   { return nil }
-func (t fakeTable) ReadRandomNodes(buf []*discover.Node) int { return copy(buf, t) }
+func (t fakeTable) Self() *enode.Node                     { return new(enode.Node) }
+func (t fakeTable) Close()                                {}
+func (t fakeTable) LookupRandom() []*enode.Node           { return nil }
+func (t fakeTable) Resolve(*enode.Node) *enode.Node       { return nil }
+func (t fakeTable) ReadRandomNodes(buf []*enode.Node) int { return copy(buf, t) }
 
 
 // This test checks that dynamic dials are launched from discovery results.
 // This test checks that dynamic dials are launched from discovery results.
 func TestDialStateDynDial(t *testing.T) {
 func TestDialStateDynDial(t *testing.T) {
@@ -92,63 +94,63 @@ func TestDialStateDynDial(t *testing.T) {
 			// A discovery query is launched.
 			// A discovery query is launched.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
 				},
 				},
 				new: []task{&discoverTask{}},
 				new: []task{&discoverTask{}},
 			},
 			},
 			// Dynamic dials are launched when it completes.
 			// Dynamic dials are launched when it completes.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
-					&discoverTask{results: []*discover.Node{
-						{ID: uintID(2)}, // this one is already connected and not dialed.
-						{ID: uintID(3)},
-						{ID: uintID(4)},
-						{ID: uintID(5)},
-						{ID: uintID(6)}, // these are not tried because max dyn dials is 5
-						{ID: uintID(7)}, // ...
+					&discoverTask{results: []*enode.Node{
+						newNode(uintID(2), nil), // this one is already connected and not dialed.
+						newNode(uintID(3), nil),
+						newNode(uintID(4), nil),
+						newNode(uintID(5), nil),
+						newNode(uintID(6), nil), // these are not tried because max dyn dials is 5
+						newNode(uintID(7), nil), // ...
 					}},
 					}},
 				},
 				},
 				new: []task{
 				new: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
 				},
 				},
 			},
 			},
 			// Some of the dials complete but no new ones are launched yet because
 			// Some of the dials complete but no new ones are launched yet because
 			// the sum of active dial count and dynamic peer count is == maxDynDials.
 			// the sum of active dial count and dynamic peer count is == maxDynDials.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(3), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
 				},
 				},
 			},
 			},
 			// No new dial tasks are launched in the this round because
 			// No new dial tasks are launched in the this round because
 			// maxDynDials has been reached.
 			// maxDynDials has been reached.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(3), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
 				},
 				},
 				new: []task{
 				new: []task{
 					&waitExpireTask{Duration: 14 * time.Second},
 					&waitExpireTask{Duration: 14 * time.Second},
@@ -158,14 +160,14 @@ func TestDialStateDynDial(t *testing.T) {
 			// results from last discovery lookup are reused.
 			// results from last discovery lookup are reused.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(3), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
 				},
 				},
 				new: []task{
 				new: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(6), nil)},
 				},
 				},
 			},
 			},
 			// More peers (3,4) drop off and dial for ID 6 completes.
 			// More peers (3,4) drop off and dial for ID 6 completes.
@@ -173,15 +175,15 @@ func TestDialStateDynDial(t *testing.T) {
 			// and a new one is spawned because more candidates are needed.
 			// and a new one is spawned because more candidates are needed.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(6), nil)},
 				},
 				},
 				new: []task{
 				new: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(7), nil)},
 					&discoverTask{},
 					&discoverTask{},
 				},
 				},
 			},
 			},
@@ -190,23 +192,23 @@ func TestDialStateDynDial(t *testing.T) {
 			// no new is started.
 			// no new is started.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(7)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(7), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(7), nil)},
 				},
 				},
 			},
 			},
 			// Finish the running node discovery with an empty set. A new lookup
 			// Finish the running node discovery with an empty set. A new lookup
 			// should be immediately requested.
 			// should be immediately requested.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(7)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(7), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
 					&discoverTask{},
 					&discoverTask{},
@@ -221,17 +223,17 @@ func TestDialStateDynDial(t *testing.T) {
 
 
 // Tests that bootnodes are dialed if no peers are connectd, but not otherwise.
 // Tests that bootnodes are dialed if no peers are connectd, but not otherwise.
 func TestDialStateDynDialBootnode(t *testing.T) {
 func TestDialStateDynDialBootnode(t *testing.T) {
-	bootnodes := []*discover.Node{
-		{ID: uintID(1)},
-		{ID: uintID(2)},
-		{ID: uintID(3)},
+	bootnodes := []*enode.Node{
+		newNode(uintID(1), nil),
+		newNode(uintID(2), nil),
+		newNode(uintID(3), nil),
 	}
 	}
 	table := fakeTable{
 	table := fakeTable{
-		{ID: uintID(4)},
-		{ID: uintID(5)},
-		{ID: uintID(6)},
-		{ID: uintID(7)},
-		{ID: uintID(8)},
+		newNode(uintID(4), nil),
+		newNode(uintID(5), nil),
+		newNode(uintID(6), nil),
+		newNode(uintID(7), nil),
+		newNode(uintID(8), nil),
 	}
 	}
 	runDialTest(t, dialtest{
 	runDialTest(t, dialtest{
 		init: newDialState(nil, bootnodes, table, 5, nil),
 		init: newDialState(nil, bootnodes, table, 5, nil),
@@ -239,16 +241,16 @@ func TestDialStateDynDialBootnode(t *testing.T) {
 			// 2 dynamic dials attempted, bootnodes pending fallback interval
 			// 2 dynamic dials attempted, bootnodes pending fallback interval
 			{
 			{
 				new: []task{
 				new: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
 					&discoverTask{},
 					&discoverTask{},
 				},
 				},
 			},
 			},
 			// No dials succeed, bootnodes still pending fallback interval
 			// No dials succeed, bootnodes still pending fallback interval
 			{
 			{
 				done: []task{
 				done: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
 				},
 				},
 			},
 			},
 			// No dials succeed, bootnodes still pending fallback interval
 			// No dials succeed, bootnodes still pending fallback interval
@@ -256,51 +258,51 @@ func TestDialStateDynDialBootnode(t *testing.T) {
 			// No dials succeed, 2 dynamic dials attempted and 1 bootnode too as fallback interval was reached
 			// No dials succeed, 2 dynamic dials attempted and 1 bootnode too as fallback interval was reached
 			{
 			{
 				new: []task{
 				new: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
 				},
 				},
 			},
 			},
 			// No dials succeed, 2nd bootnode is attempted
 			// No dials succeed, 2nd bootnode is attempted
 			{
 			{
 				done: []task{
 				done: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
 				},
 				},
 				new: []task{
 				new: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(2), nil)},
 				},
 				},
 			},
 			},
 			// No dials succeed, 3rd bootnode is attempted
 			// No dials succeed, 3rd bootnode is attempted
 			{
 			{
 				done: []task{
 				done: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(2), nil)},
 				},
 				},
 				new: []task{
 				new: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
 				},
 				},
 			},
 			},
 			// No dials succeed, 1st bootnode is attempted again, expired random nodes retried
 			// No dials succeed, 1st bootnode is attempted again, expired random nodes retried
 			{
 			{
 				done: []task{
 				done: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
 				},
 				},
 				new: []task{
 				new: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
 				},
 				},
 			},
 			},
 			// Random dial succeeds, no more bootnodes are attempted
 			// Random dial succeeds, no more bootnodes are attempted
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
 				},
 				},
 			},
 			},
 		},
 		},
@@ -311,14 +313,14 @@ func TestDialStateDynDialFromTable(t *testing.T) {
 	// This table always returns the same random nodes
 	// This table always returns the same random nodes
 	// in the order given below.
 	// in the order given below.
 	table := fakeTable{
 	table := fakeTable{
-		{ID: uintID(1)},
-		{ID: uintID(2)},
-		{ID: uintID(3)},
-		{ID: uintID(4)},
-		{ID: uintID(5)},
-		{ID: uintID(6)},
-		{ID: uintID(7)},
-		{ID: uintID(8)},
+		newNode(uintID(1), nil),
+		newNode(uintID(2), nil),
+		newNode(uintID(3), nil),
+		newNode(uintID(4), nil),
+		newNode(uintID(5), nil),
+		newNode(uintID(6), nil),
+		newNode(uintID(7), nil),
+		newNode(uintID(8), nil),
 	}
 	}
 
 
 	runDialTest(t, dialtest{
 	runDialTest(t, dialtest{
@@ -327,63 +329,63 @@ func TestDialStateDynDialFromTable(t *testing.T) {
 			// 5 out of 8 of the nodes returned by ReadRandomNodes are dialed.
 			// 5 out of 8 of the nodes returned by ReadRandomNodes are dialed.
 			{
 			{
 				new: []task{
 				new: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(2), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
 					&discoverTask{},
 					&discoverTask{},
 				},
 				},
 			},
 			},
 			// Dialing nodes 1,2 succeeds. Dials from the lookup are launched.
 			// Dialing nodes 1,2 succeeds. Dials from the lookup are launched.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
-					&discoverTask{results: []*discover.Node{
-						{ID: uintID(10)},
-						{ID: uintID(11)},
-						{ID: uintID(12)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(2), nil)},
+					&discoverTask{results: []*enode.Node{
+						newNode(uintID(10), nil),
+						newNode(uintID(11), nil),
+						newNode(uintID(12), nil),
 					}},
 					}},
 				},
 				},
 				new: []task{
 				new: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(10), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(11), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(12), nil)},
 					&discoverTask{},
 					&discoverTask{},
 				},
 				},
 			},
 			},
 			// Dialing nodes 3,4,5 fails. The dials from the lookup succeed.
 			// Dialing nodes 3,4,5 fails. The dials from the lookup succeed.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(10), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(11), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(12), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
-					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(10), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(11), nil)},
+					&dialTask{flags: dynDialedConn, dest: newNode(uintID(12), nil)},
 				},
 				},
 			},
 			},
 			// Waiting for expiry. No waitExpireTask is launched because the
 			// Waiting for expiry. No waitExpireTask is launched because the
 			// discovery query is still running.
 			// discovery query is still running.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(10), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(11), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(12), nil)}},
 				},
 				},
 			},
 			},
 			// Nodes 3,4 are not tried again because only the first two
 			// Nodes 3,4 are not tried again because only the first two
@@ -391,30 +393,38 @@ func TestDialStateDynDialFromTable(t *testing.T) {
 			// already connected.
 			// already connected.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(10), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(11), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(12), nil)}},
 				},
 				},
 			},
 			},
 		},
 		},
 	})
 	})
 }
 }
 
 
+func newNode(id enode.ID, ip net.IP) *enode.Node {
+	var r enr.Record
+	if ip != nil {
+		r.Set(enr.IP(ip))
+	}
+	return enode.SignNull(&r, id)
+}
+
 // This test checks that candidates that do not match the netrestrict list are not dialed.
 // This test checks that candidates that do not match the netrestrict list are not dialed.
 func TestDialStateNetRestrict(t *testing.T) {
 func TestDialStateNetRestrict(t *testing.T) {
 	// This table always returns the same random nodes
 	// This table always returns the same random nodes
 	// in the order given below.
 	// in the order given below.
 	table := fakeTable{
 	table := fakeTable{
-		{ID: uintID(1), IP: net.ParseIP("127.0.0.1")},
-		{ID: uintID(2), IP: net.ParseIP("127.0.0.2")},
-		{ID: uintID(3), IP: net.ParseIP("127.0.0.3")},
-		{ID: uintID(4), IP: net.ParseIP("127.0.0.4")},
-		{ID: uintID(5), IP: net.ParseIP("127.0.2.5")},
-		{ID: uintID(6), IP: net.ParseIP("127.0.2.6")},
-		{ID: uintID(7), IP: net.ParseIP("127.0.2.7")},
-		{ID: uintID(8), IP: net.ParseIP("127.0.2.8")},
+		newNode(uintID(1), net.ParseIP("127.0.0.1")),
+		newNode(uintID(2), net.ParseIP("127.0.0.2")),
+		newNode(uintID(3), net.ParseIP("127.0.0.3")),
+		newNode(uintID(4), net.ParseIP("127.0.0.4")),
+		newNode(uintID(5), net.ParseIP("127.0.2.5")),
+		newNode(uintID(6), net.ParseIP("127.0.2.6")),
+		newNode(uintID(7), net.ParseIP("127.0.2.7")),
+		newNode(uintID(8), net.ParseIP("127.0.2.8")),
 	}
 	}
 	restrict := new(netutil.Netlist)
 	restrict := new(netutil.Netlist)
 	restrict.Add("127.0.2.0/24")
 	restrict.Add("127.0.2.0/24")
@@ -434,12 +444,12 @@ func TestDialStateNetRestrict(t *testing.T) {
 
 
 // This test checks that static dials are launched.
 // This test checks that static dials are launched.
 func TestDialStateStaticDial(t *testing.T) {
 func TestDialStateStaticDial(t *testing.T) {
-	wantStatic := []*discover.Node{
-		{ID: uintID(1)},
-		{ID: uintID(2)},
-		{ID: uintID(3)},
-		{ID: uintID(4)},
-		{ID: uintID(5)},
+	wantStatic := []*enode.Node{
+		newNode(uintID(1), nil),
+		newNode(uintID(2), nil),
+		newNode(uintID(3), nil),
+		newNode(uintID(4), nil),
+		newNode(uintID(5), nil),
 	}
 	}
 
 
 	runDialTest(t, dialtest{
 	runDialTest(t, dialtest{
@@ -449,40 +459,40 @@ func TestDialStateStaticDial(t *testing.T) {
 			// aren't yet connected.
 			// aren't yet connected.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
 				},
 				},
 				new: []task{
 				new: []task{
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(4), nil)},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(5), nil)},
 				},
 				},
 			},
 			},
 			// No new tasks are launched in this round because all static
 			// No new tasks are launched in this round because all static
 			// nodes are either connected or still being dialed.
 			// nodes are either connected or still being dialed.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
-					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
 				},
 				},
 			},
 			},
 			// No new dial tasks are launched because all static
 			// No new dial tasks are launched because all static
 			// nodes are now connected.
 			// nodes are now connected.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
-					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
-					{rw: &conn{flags: staticDialedConn, id: uintID(4)}},
-					{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(4), nil)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(5), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(4), nil)},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(5), nil)},
 				},
 				},
 				new: []task{
 				new: []task{
 					&waitExpireTask{Duration: 14 * time.Second},
 					&waitExpireTask{Duration: 14 * time.Second},
@@ -491,24 +501,24 @@ func TestDialStateStaticDial(t *testing.T) {
 			// Wait a round for dial history to expire, no new tasks should spawn.
 			// Wait a round for dial history to expire, no new tasks should spawn.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
-					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
-					{rw: &conn{flags: staticDialedConn, id: uintID(4)}},
-					{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(4), nil)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(5), nil)}},
 				},
 				},
 			},
 			},
 			// If a static node is dropped, it should be immediately redialed,
 			// If a static node is dropped, it should be immediately redialed,
 			// irrespective whether it was originally static or dynamic.
 			// irrespective whether it was originally static or dynamic.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
-					{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(5), nil)}},
 				},
 				},
 				new: []task{
 				new: []task{
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(4), nil)},
 				},
 				},
 			},
 			},
 		},
 		},
@@ -517,9 +527,9 @@ func TestDialStateStaticDial(t *testing.T) {
 
 
 // This test checks that static peers will be redialed immediately if they were re-added to a static list.
 // This test checks that static peers will be redialed immediately if they were re-added to a static list.
 func TestDialStaticAfterReset(t *testing.T) {
 func TestDialStaticAfterReset(t *testing.T) {
-	wantStatic := []*discover.Node{
-		{ID: uintID(1)},
-		{ID: uintID(2)},
+	wantStatic := []*enode.Node{
+		newNode(uintID(1), nil),
+		newNode(uintID(2), nil),
 	}
 	}
 
 
 	rounds := []round{
 	rounds := []round{
@@ -527,19 +537,19 @@ func TestDialStaticAfterReset(t *testing.T) {
 		{
 		{
 			peers: nil,
 			peers: nil,
 			new: []task{
 			new: []task{
-				&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
-				&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
+				&dialTask{flags: staticDialedConn, dest: newNode(uintID(1), nil)},
+				&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
 			},
 			},
 		},
 		},
 		// No new dial tasks, all peers are connected.
 		// No new dial tasks, all peers are connected.
 		{
 		{
 			peers: []*Peer{
 			peers: []*Peer{
-				{rw: &conn{flags: staticDialedConn, id: uintID(1)}},
-				{rw: &conn{flags: staticDialedConn, id: uintID(2)}},
+				{rw: &conn{flags: staticDialedConn, node: newNode(uintID(1), nil)}},
+				{rw: &conn{flags: staticDialedConn, node: newNode(uintID(2), nil)}},
 			},
 			},
 			done: []task{
 			done: []task{
-				&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
-				&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
+				&dialTask{flags: staticDialedConn, dest: newNode(uintID(1), nil)},
+				&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
 			},
 			},
 			new: []task{
 			new: []task{
 				&waitExpireTask{Duration: 30 * time.Second},
 				&waitExpireTask{Duration: 30 * time.Second},
@@ -561,10 +571,10 @@ func TestDialStaticAfterReset(t *testing.T) {
 
 
 // This test checks that past dials are not retried for some time.
 // This test checks that past dials are not retried for some time.
 func TestDialStateCache(t *testing.T) {
 func TestDialStateCache(t *testing.T) {
-	wantStatic := []*discover.Node{
-		{ID: uintID(1)},
-		{ID: uintID(2)},
-		{ID: uintID(3)},
+	wantStatic := []*enode.Node{
+		newNode(uintID(1), nil),
+		newNode(uintID(2), nil),
+		newNode(uintID(3), nil),
 	}
 	}
 
 
 	runDialTest(t, dialtest{
 	runDialTest(t, dialtest{
@@ -575,32 +585,32 @@ func TestDialStateCache(t *testing.T) {
 			{
 			{
 				peers: nil,
 				peers: nil,
 				new: []task{
 				new: []task{
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(1), nil)},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
 				},
 				},
 			},
 			},
 			// No new tasks are launched in this round because all static
 			// No new tasks are launched in this round because all static
 			// nodes are either connected or still being dialed.
 			// nodes are either connected or still being dialed.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: staticDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: staticDialedConn, id: uintID(2)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(2), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(1), nil)},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
 				},
 				},
 			},
 			},
 			// A salvage task is launched to wait for node 3's history
 			// A salvage task is launched to wait for node 3's history
 			// entry to expire.
 			// entry to expire.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
 				},
 				},
 				done: []task{
 				done: []task{
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
 				},
 				},
 				new: []task{
 				new: []task{
 					&waitExpireTask{Duration: 14 * time.Second},
 					&waitExpireTask{Duration: 14 * time.Second},
@@ -609,18 +619,18 @@ func TestDialStateCache(t *testing.T) {
 			// Still waiting for node 3's entry to expire in the cache.
 			// Still waiting for node 3's entry to expire in the cache.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
 				},
 				},
 			},
 			},
 			// The cache entry for node 3 has expired and is retried.
 			// The cache entry for node 3 has expired and is retried.
 			{
 			{
 				peers: []*Peer{
 				peers: []*Peer{
-					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
-					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
+					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
 				},
 				},
 				new: []task{
 				new: []task{
-					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
+					&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
 				},
 				},
 			},
 			},
 		},
 		},
@@ -628,12 +638,12 @@ func TestDialStateCache(t *testing.T) {
 }
 }
 
 
 func TestDialResolve(t *testing.T) {
 func TestDialResolve(t *testing.T) {
-	resolved := discover.NewNode(uintID(1), net.IP{127, 0, 55, 234}, 3333, 4444)
+	resolved := newNode(uintID(1), net.IP{127, 0, 55, 234})
 	table := &resolveMock{answer: resolved}
 	table := &resolveMock{answer: resolved}
 	state := newDialState(nil, nil, table, 0, nil)
 	state := newDialState(nil, nil, table, 0, nil)
 
 
 	// Check that the task is generated with an incomplete ID.
 	// Check that the task is generated with an incomplete ID.
-	dest := discover.NewNode(uintID(1), nil, 0, 0)
+	dest := newNode(uintID(1), nil)
 	state.addStatic(dest)
 	state.addStatic(dest)
 	tasks := state.newTasks(0, nil, time.Time{})
 	tasks := state.newTasks(0, nil, time.Time{})
 	if !reflect.DeepEqual(tasks, []task{&dialTask{flags: staticDialedConn, dest: dest}}) {
 	if !reflect.DeepEqual(tasks, []task{&dialTask{flags: staticDialedConn, dest: dest}}) {
@@ -644,7 +654,7 @@ func TestDialResolve(t *testing.T) {
 	config := Config{Dialer: TCPDialer{&net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}}
 	config := Config{Dialer: TCPDialer{&net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}}
 	srv := &Server{ntab: table, Config: config}
 	srv := &Server{ntab: table, Config: config}
 	tasks[0].Do(srv)
 	tasks[0].Do(srv)
-	if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) {
+	if !reflect.DeepEqual(table.resolveCalls, []*enode.Node{dest}) {
 		t.Fatalf("wrong resolve calls, got %v", table.resolveCalls)
 		t.Fatalf("wrong resolve calls, got %v", table.resolveCalls)
 	}
 	}
 
 
@@ -672,25 +682,24 @@ next:
 	return true
 	return true
 }
 }
 
 
-func uintID(i uint32) discover.NodeID {
-	var id discover.NodeID
+func uintID(i uint32) enode.ID {
+	var id enode.ID
 	binary.BigEndian.PutUint32(id[:], i)
 	binary.BigEndian.PutUint32(id[:], i)
 	return id
 	return id
 }
 }
 
 
 // implements discoverTable for TestDialResolve
 // implements discoverTable for TestDialResolve
 type resolveMock struct {
 type resolveMock struct {
-	resolveCalls []discover.NodeID
-	answer       *discover.Node
+	resolveCalls []*enode.Node
+	answer       *enode.Node
 }
 }
 
 
-func (t *resolveMock) Resolve(id discover.NodeID) *discover.Node {
-	t.resolveCalls = append(t.resolveCalls, id)
+func (t *resolveMock) Resolve(n *enode.Node) *enode.Node {
+	t.resolveCalls = append(t.resolveCalls, n)
 	return t.answer
 	return t.answer
 }
 }
 
 
-func (t *resolveMock) Self() *discover.Node                     { return new(discover.Node) }
-func (t *resolveMock) Close()                                   {}
-func (t *resolveMock) Bootstrap([]*discover.Node)               {}
-func (t *resolveMock) Lookup(discover.NodeID) []*discover.Node  { return nil }
-func (t *resolveMock) ReadRandomNodes(buf []*discover.Node) int { return 0 }
+func (t *resolveMock) Self() *enode.Node                     { return new(enode.Node) }
+func (t *resolveMock) Close()                                {}
+func (t *resolveMock) LookupRandom() []*enode.Node           { return nil }
+func (t *resolveMock) ReadRandomNodes(buf []*enode.Node) int { return 0 }

+ 47 - 375
p2p/discover/node.go

@@ -18,415 +18,87 @@ package discover
 
 
 import (
 import (
 	"crypto/ecdsa"
 	"crypto/ecdsa"
-	"crypto/elliptic"
-	"encoding/hex"
 	"errors"
 	"errors"
-	"fmt"
 	"math/big"
 	"math/big"
-	"math/rand"
 	"net"
 	"net"
-	"net/url"
-	"regexp"
-	"strconv"
-	"strings"
 	"time"
 	"time"
 
 
-	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/math"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto/secp256k1"
 	"github.com/ethereum/go-ethereum/crypto/secp256k1"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 )
 
 
-const NodeIDBits = 512
-
-// Node represents a host on the network.
+// node represents a host on the network.
 // The fields of Node may not be modified.
 // The fields of Node may not be modified.
-type Node struct {
-	IP       net.IP // len 4 for IPv4 or 16 for IPv6
-	UDP, TCP uint16 // port numbers
-	ID       NodeID // the node's public key
-
-	// This is a cached copy of sha3(ID) which is used for node
-	// distance calculations. This is part of Node in order to make it
-	// possible to write tests that need a node at a certain distance.
-	// In those tests, the content of sha will not actually correspond
-	// with ID.
-	sha common.Hash
-
-	// Time when the node was added to the table.
-	addedAt time.Time
-}
-
-// NewNode creates a new node. It is mostly meant to be used for
-// testing purposes.
-func NewNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node {
-	if ipv4 := ip.To4(); ipv4 != nil {
-		ip = ipv4
-	}
-	return &Node{
-		IP:  ip,
-		UDP: udpPort,
-		TCP: tcpPort,
-		ID:  id,
-		sha: crypto.Keccak256Hash(id[:]),
-	}
-}
-
-func (n *Node) addr() *net.UDPAddr {
-	return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)}
-}
-
-// Incomplete returns true for nodes with no IP address.
-func (n *Node) Incomplete() bool {
-	return n.IP == nil
-}
-
-// checks whether n is a valid complete node.
-func (n *Node) validateComplete() error {
-	if n.Incomplete() {
-		return errors.New("incomplete node")
-	}
-	if n.UDP == 0 {
-		return errors.New("missing UDP port")
-	}
-	if n.TCP == 0 {
-		return errors.New("missing TCP port")
-	}
-	if n.IP.IsMulticast() || n.IP.IsUnspecified() {
-		return errors.New("invalid IP (multicast/unspecified)")
-	}
-	_, err := n.ID.Pubkey() // validate the key (on curve, etc.)
-	return err
-}
-
-// The string representation of a Node is a URL.
-// Please see ParseNode for a description of the format.
-func (n *Node) String() string {
-	u := url.URL{Scheme: "enode"}
-	if n.Incomplete() {
-		u.Host = fmt.Sprintf("%x", n.ID[:])
-	} else {
-		addr := net.TCPAddr{IP: n.IP, Port: int(n.TCP)}
-		u.User = url.User(fmt.Sprintf("%x", n.ID[:]))
-		u.Host = addr.String()
-		if n.UDP != n.TCP {
-			u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP))
-		}
-	}
-	return u.String()
-}
-
-var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$")
-
-// ParseNode parses a node designator.
-//
-// There are two basic forms of node designators
-//   - incomplete nodes, which only have the public key (node ID)
-//   - complete nodes, which contain the public key and IP/Port information
-//
-// For incomplete nodes, the designator must look like one of these
-//
-//    enode://<hex node id>
-//    <hex node id>
-//
-// For complete nodes, the node ID is encoded in the username portion
-// of the URL, separated from the host by an @ sign. The hostname can
-// only be given as an IP address, DNS domain names are not allowed.
-// The port in the host name section is the TCP listening port. If the
-// TCP and UDP (discovery) ports differ, the UDP port is specified as
-// query parameter "discport".
-//
-// In the following example, the node URL describes
-// a node with IP address 10.3.58.6, TCP listening port 30303
-// and UDP discovery port 30301.
-//
-//    enode://<hex node id>@10.3.58.6:30303?discport=30301
-func ParseNode(rawurl string) (*Node, error) {
-	if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil {
-		id, err := HexID(m[1])
-		if err != nil {
-			return nil, fmt.Errorf("invalid node ID (%v)", err)
-		}
-		return NewNode(id, nil, 0, 0), nil
-	}
-	return parseComplete(rawurl)
+type node struct {
+	enode.Node
+	addedAt time.Time // time when the node was added to the table
 }
 }
 
 
-func parseComplete(rawurl string) (*Node, error) {
-	var (
-		id               NodeID
-		ip               net.IP
-		tcpPort, udpPort uint64
-	)
-	u, err := url.Parse(rawurl)
-	if err != nil {
-		return nil, err
-	}
-	if u.Scheme != "enode" {
-		return nil, errors.New("invalid URL scheme, want \"enode\"")
-	}
-	// Parse the Node ID from the user portion.
-	if u.User == nil {
-		return nil, errors.New("does not contain node ID")
-	}
-	if id, err = HexID(u.User.String()); err != nil {
-		return nil, fmt.Errorf("invalid node ID (%v)", err)
-	}
-	// Parse the IP address.
-	host, port, err := net.SplitHostPort(u.Host)
-	if err != nil {
-		return nil, fmt.Errorf("invalid host: %v", err)
-	}
-	if ip = net.ParseIP(host); ip == nil {
-		return nil, errors.New("invalid IP address")
-	}
-	// Ensure the IP is 4 bytes long for IPv4 addresses.
-	if ipv4 := ip.To4(); ipv4 != nil {
-		ip = ipv4
-	}
-	// Parse the port numbers.
-	if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil {
-		return nil, errors.New("invalid port")
-	}
-	udpPort = tcpPort
-	qv := u.Query()
-	if qv.Get("discport") != "" {
-		udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16)
-		if err != nil {
-			return nil, errors.New("invalid discport in query")
-		}
-	}
-	return NewNode(id, ip, uint16(udpPort), uint16(tcpPort)), nil
-}
-
-// MustParseNode parses a node URL. It panics if the URL is not valid.
-func MustParseNode(rawurl string) *Node {
-	n, err := ParseNode(rawurl)
-	if err != nil {
-		panic("invalid node URL: " + err.Error())
-	}
-	return n
-}
+type encPubkey [64]byte
 
 
-// MarshalText implements encoding.TextMarshaler.
-func (n *Node) MarshalText() ([]byte, error) {
-	return []byte(n.String()), nil
+func encodePubkey(key *ecdsa.PublicKey) encPubkey {
+	var e encPubkey
+	math.ReadBits(key.X, e[:len(e)/2])
+	math.ReadBits(key.Y, e[len(e)/2:])
+	return e
 }
 }
 
 
-// UnmarshalText implements encoding.TextUnmarshaler.
-func (n *Node) UnmarshalText(text []byte) error {
-	dec, err := ParseNode(string(text))
-	if err == nil {
-		*n = *dec
-	}
-	return err
-}
-
-// NodeID is a unique identifier for each node.
-// The node identifier is a marshaled elliptic curve public key.
-type NodeID [NodeIDBits / 8]byte
-
-// Bytes returns a byte slice representation of the NodeID
-func (n NodeID) Bytes() []byte {
-	return n[:]
-}
-
-// NodeID prints as a long hexadecimal number.
-func (n NodeID) String() string {
-	return fmt.Sprintf("%x", n[:])
-}
-
-// The Go syntax representation of a NodeID is a call to HexID.
-func (n NodeID) GoString() string {
-	return fmt.Sprintf("discover.HexID(\"%x\")", n[:])
-}
-
-// TerminalString returns a shortened hex string for terminal logging.
-func (n NodeID) TerminalString() string {
-	return hex.EncodeToString(n[:8])
-}
-
-// MarshalText implements the encoding.TextMarshaler interface.
-func (n NodeID) MarshalText() ([]byte, error) {
-	return []byte(hex.EncodeToString(n[:])), nil
-}
-
-// UnmarshalText implements the encoding.TextUnmarshaler interface.
-func (n *NodeID) UnmarshalText(text []byte) error {
-	id, err := HexID(string(text))
-	if err != nil {
-		return err
-	}
-	*n = id
-	return nil
-}
-
-// BytesID converts a byte slice to a NodeID
-func BytesID(b []byte) (NodeID, error) {
-	var id NodeID
-	if len(b) != len(id) {
-		return id, fmt.Errorf("wrong length, want %d bytes", len(id))
-	}
-	copy(id[:], b)
-	return id, nil
-}
-
-// MustBytesID converts a byte slice to a NodeID.
-// It panics if the byte slice is not a valid NodeID.
-func MustBytesID(b []byte) NodeID {
-	id, err := BytesID(b)
-	if err != nil {
-		panic(err)
+func decodePubkey(e encPubkey) (*ecdsa.PublicKey, error) {
+	p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)}
+	half := len(e) / 2
+	p.X.SetBytes(e[:half])
+	p.Y.SetBytes(e[half:])
+	if !p.Curve.IsOnCurve(p.X, p.Y) {
+		return nil, errors.New("invalid secp256k1 curve point")
 	}
 	}
-	return id
+	return p, nil
 }
 }
 
 
-// HexID converts a hex string to a NodeID.
-// The string may be prefixed with 0x.
-func HexID(in string) (NodeID, error) {
-	var id NodeID
-	b, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
-	if err != nil {
-		return id, err
-	} else if len(b) != len(id) {
-		return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2)
-	}
-	copy(id[:], b)
-	return id, nil
+func (e encPubkey) id() enode.ID {
+	return enode.ID(crypto.Keccak256Hash(e[:]))
 }
 }
 
 
-// MustHexID converts a hex string to a NodeID.
-// It panics if the string is not a valid NodeID.
-func MustHexID(in string) NodeID {
-	id, err := HexID(in)
+// recoverNodeKey computes the public key used to sign the
+// given hash from the signature.
+func recoverNodeKey(hash, sig []byte) (key encPubkey, err error) {
+	pubkey, err := secp256k1.RecoverPubkey(hash, sig)
 	if err != nil {
 	if err != nil {
-		panic(err)
+		return key, err
 	}
 	}
-	return id
+	copy(key[:], pubkey[1:])
+	return key, nil
 }
 }
 
 
-// PubkeyID returns a marshaled representation of the given public key.
-func PubkeyID(pub *ecdsa.PublicKey) NodeID {
-	var id NodeID
-	pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y)
-	if len(pbytes)-1 != len(id) {
-		panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes)))
-	}
-	copy(id[:], pbytes[1:])
-	return id
+func wrapNode(n *enode.Node) *node {
+	return &node{Node: *n}
 }
 }
 
 
-// Pubkey returns the public key represented by the node ID.
-// It returns an error if the ID is not a point on the curve.
-func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) {
-	p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)}
-	half := len(id) / 2
-	p.X.SetBytes(id[:half])
-	p.Y.SetBytes(id[half:])
-	if !p.Curve.IsOnCurve(p.X, p.Y) {
-		return nil, errors.New("id is invalid secp256k1 curve point")
+func wrapNodes(ns []*enode.Node) []*node {
+	result := make([]*node, len(ns))
+	for i, n := range ns {
+		result[i] = wrapNode(n)
 	}
 	}
-	return p, nil
+	return result
 }
 }
 
 
-// recoverNodeID computes the public key used to sign the
-// given hash from the signature.
-func recoverNodeID(hash, sig []byte) (id NodeID, err error) {
-	pubkey, err := secp256k1.RecoverPubkey(hash, sig)
-	if err != nil {
-		return id, err
-	}
-	if len(pubkey)-1 != len(id) {
-		return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8)
-	}
-	for i := range id {
-		id[i] = pubkey[i+1]
-	}
-	return id, nil
+func unwrapNode(n *node) *enode.Node {
+	return &n.Node
 }
 }
 
 
-// distcmp compares the distances a->target and b->target.
-// Returns -1 if a is closer to target, 1 if b is closer to target
-// and 0 if they are equal.
-func distcmp(target, a, b common.Hash) int {
-	for i := range target {
-		da := a[i] ^ target[i]
-		db := b[i] ^ target[i]
-		if da > db {
-			return 1
-		} else if da < db {
-			return -1
-		}
+func unwrapNodes(ns []*node) []*enode.Node {
+	result := make([]*enode.Node, len(ns))
+	for i, n := range ns {
+		result[i] = unwrapNode(n)
 	}
 	}
-	return 0
+	return result
 }
 }
 
 
-// table of leading zero counts for bytes [0..255]
-var lzcount = [256]int{
-	8, 7, 6, 6, 5, 5, 5, 5,
-	4, 4, 4, 4, 4, 4, 4, 4,
-	3, 3, 3, 3, 3, 3, 3, 3,
-	3, 3, 3, 3, 3, 3, 3, 3,
-	2, 2, 2, 2, 2, 2, 2, 2,
-	2, 2, 2, 2, 2, 2, 2, 2,
-	2, 2, 2, 2, 2, 2, 2, 2,
-	2, 2, 2, 2, 2, 2, 2, 2,
-	1, 1, 1, 1, 1, 1, 1, 1,
-	1, 1, 1, 1, 1, 1, 1, 1,
-	1, 1, 1, 1, 1, 1, 1, 1,
-	1, 1, 1, 1, 1, 1, 1, 1,
-	1, 1, 1, 1, 1, 1, 1, 1,
-	1, 1, 1, 1, 1, 1, 1, 1,
-	1, 1, 1, 1, 1, 1, 1, 1,
-	1, 1, 1, 1, 1, 1, 1, 1,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0,
+func (n *node) addr() *net.UDPAddr {
+	return &net.UDPAddr{IP: n.IP(), Port: n.UDP()}
 }
 }
 
 
-// logdist returns the logarithmic distance between a and b, log2(a ^ b).
-func logdist(a, b common.Hash) int {
-	lz := 0
-	for i := range a {
-		x := a[i] ^ b[i]
-		if x == 0 {
-			lz += 8
-		} else {
-			lz += lzcount[x]
-			break
-		}
-	}
-	return len(a)*8 - lz
-}
-
-// hashAtDistance returns a random hash such that logdist(a, b) == n
-func hashAtDistance(a common.Hash, n int) (b common.Hash) {
-	if n == 0 {
-		return a
-	}
-	// flip bit at position n, fill the rest with random bits
-	b = a
-	pos := len(a) - n/8 - 1
-	bit := byte(0x01) << (byte(n%8) - 1)
-	if bit == 0 {
-		pos++
-		bit = 0x80
-	}
-	b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits
-	for i := pos + 1; i < len(a); i++ {
-		b[i] = byte(rand.Intn(255))
-	}
-	return b
+func (n *node) String() string {
+	return n.Node.String()
 }
 }

+ 108 - 116
p2p/discover/table.go

@@ -23,6 +23,7 @@
 package discover
 package discover
 
 
 import (
 import (
+	"crypto/ecdsa"
 	crand "crypto/rand"
 	crand "crypto/rand"
 	"encoding/binary"
 	"encoding/binary"
 	"fmt"
 	"fmt"
@@ -35,6 +36,7 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/crypto"
 	"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/netutil"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 )
 )
 
 
@@ -65,49 +67,44 @@ const (
 type Table struct {
 type Table struct {
 	mutex   sync.Mutex        // protects buckets, bucket content, nursery, rand
 	mutex   sync.Mutex        // protects buckets, bucket content, nursery, rand
 	buckets [nBuckets]*bucket // index of known nodes by distance
 	buckets [nBuckets]*bucket // index of known nodes by distance
-	nursery []*Node           // bootstrap nodes
+	nursery []*node           // bootstrap nodes
 	rand    *mrand.Rand       // source of randomness, periodically reseeded
 	rand    *mrand.Rand       // source of randomness, periodically reseeded
 	ips     netutil.DistinctNetSet
 	ips     netutil.DistinctNetSet
 
 
-	db         *nodeDB // database of known nodes
+	db         *enode.DB // database of known nodes
 	refreshReq chan chan struct{}
 	refreshReq chan chan struct{}
 	initDone   chan struct{}
 	initDone   chan struct{}
 	closeReq   chan struct{}
 	closeReq   chan struct{}
 	closed     chan struct{}
 	closed     chan struct{}
 
 
-	nodeAddedHook func(*Node) // for testing
+	nodeAddedHook func(*node) // for testing
 
 
 	net  transport
 	net  transport
-	self *Node // metadata of the local node
+	self *node // metadata of the local node
 }
 }
 
 
 // transport is implemented by the UDP transport.
 // transport is implemented by the UDP transport.
 // it is an interface so we can test without opening lots of UDP
 // it is an interface so we can test without opening lots of UDP
 // sockets and without generating a private key.
 // sockets and without generating a private key.
 type transport interface {
 type transport interface {
-	ping(NodeID, *net.UDPAddr) error
-	findnode(toid NodeID, addr *net.UDPAddr, target NodeID) ([]*Node, error)
+	ping(enode.ID, *net.UDPAddr) error
+	findnode(toid enode.ID, addr *net.UDPAddr, target encPubkey) ([]*node, error)
 	close()
 	close()
 }
 }
 
 
 // bucket contains nodes, ordered by their last activity. the entry
 // bucket contains nodes, ordered by their last activity. the entry
 // that was most recently active is the first element in entries.
 // that was most recently active is the first element in entries.
 type bucket struct {
 type bucket struct {
-	entries      []*Node // live entries, sorted by time of last contact
-	replacements []*Node // recently seen nodes to be used if revalidation fails
+	entries      []*node // live entries, sorted by time of last contact
+	replacements []*node // recently seen nodes to be used if revalidation fails
 	ips          netutil.DistinctNetSet
 	ips          netutil.DistinctNetSet
 }
 }
 
 
-func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string, bootnodes []*Node) (*Table, error) {
-	// If no node database was given, use an in-memory one
-	db, err := newNodeDB(nodeDBPath, nodeDBVersion, ourID)
-	if err != nil {
-		return nil, err
-	}
+func newTable(t transport, self *enode.Node, db *enode.DB, bootnodes []*enode.Node) (*Table, error) {
 	tab := &Table{
 	tab := &Table{
 		net:        t,
 		net:        t,
 		db:         db,
 		db:         db,
-		self:       NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)),
+		self:       wrapNode(self),
 		refreshReq: make(chan chan struct{}),
 		refreshReq: make(chan chan struct{}),
 		initDone:   make(chan struct{}),
 		initDone:   make(chan struct{}),
 		closeReq:   make(chan struct{}),
 		closeReq:   make(chan struct{}),
@@ -125,10 +122,7 @@ func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string
 	}
 	}
 	tab.seedRand()
 	tab.seedRand()
 	tab.loadSeedNodes()
 	tab.loadSeedNodes()
-	// Start the background expiration goroutine after loading seeds so that the search for
-	// seed nodes also considers older nodes that would otherwise be removed by the
-	// expiration.
-	tab.db.ensureExpirer()
+
 	go tab.loop()
 	go tab.loop()
 	return tab, nil
 	return tab, nil
 }
 }
@@ -143,15 +137,13 @@ func (tab *Table) seedRand() {
 }
 }
 
 
 // Self returns the local node.
 // Self returns the local node.
-// The returned node should not be modified by the caller.
-func (tab *Table) Self() *Node {
-	return tab.self
+func (tab *Table) Self() *enode.Node {
+	return unwrapNode(tab.self)
 }
 }
 
 
-// ReadRandomNodes fills the given slice with random nodes from the
-// table. It will not write the same node more than once. The nodes in
-// the slice are copies and can be modified by the caller.
-func (tab *Table) ReadRandomNodes(buf []*Node) (n int) {
+// ReadRandomNodes fills the given slice with random nodes from the table. The results
+// are guaranteed to be unique for a single invocation, no node will appear twice.
+func (tab *Table) ReadRandomNodes(buf []*enode.Node) (n int) {
 	if !tab.isInitDone() {
 	if !tab.isInitDone() {
 		return 0
 		return 0
 	}
 	}
@@ -159,7 +151,7 @@ func (tab *Table) ReadRandomNodes(buf []*Node) (n int) {
 	defer tab.mutex.Unlock()
 	defer tab.mutex.Unlock()
 
 
 	// Find all non-empty buckets and get a fresh slice of their entries.
 	// Find all non-empty buckets and get a fresh slice of their entries.
-	var buckets [][]*Node
+	var buckets [][]*node
 	for _, b := range &tab.buckets {
 	for _, b := range &tab.buckets {
 		if len(b.entries) > 0 {
 		if len(b.entries) > 0 {
 			buckets = append(buckets, b.entries)
 			buckets = append(buckets, b.entries)
@@ -177,7 +169,7 @@ func (tab *Table) ReadRandomNodes(buf []*Node) (n int) {
 	var i, j int
 	var i, j int
 	for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) {
 	for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) {
 		b := buckets[j]
 		b := buckets[j]
-		buf[i] = &(*b[0])
+		buf[i] = unwrapNode(b[0])
 		buckets[j] = b[1:]
 		buckets[j] = b[1:]
 		if len(b) == 1 {
 		if len(b) == 1 {
 			buckets = append(buckets[:j], buckets[j+1:]...)
 			buckets = append(buckets[:j], buckets[j+1:]...)
@@ -202,20 +194,13 @@ func (tab *Table) Close() {
 // setFallbackNodes sets the initial points of contact. These nodes
 // setFallbackNodes sets the initial points of contact. These nodes
 // are used to connect to the network if the table is empty and there
 // are used to connect to the network if the table is empty and there
 // are no known nodes in the database.
 // are no known nodes in the database.
-func (tab *Table) setFallbackNodes(nodes []*Node) error {
+func (tab *Table) setFallbackNodes(nodes []*enode.Node) error {
 	for _, n := range nodes {
 	for _, n := range nodes {
-		if err := n.validateComplete(); err != nil {
-			return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err)
+		if err := n.ValidateComplete(); err != nil {
+			return fmt.Errorf("bad bootstrap node %q: %v", n, err)
 		}
 		}
 	}
 	}
-	tab.nursery = make([]*Node, 0, len(nodes))
-	for _, n := range nodes {
-		cpy := *n
-		// Recompute cpy.sha because the node might not have been
-		// created by NewNode or ParseNode.
-		cpy.sha = crypto.Keccak256Hash(n.ID[:])
-		tab.nursery = append(tab.nursery, &cpy)
-	}
+	tab.nursery = wrapNodes(nodes)
 	return nil
 	return nil
 }
 }
 
 
@@ -231,47 +216,48 @@ func (tab *Table) isInitDone() bool {
 
 
 // Resolve searches for a specific node with the given ID.
 // Resolve searches for a specific node with the given ID.
 // It returns nil if the node could not be found.
 // It returns nil if the node could not be found.
-func (tab *Table) Resolve(targetID NodeID) *Node {
+func (tab *Table) Resolve(n *enode.Node) *enode.Node {
 	// If the node is present in the local table, no
 	// If the node is present in the local table, no
 	// network interaction is required.
 	// network interaction is required.
-	hash := crypto.Keccak256Hash(targetID[:])
+	hash := n.ID()
 	tab.mutex.Lock()
 	tab.mutex.Lock()
 	cl := tab.closest(hash, 1)
 	cl := tab.closest(hash, 1)
 	tab.mutex.Unlock()
 	tab.mutex.Unlock()
-	if len(cl.entries) > 0 && cl.entries[0].ID == targetID {
-		return cl.entries[0]
+	if len(cl.entries) > 0 && cl.entries[0].ID() == hash {
+		return unwrapNode(cl.entries[0])
 	}
 	}
 	// Otherwise, do a network lookup.
 	// Otherwise, do a network lookup.
-	result := tab.Lookup(targetID)
+	result := tab.lookup(encodePubkey(n.Pubkey()), true)
 	for _, n := range result {
 	for _, n := range result {
-		if n.ID == targetID {
-			return n
+		if n.ID() == hash {
+			return unwrapNode(n)
 		}
 		}
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-// Lookup performs a network search for nodes close
-// to the given target. It approaches the target by querying
-// nodes that are closer to it on each iteration.
-// The given target does not need to be an actual node
-// identifier.
-func (tab *Table) Lookup(targetID NodeID) []*Node {
-	return tab.lookup(targetID, true)
+// LookupRandom finds random nodes in the network.
+func (tab *Table) LookupRandom() []*enode.Node {
+	var target encPubkey
+	crand.Read(target[:])
+	return unwrapNodes(tab.lookup(target, true))
 }
 }
 
 
-func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node {
+// lookup performs a network search for nodes close to the given target. It approaches the
+// target by querying nodes that are closer to it on each iteration. The given target does
+// not need to be an actual node identifier.
+func (tab *Table) lookup(targetKey encPubkey, refreshIfEmpty bool) []*node {
 	var (
 	var (
-		target         = crypto.Keccak256Hash(targetID[:])
-		asked          = make(map[NodeID]bool)
-		seen           = make(map[NodeID]bool)
-		reply          = make(chan []*Node, alpha)
+		target         = enode.ID(crypto.Keccak256Hash(targetKey[:]))
+		asked          = make(map[enode.ID]bool)
+		seen           = make(map[enode.ID]bool)
+		reply          = make(chan []*node, alpha)
 		pendingQueries = 0
 		pendingQueries = 0
 		result         *nodesByDistance
 		result         *nodesByDistance
 	)
 	)
 	// don't query further if we hit ourself.
 	// don't query further if we hit ourself.
 	// unlikely to happen often in practice.
 	// unlikely to happen often in practice.
-	asked[tab.self.ID] = true
+	asked[tab.self.ID()] = true
 
 
 	for {
 	for {
 		tab.mutex.Lock()
 		tab.mutex.Lock()
@@ -293,10 +279,10 @@ func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node {
 		// ask the alpha closest nodes that we haven't asked yet
 		// ask the alpha closest nodes that we haven't asked yet
 		for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ {
 		for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ {
 			n := result.entries[i]
 			n := result.entries[i]
-			if !asked[n.ID] {
-				asked[n.ID] = true
+			if !asked[n.ID()] {
+				asked[n.ID()] = true
 				pendingQueries++
 				pendingQueries++
-				go tab.findnode(n, targetID, reply)
+				go tab.findnode(n, targetKey, reply)
 			}
 			}
 		}
 		}
 		if pendingQueries == 0 {
 		if pendingQueries == 0 {
@@ -305,8 +291,8 @@ func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node {
 		}
 		}
 		// wait for the next reply
 		// wait for the next reply
 		for _, n := range <-reply {
 		for _, n := range <-reply {
-			if n != nil && !seen[n.ID] {
-				seen[n.ID] = true
+			if n != nil && !seen[n.ID()] {
+				seen[n.ID()] = true
 				result.push(n, bucketSize)
 				result.push(n, bucketSize)
 			}
 			}
 		}
 		}
@@ -315,19 +301,19 @@ func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node {
 	return result.entries
 	return result.entries
 }
 }
 
 
-func (tab *Table) findnode(n *Node, targetID NodeID, reply chan<- []*Node) {
-	fails := tab.db.findFails(n.ID)
-	r, err := tab.net.findnode(n.ID, n.addr(), targetID)
+func (tab *Table) findnode(n *node, targetKey encPubkey, reply chan<- []*node) {
+	fails := tab.db.FindFails(n.ID())
+	r, err := tab.net.findnode(n.ID(), n.addr(), targetKey)
 	if err != nil || len(r) == 0 {
 	if err != nil || len(r) == 0 {
 		fails++
 		fails++
-		tab.db.updateFindFails(n.ID, fails)
-		log.Trace("Findnode failed", "id", n.ID, "failcount", fails, "err", err)
+		tab.db.UpdateFindFails(n.ID(), fails)
+		log.Trace("Findnode failed", "id", n.ID(), "failcount", fails, "err", err)
 		if fails >= maxFindnodeFailures {
 		if fails >= maxFindnodeFailures {
-			log.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails)
+			log.Trace("Too many findnode failures, dropping", "id", n.ID(), "failcount", fails)
 			tab.delete(n)
 			tab.delete(n)
 		}
 		}
 	} else if fails > 0 {
 	} else if fails > 0 {
-		tab.db.updateFindFails(n.ID, fails-1)
+		tab.db.UpdateFindFails(n.ID(), fails-1)
 	}
 	}
 
 
 	// Grab as many nodes as possible. Some of them might not be alive anymore, but we'll
 	// Grab as many nodes as possible. Some of them might not be alive anymore, but we'll
@@ -405,7 +391,6 @@ loop:
 	for _, ch := range waiting {
 	for _, ch := range waiting {
 		close(ch)
 		close(ch)
 	}
 	}
-	tab.db.close()
 	close(tab.closed)
 	close(tab.closed)
 }
 }
 
 
@@ -421,7 +406,11 @@ func (tab *Table) doRefresh(done chan struct{}) {
 	tab.loadSeedNodes()
 	tab.loadSeedNodes()
 
 
 	// Run self lookup to discover new neighbor nodes.
 	// Run self lookup to discover new neighbor nodes.
-	tab.lookup(tab.self.ID, false)
+	// We can only do this if we have a secp256k1 identity.
+	var key ecdsa.PublicKey
+	if err := tab.self.Load((*enode.Secp256k1)(&key)); err == nil {
+		tab.lookup(encodePubkey(&key), false)
+	}
 
 
 	// The Kademlia paper specifies that the bucket refresh should
 	// The Kademlia paper specifies that the bucket refresh should
 	// perform a lookup in the least recently used bucket. We cannot
 	// perform a lookup in the least recently used bucket. We cannot
@@ -430,19 +419,19 @@ func (tab *Table) doRefresh(done chan struct{}) {
 	// sha3 preimage that falls into a chosen bucket.
 	// sha3 preimage that falls into a chosen bucket.
 	// We perform a few lookups with a random target instead.
 	// We perform a few lookups with a random target instead.
 	for i := 0; i < 3; i++ {
 	for i := 0; i < 3; i++ {
-		var target NodeID
+		var target encPubkey
 		crand.Read(target[:])
 		crand.Read(target[:])
 		tab.lookup(target, false)
 		tab.lookup(target, false)
 	}
 	}
 }
 }
 
 
 func (tab *Table) loadSeedNodes() {
 func (tab *Table) loadSeedNodes() {
-	seeds := tab.db.querySeeds(seedCount, seedMaxAge)
+	seeds := wrapNodes(tab.db.QuerySeeds(seedCount, seedMaxAge))
 	seeds = append(seeds, tab.nursery...)
 	seeds = append(seeds, tab.nursery...)
 	for i := range seeds {
 	for i := range seeds {
 		seed := seeds[i]
 		seed := seeds[i]
-		age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.lastPongReceived(seed.ID)) }}
-		log.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age)
+		age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.LastPongReceived(seed.ID())) }}
+		log.Debug("Found seed node in database", "id", seed.ID(), "addr", seed.addr(), "age", age)
 		tab.add(seed)
 		tab.add(seed)
 	}
 	}
 }
 }
@@ -459,28 +448,28 @@ func (tab *Table) doRevalidate(done chan<- struct{}) {
 	}
 	}
 
 
 	// Ping the selected node and wait for a pong.
 	// Ping the selected node and wait for a pong.
-	err := tab.net.ping(last.ID, last.addr())
+	err := tab.net.ping(last.ID(), last.addr())
 
 
 	tab.mutex.Lock()
 	tab.mutex.Lock()
 	defer tab.mutex.Unlock()
 	defer tab.mutex.Unlock()
 	b := tab.buckets[bi]
 	b := tab.buckets[bi]
 	if err == nil {
 	if err == nil {
 		// The node responded, move it to the front.
 		// The node responded, move it to the front.
-		log.Trace("Revalidated node", "b", bi, "id", last.ID)
+		log.Debug("Revalidated node", "b", bi, "id", last.ID())
 		b.bump(last)
 		b.bump(last)
 		return
 		return
 	}
 	}
 	// No reply received, pick a replacement or delete the node if there aren't
 	// No reply received, pick a replacement or delete the node if there aren't
 	// any replacements.
 	// any replacements.
 	if r := tab.replace(b, last); r != nil {
 	if r := tab.replace(b, last); r != nil {
-		log.Trace("Replaced dead node", "b", bi, "id", last.ID, "ip", last.IP, "r", r.ID, "rip", r.IP)
+		log.Debug("Replaced dead node", "b", bi, "id", last.ID(), "ip", last.IP(), "r", r.ID(), "rip", r.IP())
 	} else {
 	} else {
-		log.Trace("Removed dead node", "b", bi, "id", last.ID, "ip", last.IP)
+		log.Debug("Removed dead node", "b", bi, "id", last.ID(), "ip", last.IP())
 	}
 	}
 }
 }
 
 
 // nodeToRevalidate returns the last node in a random, non-empty bucket.
 // nodeToRevalidate returns the last node in a random, non-empty bucket.
-func (tab *Table) nodeToRevalidate() (n *Node, bi int) {
+func (tab *Table) nodeToRevalidate() (n *node, bi int) {
 	tab.mutex.Lock()
 	tab.mutex.Lock()
 	defer tab.mutex.Unlock()
 	defer tab.mutex.Unlock()
 
 
@@ -511,7 +500,7 @@ func (tab *Table) copyLiveNodes() {
 	for _, b := range &tab.buckets {
 	for _, b := range &tab.buckets {
 		for _, n := range b.entries {
 		for _, n := range b.entries {
 			if now.Sub(n.addedAt) >= seedMinTableTime {
 			if now.Sub(n.addedAt) >= seedMinTableTime {
-				tab.db.updateNode(n)
+				tab.db.UpdateNode(unwrapNode(n))
 			}
 			}
 		}
 		}
 	}
 	}
@@ -519,7 +508,7 @@ func (tab *Table) copyLiveNodes() {
 
 
 // closest returns the n nodes in the table that are closest to the
 // closest returns the n nodes in the table that are closest to the
 // given id. The caller must hold tab.mutex.
 // given id. The caller must hold tab.mutex.
-func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance {
+func (tab *Table) closest(target enode.ID, nresults int) *nodesByDistance {
 	// This is a very wasteful way to find the closest nodes but
 	// This is a very wasteful way to find the closest nodes but
 	// obviously correct. I believe that tree-based buckets would make
 	// obviously correct. I believe that tree-based buckets would make
 	// this easier to implement efficiently.
 	// this easier to implement efficiently.
@@ -540,8 +529,8 @@ func (tab *Table) len() (n int) {
 }
 }
 
 
 // bucket returns the bucket for the given node ID hash.
 // bucket returns the bucket for the given node ID hash.
-func (tab *Table) bucket(sha common.Hash) *bucket {
-	d := logdist(tab.self.sha, sha)
+func (tab *Table) bucket(id enode.ID) *bucket {
+	d := enode.LogDist(tab.self.ID(), id)
 	if d <= bucketMinDistance {
 	if d <= bucketMinDistance {
 		return tab.buckets[0]
 		return tab.buckets[0]
 	}
 	}
@@ -553,11 +542,14 @@ func (tab *Table) bucket(sha common.Hash) *bucket {
 // least recently active node in the bucket does not respond to a ping packet.
 // least recently active node in the bucket does not respond to a ping packet.
 //
 //
 // The caller must not hold tab.mutex.
 // The caller must not hold tab.mutex.
-func (tab *Table) add(n *Node) {
+func (tab *Table) add(n *node) {
+	if n.ID() == tab.self.ID() {
+		return
+	}
+
 	tab.mutex.Lock()
 	tab.mutex.Lock()
 	defer tab.mutex.Unlock()
 	defer tab.mutex.Unlock()
-
-	b := tab.bucket(n.sha)
+	b := tab.bucket(n.ID())
 	if !tab.bumpOrAdd(b, n) {
 	if !tab.bumpOrAdd(b, n) {
 		// Node is not in table. Add it to the replacement list.
 		// Node is not in table. Add it to the replacement list.
 		tab.addReplacement(b, n)
 		tab.addReplacement(b, n)
@@ -570,7 +562,7 @@ func (tab *Table) add(n *Node) {
 // table could be filled by just sending ping repeatedly.
 // table could be filled by just sending ping repeatedly.
 //
 //
 // The caller must not hold tab.mutex.
 // The caller must not hold tab.mutex.
-func (tab *Table) addThroughPing(n *Node) {
+func (tab *Table) addThroughPing(n *node) {
 	if !tab.isInitDone() {
 	if !tab.isInitDone() {
 		return
 		return
 	}
 	}
@@ -579,15 +571,15 @@ func (tab *Table) addThroughPing(n *Node) {
 
 
 // stuff adds nodes the table to the end of their corresponding bucket
 // stuff adds nodes the table to the end of their corresponding bucket
 // if the bucket is not full. The caller must not hold tab.mutex.
 // if the bucket is not full. The caller must not hold tab.mutex.
-func (tab *Table) stuff(nodes []*Node) {
+func (tab *Table) stuff(nodes []*node) {
 	tab.mutex.Lock()
 	tab.mutex.Lock()
 	defer tab.mutex.Unlock()
 	defer tab.mutex.Unlock()
 
 
 	for _, n := range nodes {
 	for _, n := range nodes {
-		if n.ID == tab.self.ID {
+		if n.ID() == tab.self.ID() {
 			continue // don't add self
 			continue // don't add self
 		}
 		}
-		b := tab.bucket(n.sha)
+		b := tab.bucket(n.ID())
 		if len(b.entries) < bucketSize {
 		if len(b.entries) < bucketSize {
 			tab.bumpOrAdd(b, n)
 			tab.bumpOrAdd(b, n)
 		}
 		}
@@ -595,11 +587,11 @@ func (tab *Table) stuff(nodes []*Node) {
 }
 }
 
 
 // delete removes an entry from the node table. It is used to evacuate dead nodes.
 // delete removes an entry from the node table. It is used to evacuate dead nodes.
-func (tab *Table) delete(node *Node) {
+func (tab *Table) delete(node *node) {
 	tab.mutex.Lock()
 	tab.mutex.Lock()
 	defer tab.mutex.Unlock()
 	defer tab.mutex.Unlock()
 
 
-	tab.deleteInBucket(tab.bucket(node.sha), node)
+	tab.deleteInBucket(tab.bucket(node.ID()), node)
 }
 }
 
 
 func (tab *Table) addIP(b *bucket, ip net.IP) bool {
 func (tab *Table) addIP(b *bucket, ip net.IP) bool {
@@ -626,27 +618,27 @@ func (tab *Table) removeIP(b *bucket, ip net.IP) {
 	b.ips.Remove(ip)
 	b.ips.Remove(ip)
 }
 }
 
 
-func (tab *Table) addReplacement(b *bucket, n *Node) {
+func (tab *Table) addReplacement(b *bucket, n *node) {
 	for _, e := range b.replacements {
 	for _, e := range b.replacements {
-		if e.ID == n.ID {
+		if e.ID() == n.ID() {
 			return // already in list
 			return // already in list
 		}
 		}
 	}
 	}
-	if !tab.addIP(b, n.IP) {
+	if !tab.addIP(b, n.IP()) {
 		return
 		return
 	}
 	}
-	var removed *Node
+	var removed *node
 	b.replacements, removed = pushNode(b.replacements, n, maxReplacements)
 	b.replacements, removed = pushNode(b.replacements, n, maxReplacements)
 	if removed != nil {
 	if removed != nil {
-		tab.removeIP(b, removed.IP)
+		tab.removeIP(b, removed.IP())
 	}
 	}
 }
 }
 
 
 // replace removes n from the replacement list and replaces 'last' with it if it is the
 // replace removes n from the replacement list and replaces 'last' with it if it is the
 // last entry in the bucket. If 'last' isn't the last entry, it has either been replaced
 // last entry in the bucket. If 'last' isn't the last entry, it has either been replaced
 // with someone else or became active.
 // with someone else or became active.
-func (tab *Table) replace(b *bucket, last *Node) *Node {
-	if len(b.entries) == 0 || b.entries[len(b.entries)-1].ID != last.ID {
+func (tab *Table) replace(b *bucket, last *node) *node {
+	if len(b.entries) == 0 || b.entries[len(b.entries)-1].ID() != last.ID() {
 		// Entry has moved, don't replace it.
 		// Entry has moved, don't replace it.
 		return nil
 		return nil
 	}
 	}
@@ -658,15 +650,15 @@ func (tab *Table) replace(b *bucket, last *Node) *Node {
 	r := b.replacements[tab.rand.Intn(len(b.replacements))]
 	r := b.replacements[tab.rand.Intn(len(b.replacements))]
 	b.replacements = deleteNode(b.replacements, r)
 	b.replacements = deleteNode(b.replacements, r)
 	b.entries[len(b.entries)-1] = r
 	b.entries[len(b.entries)-1] = r
-	tab.removeIP(b, last.IP)
+	tab.removeIP(b, last.IP())
 	return r
 	return r
 }
 }
 
 
 // bump moves the given node to the front of the bucket entry list
 // bump moves the given node to the front of the bucket entry list
 // if it is contained in that list.
 // if it is contained in that list.
-func (b *bucket) bump(n *Node) bool {
+func (b *bucket) bump(n *node) bool {
 	for i := range b.entries {
 	for i := range b.entries {
-		if b.entries[i].ID == n.ID {
+		if b.entries[i].ID() == n.ID() {
 			// move it to the front
 			// move it to the front
 			copy(b.entries[1:], b.entries[:i])
 			copy(b.entries[1:], b.entries[:i])
 			b.entries[0] = n
 			b.entries[0] = n
@@ -678,11 +670,11 @@ func (b *bucket) bump(n *Node) bool {
 
 
 // bumpOrAdd moves n to the front of the bucket entry list or adds it if the list isn't
 // bumpOrAdd moves n to the front of the bucket entry list or adds it if the list isn't
 // full. The return value is true if n is in the bucket.
 // full. The return value is true if n is in the bucket.
-func (tab *Table) bumpOrAdd(b *bucket, n *Node) bool {
+func (tab *Table) bumpOrAdd(b *bucket, n *node) bool {
 	if b.bump(n) {
 	if b.bump(n) {
 		return true
 		return true
 	}
 	}
-	if len(b.entries) >= bucketSize || !tab.addIP(b, n.IP) {
+	if len(b.entries) >= bucketSize || !tab.addIP(b, n.IP()) {
 		return false
 		return false
 	}
 	}
 	b.entries, _ = pushNode(b.entries, n, bucketSize)
 	b.entries, _ = pushNode(b.entries, n, bucketSize)
@@ -694,13 +686,13 @@ func (tab *Table) bumpOrAdd(b *bucket, n *Node) bool {
 	return true
 	return true
 }
 }
 
 
-func (tab *Table) deleteInBucket(b *bucket, n *Node) {
+func (tab *Table) deleteInBucket(b *bucket, n *node) {
 	b.entries = deleteNode(b.entries, n)
 	b.entries = deleteNode(b.entries, n)
-	tab.removeIP(b, n.IP)
+	tab.removeIP(b, n.IP())
 }
 }
 
 
 // pushNode adds n to the front of list, keeping at most max items.
 // pushNode adds n to the front of list, keeping at most max items.
-func pushNode(list []*Node, n *Node, max int) ([]*Node, *Node) {
+func pushNode(list []*node, n *node, max int) ([]*node, *node) {
 	if len(list) < max {
 	if len(list) < max {
 		list = append(list, nil)
 		list = append(list, nil)
 	}
 	}
@@ -711,9 +703,9 @@ func pushNode(list []*Node, n *Node, max int) ([]*Node, *Node) {
 }
 }
 
 
 // deleteNode removes n from list.
 // deleteNode removes n from list.
-func deleteNode(list []*Node, n *Node) []*Node {
+func deleteNode(list []*node, n *node) []*node {
 	for i := range list {
 	for i := range list {
-		if list[i].ID == n.ID {
+		if list[i].ID() == n.ID() {
 			return append(list[:i], list[i+1:]...)
 			return append(list[:i], list[i+1:]...)
 		}
 		}
 	}
 	}
@@ -723,14 +715,14 @@ func deleteNode(list []*Node, n *Node) []*Node {
 // nodesByDistance is a list of nodes, ordered by
 // nodesByDistance is a list of nodes, ordered by
 // distance to target.
 // distance to target.
 type nodesByDistance struct {
 type nodesByDistance struct {
-	entries []*Node
-	target  common.Hash
+	entries []*node
+	target  enode.ID
 }
 }
 
 
 // push adds the given node to the list, keeping the total size below maxElems.
 // push adds the given node to the list, keeping the total size below maxElems.
-func (h *nodesByDistance) push(n *Node, maxElems int) {
+func (h *nodesByDistance) push(n *node, maxElems int) {
 	ix := sort.Search(len(h.entries), func(i int) bool {
 	ix := sort.Search(len(h.entries), func(i int) bool {
-		return distcmp(h.target, h.entries[i].sha, n.sha) > 0
+		return enode.DistCmp(h.target, h.entries[i].ID(), n.ID()) > 0
 	})
 	})
 	if len(h.entries) < maxElems {
 	if len(h.entries) < maxElems {
 		h.entries = append(h.entries, n)
 		h.entries = append(h.entries, n)

+ 244 - 316
p2p/discover/table_test.go

@@ -20,7 +20,6 @@ import (
 	"crypto/ecdsa"
 	"crypto/ecdsa"
 	"fmt"
 	"fmt"
 	"math/rand"
 	"math/rand"
-	"sync"
 
 
 	"net"
 	"net"
 	"reflect"
 	"reflect"
@@ -28,8 +27,9 @@ import (
 	"testing/quick"
 	"testing/quick"
 	"time"
 	"time"
 
 
-	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/ethereum/go-ethereum/p2p/enode"
+	"github.com/ethereum/go-ethereum/p2p/enr"
 )
 )
 
 
 func TestTable_pingReplace(t *testing.T) {
 func TestTable_pingReplace(t *testing.T) {
@@ -49,24 +49,26 @@ func TestTable_pingReplace(t *testing.T) {
 
 
 func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding bool) {
 func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding bool) {
 	transport := newPingRecorder()
 	transport := newPingRecorder()
-	tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}, "", nil)
+	tab, db := newTestTable(transport)
 	defer tab.Close()
 	defer tab.Close()
+	defer db.Close()
 
 
 	<-tab.initDone
 	<-tab.initDone
 
 
 	// Fill up the sender's bucket.
 	// Fill up the sender's bucket.
-	pingSender := NewNode(MustHexID("a502af0f59b2aab7746995408c79e9ca312d2793cc997e44fc55eda62f0150bbb8c59a6f9269ba3a081518b62699ee807c7c19c20125ddfccca872608af9e370"), net.IP{}, 99, 99)
+	pingKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
+	pingSender := wrapNode(enode.NewV4(&pingKey.PublicKey, net.IP{}, 99, 99))
 	last := fillBucket(tab, pingSender)
 	last := fillBucket(tab, pingSender)
 
 
 	// Add the sender as if it just pinged us. Revalidate should replace the last node in
 	// Add the sender as if it just pinged us. Revalidate should replace the last node in
 	// its bucket if it is unresponsive. Revalidate again to ensure that
 	// its bucket if it is unresponsive. Revalidate again to ensure that
-	transport.dead[last.ID] = !lastInBucketIsResponding
-	transport.dead[pingSender.ID] = !newNodeIsResponding
+	transport.dead[last.ID()] = !lastInBucketIsResponding
+	transport.dead[pingSender.ID()] = !newNodeIsResponding
 	tab.add(pingSender)
 	tab.add(pingSender)
 	tab.doRevalidate(make(chan struct{}, 1))
 	tab.doRevalidate(make(chan struct{}, 1))
 	tab.doRevalidate(make(chan struct{}, 1))
 	tab.doRevalidate(make(chan struct{}, 1))
 
 
-	if !transport.pinged[last.ID] {
+	if !transport.pinged[last.ID()] {
 		// Oldest node in bucket is pinged to see whether it is still alive.
 		// Oldest node in bucket is pinged to see whether it is still alive.
 		t.Error("table did not ping last node in bucket")
 		t.Error("table did not ping last node in bucket")
 	}
 	}
@@ -77,14 +79,14 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
 	if !lastInBucketIsResponding && !newNodeIsResponding {
 	if !lastInBucketIsResponding && !newNodeIsResponding {
 		wantSize--
 		wantSize--
 	}
 	}
-	if l := len(tab.bucket(pingSender.sha).entries); l != wantSize {
-		t.Errorf("wrong bucket size after add: got %d, want %d", l, wantSize)
+	if l := len(tab.bucket(pingSender.ID()).entries); l != wantSize {
+		t.Errorf("wrong bucket size after bond: got %d, want %d", l, wantSize)
 	}
 	}
-	if found := contains(tab.bucket(pingSender.sha).entries, last.ID); found != lastInBucketIsResponding {
+	if found := contains(tab.bucket(pingSender.ID()).entries, last.ID()); found != lastInBucketIsResponding {
 		t.Errorf("last entry found: %t, want: %t", found, lastInBucketIsResponding)
 		t.Errorf("last entry found: %t, want: %t", found, lastInBucketIsResponding)
 	}
 	}
 	wantNewEntry := newNodeIsResponding && !lastInBucketIsResponding
 	wantNewEntry := newNodeIsResponding && !lastInBucketIsResponding
-	if found := contains(tab.bucket(pingSender.sha).entries, pingSender.ID); found != wantNewEntry {
+	if found := contains(tab.bucket(pingSender.ID()).entries, pingSender.ID()); found != wantNewEntry {
 		t.Errorf("new entry found: %t, want: %t", found, wantNewEntry)
 		t.Errorf("new entry found: %t, want: %t", found, wantNewEntry)
 	}
 	}
 }
 }
@@ -97,9 +99,9 @@ func TestBucket_bumpNoDuplicates(t *testing.T) {
 		Values: func(args []reflect.Value, rand *rand.Rand) {
 		Values: func(args []reflect.Value, rand *rand.Rand) {
 			// generate a random list of nodes. this will be the content of the bucket.
 			// generate a random list of nodes. this will be the content of the bucket.
 			n := rand.Intn(bucketSize-1) + 1
 			n := rand.Intn(bucketSize-1) + 1
-			nodes := make([]*Node, n)
+			nodes := make([]*node, n)
 			for i := range nodes {
 			for i := range nodes {
-				nodes[i] = nodeAtDistance(common.Hash{}, 200)
+				nodes[i] = nodeAtDistance(enode.ID{}, 200, intIP(200))
 			}
 			}
 			args[0] = reflect.ValueOf(nodes)
 			args[0] = reflect.ValueOf(nodes)
 			// generate random bump positions.
 			// generate random bump positions.
@@ -111,8 +113,8 @@ func TestBucket_bumpNoDuplicates(t *testing.T) {
 		},
 		},
 	}
 	}
 
 
-	prop := func(nodes []*Node, bumps []int) (ok bool) {
-		b := &bucket{entries: make([]*Node, len(nodes))}
+	prop := func(nodes []*node, bumps []int) (ok bool) {
+		b := &bucket{entries: make([]*node, len(nodes))}
 		copy(b.entries, nodes)
 		copy(b.entries, nodes)
 		for i, pos := range bumps {
 		for i, pos := range bumps {
 			b.bump(b.entries[pos])
 			b.bump(b.entries[pos])
@@ -134,12 +136,12 @@ func TestBucket_bumpNoDuplicates(t *testing.T) {
 // This checks that the table-wide IP limit is applied correctly.
 // This checks that the table-wide IP limit is applied correctly.
 func TestTable_IPLimit(t *testing.T) {
 func TestTable_IPLimit(t *testing.T) {
 	transport := newPingRecorder()
 	transport := newPingRecorder()
-	tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}, "", nil)
+	tab, db := newTestTable(transport)
 	defer tab.Close()
 	defer tab.Close()
+	defer db.Close()
 
 
 	for i := 0; i < tableIPLimit+1; i++ {
 	for i := 0; i < tableIPLimit+1; i++ {
-		n := nodeAtDistance(tab.self.sha, i)
-		n.IP = net.IP{172, 0, 1, byte(i)}
+		n := nodeAtDistance(tab.self.ID(), i, net.IP{172, 0, 1, byte(i)})
 		tab.add(n)
 		tab.add(n)
 	}
 	}
 	if tab.len() > tableIPLimit {
 	if tab.len() > tableIPLimit {
@@ -147,16 +149,16 @@ func TestTable_IPLimit(t *testing.T) {
 	}
 	}
 }
 }
 
 
-// This checks that the table-wide IP limit is applied correctly.
+// This checks that the per-bucket IP limit is applied correctly.
 func TestTable_BucketIPLimit(t *testing.T) {
 func TestTable_BucketIPLimit(t *testing.T) {
 	transport := newPingRecorder()
 	transport := newPingRecorder()
-	tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}, "", nil)
+	tab, db := newTestTable(transport)
 	defer tab.Close()
 	defer tab.Close()
+	defer db.Close()
 
 
 	d := 3
 	d := 3
 	for i := 0; i < bucketIPLimit+1; i++ {
 	for i := 0; i < bucketIPLimit+1; i++ {
-		n := nodeAtDistance(tab.self.sha, d)
-		n.IP = net.IP{172, 0, 1, byte(i)}
+		n := nodeAtDistance(tab.self.ID(), d, net.IP{172, 0, 1, byte(i)})
 		tab.add(n)
 		tab.add(n)
 	}
 	}
 	if tab.len() > bucketIPLimit {
 	if tab.len() > bucketIPLimit {
@@ -164,69 +166,18 @@ func TestTable_BucketIPLimit(t *testing.T) {
 	}
 	}
 }
 }
 
 
-// fillBucket inserts nodes into the given bucket until
-// it is full. The node's IDs dont correspond to their
-// hashes.
-func fillBucket(tab *Table, n *Node) (last *Node) {
-	ld := logdist(tab.self.sha, n.sha)
-	b := tab.bucket(n.sha)
-	for len(b.entries) < bucketSize {
-		b.entries = append(b.entries, nodeAtDistance(tab.self.sha, ld))
-	}
-	return b.entries[bucketSize-1]
-}
-
-// nodeAtDistance creates a node for which logdist(base, n.sha) == ld.
-// The node's ID does not correspond to n.sha.
-func nodeAtDistance(base common.Hash, ld int) (n *Node) {
-	n = new(Node)
-	n.sha = hashAtDistance(base, ld)
-	n.IP = net.IP{byte(ld), 0, 2, byte(ld)}
-	copy(n.ID[:], n.sha[:]) // ensure the node still has a unique ID
-	return n
-}
-
-type pingRecorder struct {
-	mu           sync.Mutex
-	dead, pinged map[NodeID]bool
-}
-
-func newPingRecorder() *pingRecorder {
-	return &pingRecorder{
-		dead:   make(map[NodeID]bool),
-		pinged: make(map[NodeID]bool),
-	}
-}
-
-func (t *pingRecorder) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
-	return nil, nil
-}
-
-func (t *pingRecorder) ping(toid NodeID, toaddr *net.UDPAddr) error {
-	t.mu.Lock()
-	defer t.mu.Unlock()
-
-	t.pinged[toid] = true
-	if t.dead[toid] {
-		return errTimeout
-	} else {
-		return nil
-	}
-}
-
-func (t *pingRecorder) close() {}
-
 func TestTable_closest(t *testing.T) {
 func TestTable_closest(t *testing.T) {
 	t.Parallel()
 	t.Parallel()
 
 
 	test := func(test *closeTest) bool {
 	test := func(test *closeTest) bool {
 		// for any node table, Target and N
 		// for any node table, Target and N
 		transport := newPingRecorder()
 		transport := newPingRecorder()
-		tab, _ := newTable(transport, test.Self, &net.UDPAddr{}, "", nil)
+		tab, db := newTestTable(transport)
 		defer tab.Close()
 		defer tab.Close()
+		defer db.Close()
 		tab.stuff(test.All)
 		tab.stuff(test.All)
 
 
-		// check that doClosest(Target, N) returns nodes
+		// check that closest(Target, N) returns nodes
 		result := tab.closest(test.Target, test.N).entries
 		result := tab.closest(test.Target, test.N).entries
 		if hasDuplicates(result) {
 		if hasDuplicates(result) {
 			t.Errorf("result contains duplicates")
 			t.Errorf("result contains duplicates")
@@ -252,15 +203,15 @@ func TestTable_closest(t *testing.T) {
 		// check that the result nodes have minimum distance to target.
 		// check that the result nodes have minimum distance to target.
 		for _, b := range tab.buckets {
 		for _, b := range tab.buckets {
 			for _, n := range b.entries {
 			for _, n := range b.entries {
-				if contains(result, n.ID) {
+				if contains(result, n.ID()) {
 					continue // don't run the check below for nodes in result
 					continue // don't run the check below for nodes in result
 				}
 				}
-				farthestResult := result[len(result)-1].sha
-				if distcmp(test.Target, n.sha, farthestResult) < 0 {
+				farthestResult := result[len(result)-1].ID()
+				if enode.DistCmp(test.Target, n.ID(), farthestResult) < 0 {
 					t.Errorf("table contains node that is closer to target but it's not in result")
 					t.Errorf("table contains node that is closer to target but it's not in result")
 					t.Logf("  Target:          %v", test.Target)
 					t.Logf("  Target:          %v", test.Target)
 					t.Logf("  Farthest Result: %v", farthestResult)
 					t.Logf("  Farthest Result: %v", farthestResult)
-					t.Logf("  ID:              %v", n.ID)
+					t.Logf("  ID:              %v", n.ID())
 					return false
 					return false
 				}
 				}
 			}
 			}
@@ -277,25 +228,26 @@ func TestTable_ReadRandomNodesGetAll(t *testing.T) {
 		MaxCount: 200,
 		MaxCount: 200,
 		Rand:     rand.New(rand.NewSource(time.Now().Unix())),
 		Rand:     rand.New(rand.NewSource(time.Now().Unix())),
 		Values: func(args []reflect.Value, rand *rand.Rand) {
 		Values: func(args []reflect.Value, rand *rand.Rand) {
-			args[0] = reflect.ValueOf(make([]*Node, rand.Intn(1000)))
+			args[0] = reflect.ValueOf(make([]*enode.Node, rand.Intn(1000)))
 		},
 		},
 	}
 	}
-	test := func(buf []*Node) bool {
+	test := func(buf []*enode.Node) bool {
 		transport := newPingRecorder()
 		transport := newPingRecorder()
-		tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}, "", nil)
+		tab, db := newTestTable(transport)
 		defer tab.Close()
 		defer tab.Close()
+		defer db.Close()
 		<-tab.initDone
 		<-tab.initDone
 
 
 		for i := 0; i < len(buf); i++ {
 		for i := 0; i < len(buf); i++ {
 			ld := cfg.Rand.Intn(len(tab.buckets))
 			ld := cfg.Rand.Intn(len(tab.buckets))
-			tab.stuff([]*Node{nodeAtDistance(tab.self.sha, ld)})
+			tab.stuff([]*node{nodeAtDistance(tab.self.ID(), ld, intIP(ld))})
 		}
 		}
 		gotN := tab.ReadRandomNodes(buf)
 		gotN := tab.ReadRandomNodes(buf)
 		if gotN != tab.len() {
 		if gotN != tab.len() {
 			t.Errorf("wrong number of nodes, got %d, want %d", gotN, tab.len())
 			t.Errorf("wrong number of nodes, got %d, want %d", gotN, tab.len())
 			return false
 			return false
 		}
 		}
-		if hasDuplicates(buf[:gotN]) {
+		if hasDuplicates(wrapNodes(buf[:gotN])) {
 			t.Errorf("result contains duplicates")
 			t.Errorf("result contains duplicates")
 			return false
 			return false
 		}
 		}
@@ -307,41 +259,43 @@ func TestTable_ReadRandomNodesGetAll(t *testing.T) {
 }
 }
 
 
 type closeTest struct {
 type closeTest struct {
-	Self   NodeID
-	Target common.Hash
-	All    []*Node
+	Self   enode.ID
+	Target enode.ID
+	All    []*node
 	N      int
 	N      int
 }
 }
 
 
 func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
 func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
 	t := &closeTest{
 	t := &closeTest{
-		Self:   gen(NodeID{}, rand).(NodeID),
-		Target: gen(common.Hash{}, rand).(common.Hash),
+		Self:   gen(enode.ID{}, rand).(enode.ID),
+		Target: gen(enode.ID{}, rand).(enode.ID),
 		N:      rand.Intn(bucketSize),
 		N:      rand.Intn(bucketSize),
 	}
 	}
-	for _, id := range gen([]NodeID{}, rand).([]NodeID) {
-		t.All = append(t.All, &Node{ID: id})
+	for _, id := range gen([]enode.ID{}, rand).([]enode.ID) {
+		n := enode.SignNull(new(enr.Record), id)
+		t.All = append(t.All, wrapNode(n))
 	}
 	}
 	return reflect.ValueOf(t)
 	return reflect.ValueOf(t)
 }
 }
 
 
 func TestTable_Lookup(t *testing.T) {
 func TestTable_Lookup(t *testing.T) {
-	self := nodeAtDistance(common.Hash{}, 0)
-	tab, _ := newTable(lookupTestnet, self.ID, &net.UDPAddr{}, "", nil)
+	tab, db := newTestTable(lookupTestnet)
 	defer tab.Close()
 	defer tab.Close()
+	defer db.Close()
 
 
 	// lookup on empty table returns no nodes
 	// lookup on empty table returns no nodes
-	if results := tab.Lookup(lookupTestnet.target); len(results) > 0 {
+	if results := tab.lookup(lookupTestnet.target, false); len(results) > 0 {
 		t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results)
 		t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results)
 	}
 	}
 	// seed table with initial node (otherwise lookup will terminate immediately)
 	// seed table with initial node (otherwise lookup will terminate immediately)
-	seed := NewNode(lookupTestnet.dists[256][0], net.IP{}, 256, 0)
-	tab.stuff([]*Node{seed})
+	seedKey, _ := decodePubkey(lookupTestnet.dists[256][0])
+	seed := wrapNode(enode.NewV4(seedKey, net.IP{}, 0, 256))
+	tab.stuff([]*node{seed})
 
 
-	results := tab.Lookup(lookupTestnet.target)
+	results := tab.lookup(lookupTestnet.target, true)
 	t.Logf("results:")
 	t.Logf("results:")
 	for _, e := range results {
 	for _, e := range results {
-		t.Logf("  ld=%d, %x", logdist(lookupTestnet.targetSha, e.sha), e.sha[:])
+		t.Logf("  ld=%d, %x", enode.LogDist(lookupTestnet.targetSha, e.ID()), e.ID().Bytes())
 	}
 	}
 	if len(results) != bucketSize {
 	if len(results) != bucketSize {
 		t.Errorf("wrong number of results: got %d, want %d", len(results), bucketSize)
 		t.Errorf("wrong number of results: got %d, want %d", len(results), bucketSize)
@@ -358,235 +312,236 @@ func TestTable_Lookup(t *testing.T) {
 // This is the test network for the Lookup test.
 // This is the test network for the Lookup test.
 // The nodes were obtained by running testnet.mine with a random NodeID as target.
 // The nodes were obtained by running testnet.mine with a random NodeID as target.
 var lookupTestnet = &preminedTestnet{
 var lookupTestnet = &preminedTestnet{
-	target:    MustHexID("166aea4f556532c6d34e8b740e5d314af7e9ac0ca79833bd751d6b665f12dfd38ec563c363b32f02aef4a80b44fd3def94612d497b99cb5f17fd24de454927ec"),
-	targetSha: common.Hash{0x5c, 0x94, 0x4e, 0xe5, 0x1c, 0x5a, 0xe9, 0xf7, 0x2a, 0x95, 0xec, 0xcb, 0x8a, 0xed, 0x3, 0x74, 0xee, 0xcb, 0x51, 0x19, 0xd7, 0x20, 0xcb, 0xea, 0x68, 0x13, 0xe8, 0xe0, 0xd6, 0xad, 0x92, 0x61},
-	dists: [257][]NodeID{
+	target:    hexEncPubkey("166aea4f556532c6d34e8b740e5d314af7e9ac0ca79833bd751d6b665f12dfd38ec563c363b32f02aef4a80b44fd3def94612d497b99cb5f17fd24de454927ec"),
+	targetSha: enode.HexID("5c944ee51c5ae9f72a95eccb8aed0374eecb5119d720cbea6813e8e0d6ad9261"),
+	dists: [257][]encPubkey{
 		240: {
 		240: {
-			MustHexID("2001ad5e3e80c71b952161bc0186731cf5ffe942d24a79230a0555802296238e57ea7a32f5b6f18564eadc1c65389448481f8c9338df0a3dbd18f708cbc2cbcb"),
-			MustHexID("6ba3f4f57d084b6bf94cc4555b8c657e4a8ac7b7baf23c6874efc21dd1e4f56b7eb2721e07f5242d2f1d8381fc8cae535e860197c69236798ba1ad231b105794"),
+			hexEncPubkey("2001ad5e3e80c71b952161bc0186731cf5ffe942d24a79230a0555802296238e57ea7a32f5b6f18564eadc1c65389448481f8c9338df0a3dbd18f708cbc2cbcb"),
+			hexEncPubkey("6ba3f4f57d084b6bf94cc4555b8c657e4a8ac7b7baf23c6874efc21dd1e4f56b7eb2721e07f5242d2f1d8381fc8cae535e860197c69236798ba1ad231b105794"),
 		},
 		},
 		244: {
 		244: {
-			MustHexID("696ba1f0a9d55c59246f776600542a9e6432490f0cd78f8bb55a196918df2081a9b521c3c3ba48e465a75c10768807717f8f689b0b4adce00e1c75737552a178"),
+			hexEncPubkey("696ba1f0a9d55c59246f776600542a9e6432490f0cd78f8bb55a196918df2081a9b521c3c3ba48e465a75c10768807717f8f689b0b4adce00e1c75737552a178"),
 		},
 		},
 		246: {
 		246: {
-			MustHexID("d6d32178bdc38416f46ffb8b3ec9e4cb2cfff8d04dd7e4311a70e403cb62b10be1b447311b60b4f9ee221a8131fc2cbd45b96dd80deba68a949d467241facfa8"),
-			MustHexID("3ea3d04a43a3dfb5ac11cffc2319248cf41b6279659393c2f55b8a0a5fc9d12581a9d97ef5d8ff9b5abf3321a290e8f63a4f785f450dc8a672aba3ba2ff4fdab"),
-			MustHexID("2fc897f05ae585553e5c014effd3078f84f37f9333afacffb109f00ca8e7a3373de810a3946be971cbccdfd40249f9fe7f322118ea459ac71acca85a1ef8b7f4"),
+			hexEncPubkey("d6d32178bdc38416f46ffb8b3ec9e4cb2cfff8d04dd7e4311a70e403cb62b10be1b447311b60b4f9ee221a8131fc2cbd45b96dd80deba68a949d467241facfa8"),
+			hexEncPubkey("3ea3d04a43a3dfb5ac11cffc2319248cf41b6279659393c2f55b8a0a5fc9d12581a9d97ef5d8ff9b5abf3321a290e8f63a4f785f450dc8a672aba3ba2ff4fdab"),
+			hexEncPubkey("2fc897f05ae585553e5c014effd3078f84f37f9333afacffb109f00ca8e7a3373de810a3946be971cbccdfd40249f9fe7f322118ea459ac71acca85a1ef8b7f4"),
 		},
 		},
 		247: {
 		247: {
-			MustHexID("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"),
-			MustHexID("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"),
-			MustHexID("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"),
-			MustHexID("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"),
-			MustHexID("8b58c6073dd98bbad4e310b97186c8f822d3a5c7d57af40e2136e88e315afd115edb27d2d0685a908cfe5aa49d0debdda6e6e63972691d6bd8c5af2d771dd2a9"),
-			MustHexID("2cbb718b7dc682da19652e7d9eb4fefaf7b7147d82c1c2b6805edf77b85e29fde9f6da195741467ff2638dc62c8d3e014ea5686693c15ed0080b6de90354c137"),
-			MustHexID("e84027696d3f12f2de30a9311afea8fbd313c2360daff52bb5fc8c7094d5295758bec3134e4eef24e4cdf377b40da344993284628a7a346eba94f74160998feb"),
-			MustHexID("f1357a4f04f9d33753a57c0b65ba20a5d8777abbffd04e906014491c9103fb08590e45548d37aa4bd70965e2e81ddba94f31860348df01469eec8c1829200a68"),
-			MustHexID("4ab0a75941b12892369b4490a1928c8ca52a9ad6d3dffbd1d8c0b907bc200fe74c022d011ec39b64808a39c0ca41f1d3254386c3e7733e7044c44259486461b6"),
-			MustHexID("d45150a72dc74388773e68e03133a3b5f51447fe91837d566706b3c035ee4b56f160c878c6273394daee7f56cc398985269052f22f75a8057df2fe6172765354"),
+			hexEncPubkey("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"),
+			hexEncPubkey("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"),
+			hexEncPubkey("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"),
+			hexEncPubkey("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"),
+			hexEncPubkey("8b58c6073dd98bbad4e310b97186c8f822d3a5c7d57af40e2136e88e315afd115edb27d2d0685a908cfe5aa49d0debdda6e6e63972691d6bd8c5af2d771dd2a9"),
+			hexEncPubkey("2cbb718b7dc682da19652e7d9eb4fefaf7b7147d82c1c2b6805edf77b85e29fde9f6da195741467ff2638dc62c8d3e014ea5686693c15ed0080b6de90354c137"),
+			hexEncPubkey("e84027696d3f12f2de30a9311afea8fbd313c2360daff52bb5fc8c7094d5295758bec3134e4eef24e4cdf377b40da344993284628a7a346eba94f74160998feb"),
+			hexEncPubkey("f1357a4f04f9d33753a57c0b65ba20a5d8777abbffd04e906014491c9103fb08590e45548d37aa4bd70965e2e81ddba94f31860348df01469eec8c1829200a68"),
+			hexEncPubkey("4ab0a75941b12892369b4490a1928c8ca52a9ad6d3dffbd1d8c0b907bc200fe74c022d011ec39b64808a39c0ca41f1d3254386c3e7733e7044c44259486461b6"),
+			hexEncPubkey("d45150a72dc74388773e68e03133a3b5f51447fe91837d566706b3c035ee4b56f160c878c6273394daee7f56cc398985269052f22f75a8057df2fe6172765354"),
 		},
 		},
 		248: {
 		248: {
-			MustHexID("6aadfce366a189bab08ac84721567483202c86590642ea6d6a14f37ca78d82bdb6509eb7b8b2f6f63c78ae3ae1d8837c89509e41497d719b23ad53dd81574afa"),
-			MustHexID("a605ecfd6069a4cf4cf7f5840e5bc0ce10d23a3ac59e2aaa70c6afd5637359d2519b4524f56fc2ca180cdbebe54262f720ccaae8c1b28fd553c485675831624d"),
-			MustHexID("29701451cb9448ca33fc33680b44b840d815be90146eb521641efbffed0859c154e8892d3906eae9934bfacee72cd1d2fa9dd050fd18888eea49da155ab0efd2"),
-			MustHexID("3ed426322dee7572b08592e1e079f8b6c6b30e10e6243edd144a6a48fdbdb83df73a6e41b1143722cb82604f2203a32758610b5d9544f44a1a7921ba001528c1"),
-			MustHexID("b2e2a2b7fdd363572a3256e75435fab1da3b16f7891a8bd2015f30995dae665d7eabfd194d87d99d5df628b4bbc7b04e5b492c596422dd8272746c7a1b0b8e4f"),
-			MustHexID("0c69c9756162c593e85615b814ce57a2a8ca2df6c690b9c4e4602731b61e1531a3bbe3f7114271554427ffabea80ad8f36fa95a49fa77b675ae182c6ccac1728"),
-			MustHexID("8d28be21d5a97b0876442fa4f5e5387f5bf3faad0b6f13b8607b64d6e448c0991ca28dd7fe2f64eb8eadd7150bff5d5666aa6ed868b84c71311f4ba9a38569dd"),
-			MustHexID("2c677e1c64b9c9df6359348a7f5f33dc79e22f0177042486d125f8b6ca7f0dc756b1f672aceee5f1746bcff80aaf6f92a8dc0c9fbeb259b3fa0da060de5ab7e8"),
-			MustHexID("3994880f94a8678f0cd247a43f474a8af375d2a072128da1ad6cae84a244105ff85e94fc7d8496f639468de7ee998908a91c7e33ef7585fff92e984b210941a1"),
-			MustHexID("b45a9153c08d002a48090d15d61a7c7dad8c2af85d4ff5bd36ce23a9a11e0709bf8d56614c7b193bc028c16cbf7f20dfbcc751328b64a924995d47b41e452422"),
-			MustHexID("057ab3a9e53c7a84b0f3fc586117a525cdd18e313f52a67bf31798d48078e325abe5cfee3f6c2533230cb37d0549289d692a29dd400e899b8552d4b928f6f907"),
-			MustHexID("0ddf663d308791eb92e6bd88a2f8cb45e4f4f35bb16708a0e6ff7f1362aa6a73fedd0a1b1557fb3365e38e1b79d6918e2fae2788728b70c9ab6b51a3b94a4338"),
-			MustHexID("f637e07ff50cc1e3731735841c4798411059f2023abcf3885674f3e8032531b0edca50fd715df6feb489b6177c345374d64f4b07d257a7745de393a107b013a5"),
-			MustHexID("e24ec7c6eec094f63c7b3239f56d311ec5a3e45bc4e622a1095a65b95eea6fe13e29f3b6b7a2cbfe40906e3989f17ac834c3102dd0cadaaa26e16ee06d782b72"),
-			MustHexID("b76ea1a6fd6506ef6e3506a4f1f60ed6287fff8114af6141b2ff13e61242331b54082b023cfea5b3083354a4fb3f9eb8be01fb4a518f579e731a5d0707291a6b"),
-			MustHexID("9b53a37950ca8890ee349b325032d7b672cab7eced178d3060137b24ef6b92a43977922d5bdfb4a3409a2d80128e02f795f9dae6d7d99973ad0e23a2afb8442f"),
+			hexEncPubkey("6aadfce366a189bab08ac84721567483202c86590642ea6d6a14f37ca78d82bdb6509eb7b8b2f6f63c78ae3ae1d8837c89509e41497d719b23ad53dd81574afa"),
+			hexEncPubkey("a605ecfd6069a4cf4cf7f5840e5bc0ce10d23a3ac59e2aaa70c6afd5637359d2519b4524f56fc2ca180cdbebe54262f720ccaae8c1b28fd553c485675831624d"),
+			hexEncPubkey("29701451cb9448ca33fc33680b44b840d815be90146eb521641efbffed0859c154e8892d3906eae9934bfacee72cd1d2fa9dd050fd18888eea49da155ab0efd2"),
+			hexEncPubkey("3ed426322dee7572b08592e1e079f8b6c6b30e10e6243edd144a6a48fdbdb83df73a6e41b1143722cb82604f2203a32758610b5d9544f44a1a7921ba001528c1"),
+			hexEncPubkey("b2e2a2b7fdd363572a3256e75435fab1da3b16f7891a8bd2015f30995dae665d7eabfd194d87d99d5df628b4bbc7b04e5b492c596422dd8272746c7a1b0b8e4f"),
+			hexEncPubkey("0c69c9756162c593e85615b814ce57a2a8ca2df6c690b9c4e4602731b61e1531a3bbe3f7114271554427ffabea80ad8f36fa95a49fa77b675ae182c6ccac1728"),
+			hexEncPubkey("8d28be21d5a97b0876442fa4f5e5387f5bf3faad0b6f13b8607b64d6e448c0991ca28dd7fe2f64eb8eadd7150bff5d5666aa6ed868b84c71311f4ba9a38569dd"),
+			hexEncPubkey("2c677e1c64b9c9df6359348a7f5f33dc79e22f0177042486d125f8b6ca7f0dc756b1f672aceee5f1746bcff80aaf6f92a8dc0c9fbeb259b3fa0da060de5ab7e8"),
+			hexEncPubkey("3994880f94a8678f0cd247a43f474a8af375d2a072128da1ad6cae84a244105ff85e94fc7d8496f639468de7ee998908a91c7e33ef7585fff92e984b210941a1"),
+			hexEncPubkey("b45a9153c08d002a48090d15d61a7c7dad8c2af85d4ff5bd36ce23a9a11e0709bf8d56614c7b193bc028c16cbf7f20dfbcc751328b64a924995d47b41e452422"),
+			hexEncPubkey("057ab3a9e53c7a84b0f3fc586117a525cdd18e313f52a67bf31798d48078e325abe5cfee3f6c2533230cb37d0549289d692a29dd400e899b8552d4b928f6f907"),
+			hexEncPubkey("0ddf663d308791eb92e6bd88a2f8cb45e4f4f35bb16708a0e6ff7f1362aa6a73fedd0a1b1557fb3365e38e1b79d6918e2fae2788728b70c9ab6b51a3b94a4338"),
+			hexEncPubkey("f637e07ff50cc1e3731735841c4798411059f2023abcf3885674f3e8032531b0edca50fd715df6feb489b6177c345374d64f4b07d257a7745de393a107b013a5"),
+			hexEncPubkey("e24ec7c6eec094f63c7b3239f56d311ec5a3e45bc4e622a1095a65b95eea6fe13e29f3b6b7a2cbfe40906e3989f17ac834c3102dd0cadaaa26e16ee06d782b72"),
+			hexEncPubkey("b76ea1a6fd6506ef6e3506a4f1f60ed6287fff8114af6141b2ff13e61242331b54082b023cfea5b3083354a4fb3f9eb8be01fb4a518f579e731a5d0707291a6b"),
+			hexEncPubkey("9b53a37950ca8890ee349b325032d7b672cab7eced178d3060137b24ef6b92a43977922d5bdfb4a3409a2d80128e02f795f9dae6d7d99973ad0e23a2afb8442f"),
 		},
 		},
 		249: {
 		249: {
-			MustHexID("675ae65567c3c72c50c73bc0fd4f61f202ea5f93346ca57b551de3411ccc614fad61cb9035493af47615311b9d44ee7a161972ee4d77c28fe1ec029d01434e6a"),
-			MustHexID("8eb81408389da88536ae5800392b16ef5109d7ea132c18e9a82928047ecdb502693f6e4a4cdd18b54296caf561db937185731456c456c98bfe7de0baf0eaa495"),
-			MustHexID("2adba8b1612a541771cb93a726a38a4b88e97b18eced2593eb7daf82f05a5321ca94a72cc780c306ff21e551a932fc2c6d791e4681907b5ceab7f084c3fa2944"),
-			MustHexID("b1b4bfbda514d9b8f35b1c28961da5d5216fe50548f4066f69af3b7666a3b2e06eac646735e963e5c8f8138a2fb95af15b13b23ff00c6986eccc0efaa8ee6fb4"),
-			MustHexID("d2139281b289ad0e4d7b4243c4364f5c51aac8b60f4806135de06b12b5b369c9e43a6eb494eab860d115c15c6fbb8c5a1b0e382972e0e460af395b8385363de7"),
-			MustHexID("4a693df4b8fc5bdc7cec342c3ed2e228d7c5b4ab7321ddaa6cccbeb45b05a9f1d95766b4002e6d4791c2deacb8a667aadea6a700da28a3eea810a30395701bbc"),
-			MustHexID("ab41611195ec3c62bb8cd762ee19fb182d194fd141f4a66780efbef4b07ce916246c022b841237a3a6b512a93431157edd221e854ed2a259b72e9c5351f44d0c"),
-			MustHexID("68e8e26099030d10c3c703ae7045c0a48061fb88058d853b3e67880014c449d4311014da99d617d3150a20f1a3da5e34bf0f14f1c51fe4dd9d58afd222823176"),
-			MustHexID("3fbcacf546fb129cd70fc48de3b593ba99d3c473798bc309292aca280320e0eacc04442c914cad5c4cf6950345ba79b0d51302df88285d4e83ee3fe41339eee7"),
-			MustHexID("1d4a623659f7c8f80b6c3939596afdf42e78f892f682c768ad36eb7bfba402dbf97aea3a268f3badd8fe7636be216edf3d67ee1e08789ebbc7be625056bd7109"),
-			MustHexID("a283c474ab09da02bbc96b16317241d0627646fcc427d1fe790b76a7bf1989ced90f92101a973047ae9940c92720dffbac8eff21df8cae468a50f72f9e159417"),
-			MustHexID("dbf7e5ad7f87c3dfecae65d87c3039e14ed0bdc56caf00ce81931073e2e16719d746295512ff7937a15c3b03603e7c41a4f9df94fcd37bb200dd8f332767e9cb"),
-			MustHexID("caaa070a26692f64fc77f30d7b5ae980d419b4393a0f442b1c821ef58c0862898b0d22f74a4f8c5d83069493e3ec0b92f17dc1fe6e4cd437c1ec25039e7ce839"),
-			MustHexID("874cc8d1213beb65c4e0e1de38ef5d8165235893ac74ab5ea937c885eaab25c8d79dad0456e9fd3e9450626cac7e107b004478fb59842f067857f39a47cee695"),
-			MustHexID("d94193f236105010972f5df1b7818b55846592a0445b9cdc4eaed811b8c4c0f7c27dc8cc9837a4774656d6b34682d6d329d42b6ebb55da1d475c2474dc3dfdf4"),
-			MustHexID("edd9af6aded4094e9785637c28fccbd3980cbe28e2eb9a411048a23c2ace4bd6b0b7088a7817997b49a3dd05fc6929ca6c7abbb69438dbdabe65e971d2a794b2"),
+			hexEncPubkey("675ae65567c3c72c50c73bc0fd4f61f202ea5f93346ca57b551de3411ccc614fad61cb9035493af47615311b9d44ee7a161972ee4d77c28fe1ec029d01434e6a"),
+			hexEncPubkey("8eb81408389da88536ae5800392b16ef5109d7ea132c18e9a82928047ecdb502693f6e4a4cdd18b54296caf561db937185731456c456c98bfe7de0baf0eaa495"),
+			hexEncPubkey("2adba8b1612a541771cb93a726a38a4b88e97b18eced2593eb7daf82f05a5321ca94a72cc780c306ff21e551a932fc2c6d791e4681907b5ceab7f084c3fa2944"),
+			hexEncPubkey("b1b4bfbda514d9b8f35b1c28961da5d5216fe50548f4066f69af3b7666a3b2e06eac646735e963e5c8f8138a2fb95af15b13b23ff00c6986eccc0efaa8ee6fb4"),
+			hexEncPubkey("d2139281b289ad0e4d7b4243c4364f5c51aac8b60f4806135de06b12b5b369c9e43a6eb494eab860d115c15c6fbb8c5a1b0e382972e0e460af395b8385363de7"),
+			hexEncPubkey("4a693df4b8fc5bdc7cec342c3ed2e228d7c5b4ab7321ddaa6cccbeb45b05a9f1d95766b4002e6d4791c2deacb8a667aadea6a700da28a3eea810a30395701bbc"),
+			hexEncPubkey("ab41611195ec3c62bb8cd762ee19fb182d194fd141f4a66780efbef4b07ce916246c022b841237a3a6b512a93431157edd221e854ed2a259b72e9c5351f44d0c"),
+			hexEncPubkey("68e8e26099030d10c3c703ae7045c0a48061fb88058d853b3e67880014c449d4311014da99d617d3150a20f1a3da5e34bf0f14f1c51fe4dd9d58afd222823176"),
+			hexEncPubkey("3fbcacf546fb129cd70fc48de3b593ba99d3c473798bc309292aca280320e0eacc04442c914cad5c4cf6950345ba79b0d51302df88285d4e83ee3fe41339eee7"),
+			hexEncPubkey("1d4a623659f7c8f80b6c3939596afdf42e78f892f682c768ad36eb7bfba402dbf97aea3a268f3badd8fe7636be216edf3d67ee1e08789ebbc7be625056bd7109"),
+			hexEncPubkey("a283c474ab09da02bbc96b16317241d0627646fcc427d1fe790b76a7bf1989ced90f92101a973047ae9940c92720dffbac8eff21df8cae468a50f72f9e159417"),
+			hexEncPubkey("dbf7e5ad7f87c3dfecae65d87c3039e14ed0bdc56caf00ce81931073e2e16719d746295512ff7937a15c3b03603e7c41a4f9df94fcd37bb200dd8f332767e9cb"),
+			hexEncPubkey("caaa070a26692f64fc77f30d7b5ae980d419b4393a0f442b1c821ef58c0862898b0d22f74a4f8c5d83069493e3ec0b92f17dc1fe6e4cd437c1ec25039e7ce839"),
+			hexEncPubkey("874cc8d1213beb65c4e0e1de38ef5d8165235893ac74ab5ea937c885eaab25c8d79dad0456e9fd3e9450626cac7e107b004478fb59842f067857f39a47cee695"),
+			hexEncPubkey("d94193f236105010972f5df1b7818b55846592a0445b9cdc4eaed811b8c4c0f7c27dc8cc9837a4774656d6b34682d6d329d42b6ebb55da1d475c2474dc3dfdf4"),
+			hexEncPubkey("edd9af6aded4094e9785637c28fccbd3980cbe28e2eb9a411048a23c2ace4bd6b0b7088a7817997b49a3dd05fc6929ca6c7abbb69438dbdabe65e971d2a794b2"),
 		},
 		},
 		250: {
 		250: {
-			MustHexID("53a5bd1215d4ab709ae8fdc2ced50bba320bced78bd9c5dc92947fb402250c914891786db0978c898c058493f86fc68b1c5de8a5cb36336150ac7a88655b6c39"),
-			MustHexID("b7f79e3ab59f79262623c9ccefc8f01d682323aee56ffbe295437487e9d5acaf556a9c92e1f1c6a9601f2b9eb6b027ae1aeaebac71d61b9b78e88676efd3e1a3"),
-			MustHexID("d374bf7e8d7ffff69cc00bebff38ef5bc1dcb0a8d51c1a3d70e61ac6b2e2d6617109254b0ac224354dfbf79009fe4239e09020c483cc60c071e00b9238684f30"),
-			MustHexID("1e1eac1c9add703eb252eb991594f8f5a173255d526a855fab24ae57dc277e055bc3c7a7ae0b45d437c4f47a72d97eb7b126f2ba344ba6c0e14b2c6f27d4b1e6"),
-			MustHexID("ae28953f63d4bc4e706712a59319c111f5ff8f312584f65d7436b4cd3d14b217b958f8486bad666b4481fe879019fb1f767cf15b3e3e2711efc33b56d460448a"),
-			MustHexID("934bb1edf9c7a318b82306aca67feb3d6b434421fa275d694f0b4927afd8b1d3935b727fd4ff6e3d012e0c82f1824385174e8c6450ade59c2a43281a4b3446b6"),
-			MustHexID("9eef3f28f70ce19637519a0916555bf76d26de31312ac656cf9d3e379899ea44e4dd7ffcce923b4f3563f8a00489a34bd6936db0cbb4c959d32c49f017e07d05"),
-			MustHexID("82200872e8f871c48f1fad13daec6478298099b591bb3dbc4ef6890aa28ebee5860d07d70be62f4c0af85085a90ae8179ee8f937cf37915c67ea73e704b03ee7"),
-			MustHexID("6c75a5834a08476b7fc37ff3dc2011dc3ea3b36524bad7a6d319b18878fad813c0ba76d1f4555cacd3890c865438c21f0e0aed1f80e0a157e642124c69f43a11"),
-			MustHexID("995b873742206cb02b736e73a88580c2aacb0bd4a3c97a647b647bcab3f5e03c0e0736520a8b3600da09edf4248991fb01091ec7ff3ec7cdc8a1beae011e7aae"),
-			MustHexID("c773a056594b5cdef2e850d30891ff0e927c3b1b9c35cd8e8d53a1017001e237468e1ece3ae33d612ca3e6abb0a9169aa352e9dcda358e5af2ad982b577447db"),
-			MustHexID("2b46a5f6923f475c6be99ec6d134437a6d11f6bb4b4ac6bcd94572fa1092639d1c08aeefcb51f0912f0a060f71d4f38ee4da70ecc16010b05dd4a674aab14c3a"),
-			MustHexID("af6ab501366debbaa0d22e20e9688f32ef6b3b644440580fd78de4fe0e99e2a16eb5636bbae0d1c259df8ddda77b35b9a35cbc36137473e9c68fbc9d203ba842"),
-			MustHexID("c9f6f2dd1a941926f03f770695bda289859e85fabaf94baaae20b93e5015dc014ba41150176a36a1884adb52f405194693e63b0c464a6891cc9cc1c80d450326"),
-			MustHexID("5b116f0751526868a909b61a30b0c5282c37df6925cc03ddea556ef0d0602a9595fd6c14d371f8ed7d45d89918a032dcd22be4342a8793d88fdbeb3ca3d75bd7"),
-			MustHexID("50f3222fb6b82481c7c813b2172e1daea43e2710a443b9c2a57a12bd160dd37e20f87aa968c82ad639af6972185609d47036c0d93b4b7269b74ebd7073221c10"),
+			hexEncPubkey("53a5bd1215d4ab709ae8fdc2ced50bba320bced78bd9c5dc92947fb402250c914891786db0978c898c058493f86fc68b1c5de8a5cb36336150ac7a88655b6c39"),
+			hexEncPubkey("b7f79e3ab59f79262623c9ccefc8f01d682323aee56ffbe295437487e9d5acaf556a9c92e1f1c6a9601f2b9eb6b027ae1aeaebac71d61b9b78e88676efd3e1a3"),
+			hexEncPubkey("d374bf7e8d7ffff69cc00bebff38ef5bc1dcb0a8d51c1a3d70e61ac6b2e2d6617109254b0ac224354dfbf79009fe4239e09020c483cc60c071e00b9238684f30"),
+			hexEncPubkey("1e1eac1c9add703eb252eb991594f8f5a173255d526a855fab24ae57dc277e055bc3c7a7ae0b45d437c4f47a72d97eb7b126f2ba344ba6c0e14b2c6f27d4b1e6"),
+			hexEncPubkey("ae28953f63d4bc4e706712a59319c111f5ff8f312584f65d7436b4cd3d14b217b958f8486bad666b4481fe879019fb1f767cf15b3e3e2711efc33b56d460448a"),
+			hexEncPubkey("934bb1edf9c7a318b82306aca67feb3d6b434421fa275d694f0b4927afd8b1d3935b727fd4ff6e3d012e0c82f1824385174e8c6450ade59c2a43281a4b3446b6"),
+			hexEncPubkey("9eef3f28f70ce19637519a0916555bf76d26de31312ac656cf9d3e379899ea44e4dd7ffcce923b4f3563f8a00489a34bd6936db0cbb4c959d32c49f017e07d05"),
+			hexEncPubkey("82200872e8f871c48f1fad13daec6478298099b591bb3dbc4ef6890aa28ebee5860d07d70be62f4c0af85085a90ae8179ee8f937cf37915c67ea73e704b03ee7"),
+			hexEncPubkey("6c75a5834a08476b7fc37ff3dc2011dc3ea3b36524bad7a6d319b18878fad813c0ba76d1f4555cacd3890c865438c21f0e0aed1f80e0a157e642124c69f43a11"),
+			hexEncPubkey("995b873742206cb02b736e73a88580c2aacb0bd4a3c97a647b647bcab3f5e03c0e0736520a8b3600da09edf4248991fb01091ec7ff3ec7cdc8a1beae011e7aae"),
+			hexEncPubkey("c773a056594b5cdef2e850d30891ff0e927c3b1b9c35cd8e8d53a1017001e237468e1ece3ae33d612ca3e6abb0a9169aa352e9dcda358e5af2ad982b577447db"),
+			hexEncPubkey("2b46a5f6923f475c6be99ec6d134437a6d11f6bb4b4ac6bcd94572fa1092639d1c08aeefcb51f0912f0a060f71d4f38ee4da70ecc16010b05dd4a674aab14c3a"),
+			hexEncPubkey("af6ab501366debbaa0d22e20e9688f32ef6b3b644440580fd78de4fe0e99e2a16eb5636bbae0d1c259df8ddda77b35b9a35cbc36137473e9c68fbc9d203ba842"),
+			hexEncPubkey("c9f6f2dd1a941926f03f770695bda289859e85fabaf94baaae20b93e5015dc014ba41150176a36a1884adb52f405194693e63b0c464a6891cc9cc1c80d450326"),
+			hexEncPubkey("5b116f0751526868a909b61a30b0c5282c37df6925cc03ddea556ef0d0602a9595fd6c14d371f8ed7d45d89918a032dcd22be4342a8793d88fdbeb3ca3d75bd7"),
+			hexEncPubkey("50f3222fb6b82481c7c813b2172e1daea43e2710a443b9c2a57a12bd160dd37e20f87aa968c82ad639af6972185609d47036c0d93b4b7269b74ebd7073221c10"),
 		},
 		},
 		251: {
 		251: {
-			MustHexID("9b8f702a62d1bee67bedfeb102eca7f37fa1713e310f0d6651cc0c33ea7c5477575289ccd463e5a2574a00a676a1fdce05658ba447bb9d2827f0ba47b947e894"),
-			MustHexID("b97532eb83054ed054b4abdf413bb30c00e4205545c93521554dbe77faa3cfaa5bd31ef466a107b0b34a71ec97214c0c83919720142cddac93aa7a3e928d4708"),
-			MustHexID("2f7a5e952bfb67f2f90b8441b5fadc9ee13b1dcde3afeeb3dd64bf937f86663cc5c55d1fa83952b5422763c7df1b7f2794b751c6be316ebc0beb4942e65ab8c1"),
-			MustHexID("42c7483781727051a0b3660f14faf39e0d33de5e643702ae933837d036508ab856ce7eec8ec89c4929a4901256e5233a3d847d5d4893f91bcf21835a9a880fee"),
-			MustHexID("873bae27bf1dc854408fba94046a53ab0c965cebe1e4e12290806fc62b88deb1f4a47f9e18f78fc0e7913a0c6e42ac4d0fc3a20cea6bc65f0c8a0ca90b67521e"),
-			MustHexID("a7e3a370bbd761d413f8d209e85886f68bf73d5c3089b2dc6fa42aab1ecb5162635497eed95dee2417f3c9c74a3e76319625c48ead2e963c7de877cd4551f347"),
-			MustHexID("528597534776a40df2addaaea15b6ff832ce36b9748a265768368f657e76d58569d9f30dbb91e91cf0ae7efe8f402f17aa0ae15f5c55051ba03ba830287f4c42"),
-			MustHexID("461d8bd4f13c3c09031fdb84f104ed737a52f630261463ce0bdb5704259bab4b737dda688285b8444dbecaecad7f50f835190b38684ced5e90c54219e5adf1bc"),
-			MustHexID("6ec50c0be3fd232737090fc0111caaf0bb6b18f72be453428087a11a97fd6b52db0344acbf789a689bd4f5f50f79017ea784f8fd6fe723ad6ae675b9e3b13e21"),
-			MustHexID("12fc5e2f77a83fdcc727b79d8ae7fe6a516881138d3011847ee136b400fed7cfba1f53fd7a9730253c7aa4f39abeacd04f138417ba7fcb0f36cccc3514e0dab6"),
-			MustHexID("4fdbe75914ccd0bce02101606a1ccf3657ec963e3b3c20239d5fec87673fe446d649b4f15f1fe1a40e6cfbd446dda2d31d40bb602b1093b8fcd5f139ba0eb46a"),
-			MustHexID("3753668a0f6281e425ea69b52cb2d17ab97afbe6eb84cf5d25425bc5e53009388857640668fadd7c110721e6047c9697803bd8a6487b43bb343bfa32ebf24039"),
-			MustHexID("2e81b16346637dec4410fd88e527346145b9c0a849dbf2628049ac7dae016c8f4305649d5659ec77f1e8a0fac0db457b6080547226f06283598e3740ad94849a"),
-			MustHexID("802c3cc27f91c89213223d758f8d2ecd41135b357b6d698f24d811cdf113033a81c38e0bdff574a5c005b00a8c193dc2531f8c1fa05fa60acf0ab6f2858af09f"),
-			MustHexID("fcc9a2e1ac3667026ff16192876d1813bb75abdbf39b929a92863012fe8b1d890badea7a0de36274d5c1eb1e8f975785532c50d80fd44b1a4b692f437303393f"),
-			MustHexID("6d8b3efb461151dd4f6de809b62726f5b89e9b38e9ba1391967f61cde844f7528fecf821b74049207cee5a527096b31f3ad623928cd3ce51d926fa345a6b2951"),
+			hexEncPubkey("9b8f702a62d1bee67bedfeb102eca7f37fa1713e310f0d6651cc0c33ea7c5477575289ccd463e5a2574a00a676a1fdce05658ba447bb9d2827f0ba47b947e894"),
+			hexEncPubkey("b97532eb83054ed054b4abdf413bb30c00e4205545c93521554dbe77faa3cfaa5bd31ef466a107b0b34a71ec97214c0c83919720142cddac93aa7a3e928d4708"),
+			hexEncPubkey("2f7a5e952bfb67f2f90b8441b5fadc9ee13b1dcde3afeeb3dd64bf937f86663cc5c55d1fa83952b5422763c7df1b7f2794b751c6be316ebc0beb4942e65ab8c1"),
+			hexEncPubkey("42c7483781727051a0b3660f14faf39e0d33de5e643702ae933837d036508ab856ce7eec8ec89c4929a4901256e5233a3d847d5d4893f91bcf21835a9a880fee"),
+			hexEncPubkey("873bae27bf1dc854408fba94046a53ab0c965cebe1e4e12290806fc62b88deb1f4a47f9e18f78fc0e7913a0c6e42ac4d0fc3a20cea6bc65f0c8a0ca90b67521e"),
+			hexEncPubkey("a7e3a370bbd761d413f8d209e85886f68bf73d5c3089b2dc6fa42aab1ecb5162635497eed95dee2417f3c9c74a3e76319625c48ead2e963c7de877cd4551f347"),
+			hexEncPubkey("528597534776a40df2addaaea15b6ff832ce36b9748a265768368f657e76d58569d9f30dbb91e91cf0ae7efe8f402f17aa0ae15f5c55051ba03ba830287f4c42"),
+			hexEncPubkey("461d8bd4f13c3c09031fdb84f104ed737a52f630261463ce0bdb5704259bab4b737dda688285b8444dbecaecad7f50f835190b38684ced5e90c54219e5adf1bc"),
+			hexEncPubkey("6ec50c0be3fd232737090fc0111caaf0bb6b18f72be453428087a11a97fd6b52db0344acbf789a689bd4f5f50f79017ea784f8fd6fe723ad6ae675b9e3b13e21"),
+			hexEncPubkey("12fc5e2f77a83fdcc727b79d8ae7fe6a516881138d3011847ee136b400fed7cfba1f53fd7a9730253c7aa4f39abeacd04f138417ba7fcb0f36cccc3514e0dab6"),
+			hexEncPubkey("4fdbe75914ccd0bce02101606a1ccf3657ec963e3b3c20239d5fec87673fe446d649b4f15f1fe1a40e6cfbd446dda2d31d40bb602b1093b8fcd5f139ba0eb46a"),
+			hexEncPubkey("3753668a0f6281e425ea69b52cb2d17ab97afbe6eb84cf5d25425bc5e53009388857640668fadd7c110721e6047c9697803bd8a6487b43bb343bfa32ebf24039"),
+			hexEncPubkey("2e81b16346637dec4410fd88e527346145b9c0a849dbf2628049ac7dae016c8f4305649d5659ec77f1e8a0fac0db457b6080547226f06283598e3740ad94849a"),
+			hexEncPubkey("802c3cc27f91c89213223d758f8d2ecd41135b357b6d698f24d811cdf113033a81c38e0bdff574a5c005b00a8c193dc2531f8c1fa05fa60acf0ab6f2858af09f"),
+			hexEncPubkey("fcc9a2e1ac3667026ff16192876d1813bb75abdbf39b929a92863012fe8b1d890badea7a0de36274d5c1eb1e8f975785532c50d80fd44b1a4b692f437303393f"),
+			hexEncPubkey("6d8b3efb461151dd4f6de809b62726f5b89e9b38e9ba1391967f61cde844f7528fecf821b74049207cee5a527096b31f3ad623928cd3ce51d926fa345a6b2951"),
 		},
 		},
 		252: {
 		252: {
-			MustHexID("f1ae93157cc48c2075dd5868fbf523e79e06caf4b8198f352f6e526680b78ff4227263de92612f7d63472bd09367bb92a636fff16fe46ccf41614f7a72495c2a"),
-			MustHexID("587f482d111b239c27c0cb89b51dd5d574db8efd8de14a2e6a1400c54d4567e77c65f89c1da52841212080b91604104768350276b6682f2f961cdaf4039581c7"),
-			MustHexID("e3f88274d35cefdaabdf205afe0e80e936cc982b8e3e47a84ce664c413b29016a4fb4f3a3ebae0a2f79671f8323661ed462bf4390af94c424dc8ace0c301b90f"),
-			MustHexID("0ddc736077da9a12ba410dc5ea63cbcbe7659dd08596485b2bff3435221f82c10d263efd9af938e128464be64a178b7cd22e19f400d5802f4c9df54bf89f2619"),
-			MustHexID("784aa34d833c6ce63fcc1279630113c3272e82c4ae8c126c5a52a88ac461b6baeed4244e607b05dc14e5b2f41c70a273c3804dea237f14f7a1e546f6d1309d14"),
-			MustHexID("f253a2c354ee0e27cfcae786d726753d4ad24be6516b279a936195a487de4a59dbc296accf20463749ff55293263ed8c1b6365eecb248d44e75e9741c0d18205"),
-			MustHexID("a1910b80357b3ad9b4593e0628922939614dc9056a5fbf477279c8b2c1d0b4b31d89a0c09d0d41f795271d14d3360ef08a3f821e65e7e1f56c07a36afe49c7c5"),
-			MustHexID("f1168552c2efe541160f0909b0b4a9d6aeedcf595cdf0e9b165c97e3e197471a1ee6320e93389edfba28af6eaf10de98597ad56e7ab1b504ed762451996c3b98"),
-			MustHexID("b0c8e5d2c8634a7930e1a6fd082e448c6cf9d2d8b7293558b59238815a4df926c286bf297d2049f14e8296a6eb3256af614ec1812c4f2bbe807673b58bf14c8c"),
-			MustHexID("0fb346076396a38badc342df3679b55bd7f40a609ab103411fe45082c01f12ea016729e95914b2b5540e987ff5c9b133e85862648e7f36abdfd23100d248d234"),
-			MustHexID("f736e0cc83417feaa280d9483f5d4d72d1b036cd0c6d9cbdeb8ac35ceb2604780de46dddaa32a378474e1d5ccdf79b373331c30c7911ade2ae32f98832e5de1f"),
-			MustHexID("8b02991457602f42b38b342d3f2259ae4100c354b3843885f7e4e07bd644f64dab94bb7f38a3915f8b7f11d8e3f81c28e07a0078cf79d7397e38a7b7e0c857e2"),
-			MustHexID("9221d9f04a8a184993d12baa91116692bb685f887671302999d69300ad103eb2d2c75a09d8979404c6dd28f12362f58a1a43619c493d9108fd47588a23ce5824"),
-			MustHexID("652797801744dada833fff207d67484742eea6835d695925f3e618d71b68ec3c65bdd85b4302b2cdcb835ad3f94fd00d8da07e570b41bc0d2bcf69a8de1b3284"),
-			MustHexID("d84f06fe64debc4cd0625e36d19b99014b6218375262cc2209202bdbafd7dffcc4e34ce6398e182e02fd8faeed622c3e175545864902dfd3d1ac57647cddf4c6"),
-			MustHexID("d0ed87b294f38f1d741eb601020eeec30ac16331d05880fe27868f1e454446de367d7457b41c79e202eaf9525b029e4f1d7e17d85a55f83a557c005c68d7328a"),
+			hexEncPubkey("f1ae93157cc48c2075dd5868fbf523e79e06caf4b8198f352f6e526680b78ff4227263de92612f7d63472bd09367bb92a636fff16fe46ccf41614f7a72495c2a"),
+			hexEncPubkey("587f482d111b239c27c0cb89b51dd5d574db8efd8de14a2e6a1400c54d4567e77c65f89c1da52841212080b91604104768350276b6682f2f961cdaf4039581c7"),
+			hexEncPubkey("e3f88274d35cefdaabdf205afe0e80e936cc982b8e3e47a84ce664c413b29016a4fb4f3a3ebae0a2f79671f8323661ed462bf4390af94c424dc8ace0c301b90f"),
+			hexEncPubkey("0ddc736077da9a12ba410dc5ea63cbcbe7659dd08596485b2bff3435221f82c10d263efd9af938e128464be64a178b7cd22e19f400d5802f4c9df54bf89f2619"),
+			hexEncPubkey("784aa34d833c6ce63fcc1279630113c3272e82c4ae8c126c5a52a88ac461b6baeed4244e607b05dc14e5b2f41c70a273c3804dea237f14f7a1e546f6d1309d14"),
+			hexEncPubkey("f253a2c354ee0e27cfcae786d726753d4ad24be6516b279a936195a487de4a59dbc296accf20463749ff55293263ed8c1b6365eecb248d44e75e9741c0d18205"),
+			hexEncPubkey("a1910b80357b3ad9b4593e0628922939614dc9056a5fbf477279c8b2c1d0b4b31d89a0c09d0d41f795271d14d3360ef08a3f821e65e7e1f56c07a36afe49c7c5"),
+			hexEncPubkey("f1168552c2efe541160f0909b0b4a9d6aeedcf595cdf0e9b165c97e3e197471a1ee6320e93389edfba28af6eaf10de98597ad56e7ab1b504ed762451996c3b98"),
+			hexEncPubkey("b0c8e5d2c8634a7930e1a6fd082e448c6cf9d2d8b7293558b59238815a4df926c286bf297d2049f14e8296a6eb3256af614ec1812c4f2bbe807673b58bf14c8c"),
+			hexEncPubkey("0fb346076396a38badc342df3679b55bd7f40a609ab103411fe45082c01f12ea016729e95914b2b5540e987ff5c9b133e85862648e7f36abdfd23100d248d234"),
+			hexEncPubkey("f736e0cc83417feaa280d9483f5d4d72d1b036cd0c6d9cbdeb8ac35ceb2604780de46dddaa32a378474e1d5ccdf79b373331c30c7911ade2ae32f98832e5de1f"),
+			hexEncPubkey("8b02991457602f42b38b342d3f2259ae4100c354b3843885f7e4e07bd644f64dab94bb7f38a3915f8b7f11d8e3f81c28e07a0078cf79d7397e38a7b7e0c857e2"),
+			hexEncPubkey("9221d9f04a8a184993d12baa91116692bb685f887671302999d69300ad103eb2d2c75a09d8979404c6dd28f12362f58a1a43619c493d9108fd47588a23ce5824"),
+			hexEncPubkey("652797801744dada833fff207d67484742eea6835d695925f3e618d71b68ec3c65bdd85b4302b2cdcb835ad3f94fd00d8da07e570b41bc0d2bcf69a8de1b3284"),
+			hexEncPubkey("d84f06fe64debc4cd0625e36d19b99014b6218375262cc2209202bdbafd7dffcc4e34ce6398e182e02fd8faeed622c3e175545864902dfd3d1ac57647cddf4c6"),
+			hexEncPubkey("d0ed87b294f38f1d741eb601020eeec30ac16331d05880fe27868f1e454446de367d7457b41c79e202eaf9525b029e4f1d7e17d85a55f83a557c005c68d7328a"),
 		},
 		},
 		253: {
 		253: {
-			MustHexID("ad4485e386e3cc7c7310366a7c38fb810b8896c0d52e55944bfd320ca294e7912d6c53c0a0cf85e7ce226e92491d60430e86f8f15cda0161ed71893fb4a9e3a1"),
-			MustHexID("36d0e7e5b7734f98c6183eeeb8ac5130a85e910a925311a19c4941b1290f945d4fc3996b12ef4966960b6fa0fb29b1604f83a0f81bd5fd6398d2e1a22e46af0c"),
-			MustHexID("7d307d8acb4a561afa23bdf0bd945d35c90245e26345ec3a1f9f7df354222a7cdcb81339c9ed6744526c27a1a0c8d10857e98df942fa433602facac71ac68a31"),
-			MustHexID("d97bf55f88c83fae36232661af115d66ca600fc4bd6d1fb35ff9bb4dad674c02cf8c8d05f317525b5522250db58bb1ecafb7157392bf5aa61b178c61f098d995"),
-			MustHexID("7045d678f1f9eb7a4613764d17bd5698796494d0bf977b16f2dbc272b8a0f7858a60805c022fc3d1fe4f31c37e63cdaca0416c0d053ef48a815f8b19121605e0"),
-			MustHexID("14e1f21418d445748de2a95cd9a8c3b15b506f86a0acabd8af44bb968ce39885b19c8822af61b3dd58a34d1f265baec30e3ae56149dc7d2aa4a538f7319f69c8"),
-			MustHexID("b9453d78281b66a4eac95a1546017111eaaa5f92a65d0de10b1122940e92b319728a24edf4dec6acc412321b1c95266d39c7b3a5d265c629c3e49a65fb022c09"),
-			MustHexID("e8a49248419e3824a00d86af422f22f7366e2d4922b304b7169937616a01d9d6fa5abf5cc01061a352dc866f48e1fa2240dbb453d872b1d7be62bdfc1d5e248c"),
-			MustHexID("bebcff24b52362f30e0589ee573ce2d86f073d58d18e6852a592fa86ceb1a6c9b96d7fb9ec7ed1ed98a51b6743039e780279f6bb49d0a04327ac7a182d9a56f6"),
-			MustHexID("d0835e5a4291db249b8d2fca9f503049988180c7d247bedaa2cf3a1bad0a76709360a85d4f9a1423b2cbc82bb4d94b47c0cde20afc430224834c49fe312a9ae3"),
-			MustHexID("6b087fe2a2da5e4f0b0f4777598a4a7fb66bf77dbd5bfc44e8a7eaa432ab585a6e226891f56a7d4f5ed11a7c57b90f1661bba1059590ca4267a35801c2802913"),
-			MustHexID("d901e5bde52d1a0f4ddf010a686a53974cdae4ebe5c6551b3c37d6b6d635d38d5b0e5f80bc0186a2c7809dbf3a42870dd09643e68d32db896c6da8ba734579e7"),
-			MustHexID("96419fb80efae4b674402bb969ebaab86c1274f29a83a311e24516d36cdf148fe21754d46c97688cdd7468f24c08b13e4727c29263393638a3b37b99ff60ebca"),
-			MustHexID("7b9c1889ae916a5d5abcdfb0aaedcc9c6f9eb1c1a4f68d0c2d034fe79ac610ce917c3abc670744150fa891bfcd8ab14fed6983fca964de920aa393fa7b326748"),
-			MustHexID("7a369b2b8962cc4c65900be046482fbf7c14f98a135bbbae25152c82ad168fb2097b3d1429197cf46d3ce9fdeb64808f908a489cc6019725db040060fdfe5405"),
-			MustHexID("47bcae48288da5ecc7f5058dfa07cf14d89d06d6e449cb946e237aa6652ea050d9f5a24a65efdc0013ccf232bf88670979eddef249b054f63f38da9d7796dbd8"),
+			hexEncPubkey("ad4485e386e3cc7c7310366a7c38fb810b8896c0d52e55944bfd320ca294e7912d6c53c0a0cf85e7ce226e92491d60430e86f8f15cda0161ed71893fb4a9e3a1"),
+			hexEncPubkey("36d0e7e5b7734f98c6183eeeb8ac5130a85e910a925311a19c4941b1290f945d4fc3996b12ef4966960b6fa0fb29b1604f83a0f81bd5fd6398d2e1a22e46af0c"),
+			hexEncPubkey("7d307d8acb4a561afa23bdf0bd945d35c90245e26345ec3a1f9f7df354222a7cdcb81339c9ed6744526c27a1a0c8d10857e98df942fa433602facac71ac68a31"),
+			hexEncPubkey("d97bf55f88c83fae36232661af115d66ca600fc4bd6d1fb35ff9bb4dad674c02cf8c8d05f317525b5522250db58bb1ecafb7157392bf5aa61b178c61f098d995"),
+			hexEncPubkey("7045d678f1f9eb7a4613764d17bd5698796494d0bf977b16f2dbc272b8a0f7858a60805c022fc3d1fe4f31c37e63cdaca0416c0d053ef48a815f8b19121605e0"),
+			hexEncPubkey("14e1f21418d445748de2a95cd9a8c3b15b506f86a0acabd8af44bb968ce39885b19c8822af61b3dd58a34d1f265baec30e3ae56149dc7d2aa4a538f7319f69c8"),
+			hexEncPubkey("b9453d78281b66a4eac95a1546017111eaaa5f92a65d0de10b1122940e92b319728a24edf4dec6acc412321b1c95266d39c7b3a5d265c629c3e49a65fb022c09"),
+			hexEncPubkey("e8a49248419e3824a00d86af422f22f7366e2d4922b304b7169937616a01d9d6fa5abf5cc01061a352dc866f48e1fa2240dbb453d872b1d7be62bdfc1d5e248c"),
+			hexEncPubkey("bebcff24b52362f30e0589ee573ce2d86f073d58d18e6852a592fa86ceb1a6c9b96d7fb9ec7ed1ed98a51b6743039e780279f6bb49d0a04327ac7a182d9a56f6"),
+			hexEncPubkey("d0835e5a4291db249b8d2fca9f503049988180c7d247bedaa2cf3a1bad0a76709360a85d4f9a1423b2cbc82bb4d94b47c0cde20afc430224834c49fe312a9ae3"),
+			hexEncPubkey("6b087fe2a2da5e4f0b0f4777598a4a7fb66bf77dbd5bfc44e8a7eaa432ab585a6e226891f56a7d4f5ed11a7c57b90f1661bba1059590ca4267a35801c2802913"),
+			hexEncPubkey("d901e5bde52d1a0f4ddf010a686a53974cdae4ebe5c6551b3c37d6b6d635d38d5b0e5f80bc0186a2c7809dbf3a42870dd09643e68d32db896c6da8ba734579e7"),
+			hexEncPubkey("96419fb80efae4b674402bb969ebaab86c1274f29a83a311e24516d36cdf148fe21754d46c97688cdd7468f24c08b13e4727c29263393638a3b37b99ff60ebca"),
+			hexEncPubkey("7b9c1889ae916a5d5abcdfb0aaedcc9c6f9eb1c1a4f68d0c2d034fe79ac610ce917c3abc670744150fa891bfcd8ab14fed6983fca964de920aa393fa7b326748"),
+			hexEncPubkey("7a369b2b8962cc4c65900be046482fbf7c14f98a135bbbae25152c82ad168fb2097b3d1429197cf46d3ce9fdeb64808f908a489cc6019725db040060fdfe5405"),
+			hexEncPubkey("47bcae48288da5ecc7f5058dfa07cf14d89d06d6e449cb946e237aa6652ea050d9f5a24a65efdc0013ccf232bf88670979eddef249b054f63f38da9d7796dbd8"),
 		},
 		},
 		254: {
 		254: {
-			MustHexID("099739d7abc8abd38ecc7a816c521a1168a4dbd359fa7212a5123ab583ffa1cf485a5fed219575d6475dbcdd541638b2d3631a6c7fce7474e7fe3cba1d4d5853"),
-			MustHexID("c2b01603b088a7182d0cf7ef29fb2b04c70acb320fccf78526bf9472e10c74ee70b3fcfa6f4b11d167bd7d3bc4d936b660f2c9bff934793d97cb21750e7c3d31"),
-			MustHexID("20e4d8f45f2f863e94b45548c1ef22a11f7d36f263e4f8623761e05a64c4572379b000a52211751e2561b0f14f4fc92dd4130410c8ccc71eb4f0e95a700d4ca9"),
-			MustHexID("27f4a16cc085e72d86e25c98bd2eca173eaaee7565c78ec5a52e9e12b2211f35de81b5b45e9195de2ebfe29106742c59112b951a04eb7ae48822911fc1f9389e"),
-			MustHexID("55db5ee7d98e7f0b1c3b9d5be6f2bc619a1b86c3cdd513160ad4dcf267037a5fffad527ac15d50aeb32c59c13d1d4c1e567ebbf4de0d25236130c8361f9aac63"),
-			MustHexID("883df308b0130fc928a8559fe50667a0fff80493bc09685d18213b2db241a3ad11310ed86b0ef662b3ce21fc3d9aa7f3fc24b8d9afe17c7407e9afd3345ae548"),
-			MustHexID("c7af968cc9bc8200c3ee1a387405f7563be1dce6710a3439f42ea40657d0eae9d2b3c16c42d779605351fcdece4da637b9804e60ca08cfb89aec32c197beffa6"),
-			MustHexID("3e66f2b788e3ff1d04106b80597915cd7afa06c405a7ae026556b6e583dca8e05cfbab5039bb9a1b5d06083ffe8de5780b1775550e7218f5e98624bf7af9a0a8"),
-			MustHexID("4fc7f53764de3337fdaec0a711d35d3a923e72fa65025444d12230b3552ed43d9b2d1ad08ccb11f2d50c58809e6dd74dde910e195294fca3b47ae5a3967cc479"),
-			MustHexID("bafdfdcf6ccaa989436752fa97c77477b6baa7deb374b16c095492c529eb133e8e2f99e1977012b64767b9d34b2cf6d2048ed489bd822b5139b523f6a423167b"),
-			MustHexID("7f5d78008a4312fe059104ce80202c82b8915c2eb4411c6b812b16f7642e57c00f2c9425121f5cbac4257fe0b3e81ef5dea97ea2dbaa98f6a8b6fd4d1e5980bb"),
-			MustHexID("598c37fe78f922751a052f463aeb0cb0bc7f52b7c2a4cf2da72ec0931c7c32175d4165d0f8998f7320e87324ac3311c03f9382a5385c55f0407b7a66b2acd864"),
-			MustHexID("f758c4136e1c148777a7f3275a76e2db0b2b04066fd738554ec398c1c6cc9fb47e14a3b4c87bd47deaeab3ffd2110514c3855685a374794daff87b605b27ee2e"),
-			MustHexID("0307bb9e4fd865a49dcf1fe4333d1b944547db650ab580af0b33e53c4fef6c789531110fac801bbcbce21fc4d6f61b6d5b24abdf5b22e3030646d579f6dca9c2"),
-			MustHexID("82504b6eb49bb2c0f91a7006ce9cefdbaf6df38706198502c2e06601091fc9dc91e4f15db3410d45c6af355bc270b0f268d3dff560f956985c7332d4b10bd1ed"),
-			MustHexID("b39b5b677b45944ceebe76e76d1f051de2f2a0ec7b0d650da52135743e66a9a5dba45f638258f9a7545d9a790c7fe6d3fdf82c25425c7887323e45d27d06c057"),
+			hexEncPubkey("099739d7abc8abd38ecc7a816c521a1168a4dbd359fa7212a5123ab583ffa1cf485a5fed219575d6475dbcdd541638b2d3631a6c7fce7474e7fe3cba1d4d5853"),
+			hexEncPubkey("c2b01603b088a7182d0cf7ef29fb2b04c70acb320fccf78526bf9472e10c74ee70b3fcfa6f4b11d167bd7d3bc4d936b660f2c9bff934793d97cb21750e7c3d31"),
+			hexEncPubkey("20e4d8f45f2f863e94b45548c1ef22a11f7d36f263e4f8623761e05a64c4572379b000a52211751e2561b0f14f4fc92dd4130410c8ccc71eb4f0e95a700d4ca9"),
+			hexEncPubkey("27f4a16cc085e72d86e25c98bd2eca173eaaee7565c78ec5a52e9e12b2211f35de81b5b45e9195de2ebfe29106742c59112b951a04eb7ae48822911fc1f9389e"),
+			hexEncPubkey("55db5ee7d98e7f0b1c3b9d5be6f2bc619a1b86c3cdd513160ad4dcf267037a5fffad527ac15d50aeb32c59c13d1d4c1e567ebbf4de0d25236130c8361f9aac63"),
+			hexEncPubkey("883df308b0130fc928a8559fe50667a0fff80493bc09685d18213b2db241a3ad11310ed86b0ef662b3ce21fc3d9aa7f3fc24b8d9afe17c7407e9afd3345ae548"),
+			hexEncPubkey("c7af968cc9bc8200c3ee1a387405f7563be1dce6710a3439f42ea40657d0eae9d2b3c16c42d779605351fcdece4da637b9804e60ca08cfb89aec32c197beffa6"),
+			hexEncPubkey("3e66f2b788e3ff1d04106b80597915cd7afa06c405a7ae026556b6e583dca8e05cfbab5039bb9a1b5d06083ffe8de5780b1775550e7218f5e98624bf7af9a0a8"),
+			hexEncPubkey("4fc7f53764de3337fdaec0a711d35d3a923e72fa65025444d12230b3552ed43d9b2d1ad08ccb11f2d50c58809e6dd74dde910e195294fca3b47ae5a3967cc479"),
+			hexEncPubkey("bafdfdcf6ccaa989436752fa97c77477b6baa7deb374b16c095492c529eb133e8e2f99e1977012b64767b9d34b2cf6d2048ed489bd822b5139b523f6a423167b"),
+			hexEncPubkey("7f5d78008a4312fe059104ce80202c82b8915c2eb4411c6b812b16f7642e57c00f2c9425121f5cbac4257fe0b3e81ef5dea97ea2dbaa98f6a8b6fd4d1e5980bb"),
+			hexEncPubkey("598c37fe78f922751a052f463aeb0cb0bc7f52b7c2a4cf2da72ec0931c7c32175d4165d0f8998f7320e87324ac3311c03f9382a5385c55f0407b7a66b2acd864"),
+			hexEncPubkey("f758c4136e1c148777a7f3275a76e2db0b2b04066fd738554ec398c1c6cc9fb47e14a3b4c87bd47deaeab3ffd2110514c3855685a374794daff87b605b27ee2e"),
+			hexEncPubkey("0307bb9e4fd865a49dcf1fe4333d1b944547db650ab580af0b33e53c4fef6c789531110fac801bbcbce21fc4d6f61b6d5b24abdf5b22e3030646d579f6dca9c2"),
+			hexEncPubkey("82504b6eb49bb2c0f91a7006ce9cefdbaf6df38706198502c2e06601091fc9dc91e4f15db3410d45c6af355bc270b0f268d3dff560f956985c7332d4b10bd1ed"),
+			hexEncPubkey("b39b5b677b45944ceebe76e76d1f051de2f2a0ec7b0d650da52135743e66a9a5dba45f638258f9a7545d9a790c7fe6d3fdf82c25425c7887323e45d27d06c057"),
 		},
 		},
 		255: {
 		255: {
-			MustHexID("5c4d58d46e055dd1f093f81ee60a675e1f02f54da6206720adee4dccef9b67a31efc5c2a2949c31a04ee31beadc79aba10da31440a1f9ff2a24093c63c36d784"),
-			MustHexID("ea72161ffdd4b1e124c7b93b0684805f4c4b58d617ed498b37a145c670dbc2e04976f8785583d9c805ffbf343c31d492d79f841652bbbd01b61ed85640b23495"),
-			MustHexID("51caa1d93352d47a8e531692a3612adac1e8ac68d0a200d086c1c57ae1e1a91aa285ab242e8c52ef9d7afe374c9485b122ae815f1707b875569d0433c1c3ce85"),
-			MustHexID("c08397d5751b47bd3da044b908be0fb0e510d3149574dff7aeab33749b023bb171b5769990fe17469dbebc100bc150e798aeda426a2dcc766699a225fddd75c6"),
-			MustHexID("0222c1c194b749736e593f937fad67ee348ac57287a15c7e42877aa38a9b87732a408bca370f812efd0eedbff13e6d5b854bf3ba1dec431a796ed47f32552b09"),
-			MustHexID("03d859cd46ef02d9bfad5268461a6955426845eef4126de6be0fa4e8d7e0727ba2385b78f1a883a8239e95ebb814f2af8379632c7d5b100688eebc5841209582"),
-			MustHexID("64d5004b7e043c39ff0bd10cb20094c287721d5251715884c280a612b494b3e9e1c64ba6f67614994c7d969a0d0c0295d107d53fc225d47c44c4b82852d6f960"),
-			MustHexID("b0a5eefb2dab6f786670f35bf9641eefe6dd87fd3f1362bcab4aaa792903500ab23d88fae68411372e0813b057535a601d46e454323745a948017f6063a47b1f"),
-			MustHexID("0cc6df0a3433d448b5684d2a3ffa9d1a825388177a18f44ad0008c7bd7702f1ec0fc38b83506f7de689c3b6ecb552599927e29699eed6bb867ff08f80068b287"),
-			MustHexID("50772f7b8c03a4e153355fbbf79c8a80cf32af656ff0c7873c99911099d04a0dae0674706c357e0145ad017a0ade65e6052cb1b0d574fcd6f67da3eee0ace66b"),
-			MustHexID("1ae37829c9ef41f8b508b82259ebac76b1ed900d7a45c08b7970f25d2d48ddd1829e2f11423a18749940b6dab8598c6e416cef0efd47e46e51f29a0bc65b37cd"),
-			MustHexID("ba973cab31c2af091fc1644a93527d62b2394999e2b6ccbf158dd5ab9796a43d408786f1803ef4e29debfeb62fce2b6caa5ab2b24d1549c822a11c40c2856665"),
-			MustHexID("bc413ad270dd6ea25bddba78f3298b03b8ba6f8608ac03d06007d4116fa78ef5a0cfe8c80155089382fc7a193243ee5500082660cb5d7793f60f2d7d18650964"),
-			MustHexID("5a6a9ef07634d9eec3baa87c997b529b92652afa11473dfee41ef7037d5c06e0ddb9fe842364462d79dd31cff8a59a1b8d5bc2b810dea1d4cbbd3beb80ecec83"),
-			MustHexID("f492c6ee2696d5f682f7f537757e52744c2ae560f1090a07024609e903d334e9e174fc01609c5a229ddbcac36c9d21adaf6457dab38a25bfd44f2f0ee4277998"),
-			MustHexID("459e4db99298cb0467a90acee6888b08bb857450deac11015cced5104853be5adce5b69c740968bc7f931495d671a70cad9f48546d7cd203357fe9af0e8d2164"),
+			hexEncPubkey("5c4d58d46e055dd1f093f81ee60a675e1f02f54da6206720adee4dccef9b67a31efc5c2a2949c31a04ee31beadc79aba10da31440a1f9ff2a24093c63c36d784"),
+			hexEncPubkey("ea72161ffdd4b1e124c7b93b0684805f4c4b58d617ed498b37a145c670dbc2e04976f8785583d9c805ffbf343c31d492d79f841652bbbd01b61ed85640b23495"),
+			hexEncPubkey("51caa1d93352d47a8e531692a3612adac1e8ac68d0a200d086c1c57ae1e1a91aa285ab242e8c52ef9d7afe374c9485b122ae815f1707b875569d0433c1c3ce85"),
+			hexEncPubkey("c08397d5751b47bd3da044b908be0fb0e510d3149574dff7aeab33749b023bb171b5769990fe17469dbebc100bc150e798aeda426a2dcc766699a225fddd75c6"),
+			hexEncPubkey("0222c1c194b749736e593f937fad67ee348ac57287a15c7e42877aa38a9b87732a408bca370f812efd0eedbff13e6d5b854bf3ba1dec431a796ed47f32552b09"),
+			hexEncPubkey("03d859cd46ef02d9bfad5268461a6955426845eef4126de6be0fa4e8d7e0727ba2385b78f1a883a8239e95ebb814f2af8379632c7d5b100688eebc5841209582"),
+			hexEncPubkey("64d5004b7e043c39ff0bd10cb20094c287721d5251715884c280a612b494b3e9e1c64ba6f67614994c7d969a0d0c0295d107d53fc225d47c44c4b82852d6f960"),
+			hexEncPubkey("b0a5eefb2dab6f786670f35bf9641eefe6dd87fd3f1362bcab4aaa792903500ab23d88fae68411372e0813b057535a601d46e454323745a948017f6063a47b1f"),
+			hexEncPubkey("0cc6df0a3433d448b5684d2a3ffa9d1a825388177a18f44ad0008c7bd7702f1ec0fc38b83506f7de689c3b6ecb552599927e29699eed6bb867ff08f80068b287"),
+			hexEncPubkey("50772f7b8c03a4e153355fbbf79c8a80cf32af656ff0c7873c99911099d04a0dae0674706c357e0145ad017a0ade65e6052cb1b0d574fcd6f67da3eee0ace66b"),
+			hexEncPubkey("1ae37829c9ef41f8b508b82259ebac76b1ed900d7a45c08b7970f25d2d48ddd1829e2f11423a18749940b6dab8598c6e416cef0efd47e46e51f29a0bc65b37cd"),
+			hexEncPubkey("ba973cab31c2af091fc1644a93527d62b2394999e2b6ccbf158dd5ab9796a43d408786f1803ef4e29debfeb62fce2b6caa5ab2b24d1549c822a11c40c2856665"),
+			hexEncPubkey("bc413ad270dd6ea25bddba78f3298b03b8ba6f8608ac03d06007d4116fa78ef5a0cfe8c80155089382fc7a193243ee5500082660cb5d7793f60f2d7d18650964"),
+			hexEncPubkey("5a6a9ef07634d9eec3baa87c997b529b92652afa11473dfee41ef7037d5c06e0ddb9fe842364462d79dd31cff8a59a1b8d5bc2b810dea1d4cbbd3beb80ecec83"),
+			hexEncPubkey("f492c6ee2696d5f682f7f537757e52744c2ae560f1090a07024609e903d334e9e174fc01609c5a229ddbcac36c9d21adaf6457dab38a25bfd44f2f0ee4277998"),
+			hexEncPubkey("459e4db99298cb0467a90acee6888b08bb857450deac11015cced5104853be5adce5b69c740968bc7f931495d671a70cad9f48546d7cd203357fe9af0e8d2164"),
 		},
 		},
 		256: {
 		256: {
-			MustHexID("a8593af8a4aef7b806b5197612017951bac8845a1917ca9a6a15dd6086d608505144990b245785c4cd2d67a295701c7aac2aa18823fb0033987284b019656268"),
-			MustHexID("d2eebef914928c3aad77fc1b2a495f52d2294acf5edaa7d8a530b540f094b861a68fe8348a46a7c302f08ab609d85912a4968eacfea0740847b29421b4795d9e"),
-			MustHexID("b14bfcb31495f32b650b63cf7d08492e3e29071fdc73cf2da0da48d4b191a70ba1a65f42ad8c343206101f00f8a48e8db4b08bf3f622c0853e7323b250835b91"),
-			MustHexID("7feaee0d818c03eb30e4e0bf03ade0f3c21ca38e938a761aa1781cf70bda8cc5cd631a6cc53dd44f1d4a6d3e2dae6513c6c66ee50cb2f0e9ad6f7e319b309fd9"),
-			MustHexID("4ca3b657b139311db8d583c25dd5963005e46689e1317620496cc64129c7f3e52870820e0ec7941d28809311df6db8a2867bbd4f235b4248af24d7a9c22d1232"),
-			MustHexID("1181defb1d16851d42dd951d84424d6bd1479137f587fa184d5a8152be6b6b16ed08bcdb2c2ed8539bcde98c80c432875f9f724737c316a2bd385a39d3cab1d8"),
-			MustHexID("d9dd818769fa0c3ec9f553c759b92476f082817252a04a47dc1777740b1731d280058c66f982812f173a294acf4944a85ba08346e2de153ba3ba41ce8a62cb64"),
-			MustHexID("bd7c4f8a9e770aa915c771b15e107ca123d838762da0d3ffc53aa6b53e9cd076cffc534ec4d2e4c334c683f1f5ea72e0e123f6c261915ed5b58ac1b59f003d88"),
-			MustHexID("3dd5739c73649d510456a70e9d6b46a855864a4a3f744e088fd8c8da11b18e4c9b5f2d7da50b1c147b2bae5ca9609ae01f7a3cdea9dce34f80a91d29cd82f918"),
-			MustHexID("f0d7df1efc439b4bcc0b762118c1cfa99b2a6143a9f4b10e3c9465125f4c9fca4ab88a2504169bbcad65492cf2f50da9dd5d077c39574a944f94d8246529066b"),
-			MustHexID("dd598b9ba441448e5fb1a6ec6c5f5aa9605bad6e223297c729b1705d11d05f6bfd3d41988b694681ae69bb03b9a08bff4beab5596503d12a39bffb5cd6e94c7c"),
-			MustHexID("3fce284ac97e567aebae681b15b7a2b6df9d873945536335883e4bbc26460c064370537f323fd1ada828ea43154992d14ac0cec0940a2bd2a3f42ec156d60c83"),
-			MustHexID("7c8dfa8c1311cb14fb29a8ac11bca23ecc115e56d9fcf7b7ac1db9066aa4eb39f8b1dabf46e192a65be95ebfb4e839b5ab4533fef414921825e996b210dd53bd"),
-			MustHexID("cafa6934f82120456620573d7f801390ed5e16ed619613a37e409e44ab355ef755e83565a913b48a9466db786f8d4fbd590bfec474c2524d4a2608d4eafd6abd"),
-			MustHexID("9d16600d0dd310d77045769fed2cb427f32db88cd57d86e49390c2ba8a9698cfa856f775be2013237226e7bf47b248871cf865d23015937d1edeb20db5e3e760"),
-			MustHexID("17be6b6ba54199b1d80eff866d348ea11d8a4b341d63ad9a6681d3ef8a43853ac564d153eb2a8737f0afc9ab320f6f95c55aa11aaa13bbb1ff422fd16bdf8188"),
+			hexEncPubkey("a8593af8a4aef7b806b5197612017951bac8845a1917ca9a6a15dd6086d608505144990b245785c4cd2d67a295701c7aac2aa18823fb0033987284b019656268"),
+			hexEncPubkey("d2eebef914928c3aad77fc1b2a495f52d2294acf5edaa7d8a530b540f094b861a68fe8348a46a7c302f08ab609d85912a4968eacfea0740847b29421b4795d9e"),
+			hexEncPubkey("b14bfcb31495f32b650b63cf7d08492e3e29071fdc73cf2da0da48d4b191a70ba1a65f42ad8c343206101f00f8a48e8db4b08bf3f622c0853e7323b250835b91"),
+			hexEncPubkey("7feaee0d818c03eb30e4e0bf03ade0f3c21ca38e938a761aa1781cf70bda8cc5cd631a6cc53dd44f1d4a6d3e2dae6513c6c66ee50cb2f0e9ad6f7e319b309fd9"),
+			hexEncPubkey("4ca3b657b139311db8d583c25dd5963005e46689e1317620496cc64129c7f3e52870820e0ec7941d28809311df6db8a2867bbd4f235b4248af24d7a9c22d1232"),
+			hexEncPubkey("1181defb1d16851d42dd951d84424d6bd1479137f587fa184d5a8152be6b6b16ed08bcdb2c2ed8539bcde98c80c432875f9f724737c316a2bd385a39d3cab1d8"),
+			hexEncPubkey("d9dd818769fa0c3ec9f553c759b92476f082817252a04a47dc1777740b1731d280058c66f982812f173a294acf4944a85ba08346e2de153ba3ba41ce8a62cb64"),
+			hexEncPubkey("bd7c4f8a9e770aa915c771b15e107ca123d838762da0d3ffc53aa6b53e9cd076cffc534ec4d2e4c334c683f1f5ea72e0e123f6c261915ed5b58ac1b59f003d88"),
+			hexEncPubkey("3dd5739c73649d510456a70e9d6b46a855864a4a3f744e088fd8c8da11b18e4c9b5f2d7da50b1c147b2bae5ca9609ae01f7a3cdea9dce34f80a91d29cd82f918"),
+			hexEncPubkey("f0d7df1efc439b4bcc0b762118c1cfa99b2a6143a9f4b10e3c9465125f4c9fca4ab88a2504169bbcad65492cf2f50da9dd5d077c39574a944f94d8246529066b"),
+			hexEncPubkey("dd598b9ba441448e5fb1a6ec6c5f5aa9605bad6e223297c729b1705d11d05f6bfd3d41988b694681ae69bb03b9a08bff4beab5596503d12a39bffb5cd6e94c7c"),
+			hexEncPubkey("3fce284ac97e567aebae681b15b7a2b6df9d873945536335883e4bbc26460c064370537f323fd1ada828ea43154992d14ac0cec0940a2bd2a3f42ec156d60c83"),
+			hexEncPubkey("7c8dfa8c1311cb14fb29a8ac11bca23ecc115e56d9fcf7b7ac1db9066aa4eb39f8b1dabf46e192a65be95ebfb4e839b5ab4533fef414921825e996b210dd53bd"),
+			hexEncPubkey("cafa6934f82120456620573d7f801390ed5e16ed619613a37e409e44ab355ef755e83565a913b48a9466db786f8d4fbd590bfec474c2524d4a2608d4eafd6abd"),
+			hexEncPubkey("9d16600d0dd310d77045769fed2cb427f32db88cd57d86e49390c2ba8a9698cfa856f775be2013237226e7bf47b248871cf865d23015937d1edeb20db5e3e760"),
+			hexEncPubkey("17be6b6ba54199b1d80eff866d348ea11d8a4b341d63ad9a6681d3ef8a43853ac564d153eb2a8737f0afc9ab320f6f95c55aa11aaa13bbb1ff422fd16bdf8188"),
 		},
 		},
 	},
 	},
 }
 }
 
 
 type preminedTestnet struct {
 type preminedTestnet struct {
-	target    NodeID
-	targetSha common.Hash // sha3(target)
-	dists     [hashBits + 1][]NodeID
+	target    encPubkey
+	targetSha enode.ID // sha3(target)
+	dists     [hashBits + 1][]encPubkey
 }
 }
 
 
-func (tn *preminedTestnet) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
+func (tn *preminedTestnet) findnode(toid enode.ID, toaddr *net.UDPAddr, target encPubkey) ([]*node, error) {
 	// current log distance is encoded in port number
 	// current log distance is encoded in port number
 	// fmt.Println("findnode query at dist", toaddr.Port)
 	// fmt.Println("findnode query at dist", toaddr.Port)
 	if toaddr.Port == 0 {
 	if toaddr.Port == 0 {
 		panic("query to node at distance 0")
 		panic("query to node at distance 0")
 	}
 	}
-	next := uint16(toaddr.Port) - 1
-	var result []*Node
-	for i, id := range tn.dists[toaddr.Port] {
-		result = append(result, NewNode(id, net.ParseIP("127.0.0.1"), next, uint16(i)))
+	next := toaddr.Port - 1
+	var result []*node
+	for i, ekey := range tn.dists[toaddr.Port] {
+		key, _ := decodePubkey(ekey)
+		node := wrapNode(enode.NewV4(key, net.ParseIP("127.0.0.1"), i, next))
+		result = append(result, node)
 	}
 	}
 	return result, nil
 	return result, nil
 }
 }
 
 
-func (*preminedTestnet) close()                                      {}
-func (*preminedTestnet) waitping(from NodeID) error                  { return nil }
-func (*preminedTestnet) ping(toid NodeID, toaddr *net.UDPAddr) error { return nil }
+func (*preminedTestnet) close()                                        {}
+func (*preminedTestnet) waitping(from enode.ID) error                  { return nil }
+func (*preminedTestnet) ping(toid enode.ID, toaddr *net.UDPAddr) error { return nil }
 
 
 // mine generates a testnet struct literal with nodes at
 // mine generates a testnet struct literal with nodes at
 // various distances to the given target.
 // various distances to the given target.
-func (tn *preminedTestnet) mine(target NodeID) {
+func (tn *preminedTestnet) mine(target encPubkey) {
 	tn.target = target
 	tn.target = target
-	tn.targetSha = crypto.Keccak256Hash(tn.target[:])
+	tn.targetSha = tn.target.id()
 	found := 0
 	found := 0
 	for found < bucketSize*10 {
 	for found < bucketSize*10 {
 		k := newkey()
 		k := newkey()
-		id := PubkeyID(&k.PublicKey)
-		sha := crypto.Keccak256Hash(id[:])
-		ld := logdist(tn.targetSha, sha)
+		key := encodePubkey(&k.PublicKey)
+		ld := enode.LogDist(tn.targetSha, key.id())
 		if len(tn.dists[ld]) < bucketSize {
 		if len(tn.dists[ld]) < bucketSize {
-			tn.dists[ld] = append(tn.dists[ld], id)
+			tn.dists[ld] = append(tn.dists[ld], key)
 			fmt.Println("found ID with ld", ld)
 			fmt.Println("found ID with ld", ld)
 			found++
 			found++
 		}
 		}
@@ -594,14 +549,14 @@ func (tn *preminedTestnet) mine(target NodeID) {
 	fmt.Println("&preminedTestnet{")
 	fmt.Println("&preminedTestnet{")
 	fmt.Printf("	target: %#v,\n", tn.target)
 	fmt.Printf("	target: %#v,\n", tn.target)
 	fmt.Printf("	targetSha: %#v,\n", tn.targetSha)
 	fmt.Printf("	targetSha: %#v,\n", tn.targetSha)
-	fmt.Printf("	dists: [%d][]NodeID{\n", len(tn.dists))
+	fmt.Printf("	dists: [%d][]encPubkey{\n", len(tn.dists))
 	for ld, ns := range tn.dists {
 	for ld, ns := range tn.dists {
 		if len(ns) == 0 {
 		if len(ns) == 0 {
 			continue
 			continue
 		}
 		}
-		fmt.Printf("		%d: []NodeID{\n", ld)
+		fmt.Printf("		%d: []encPubkey{\n", ld)
 		for _, n := range ns {
 		for _, n := range ns {
-			fmt.Printf("			MustHexID(\"%x\"),\n", n[:])
+			fmt.Printf("			hexEncPubkey(\"%x\"),\n", n[:])
 		}
 		}
 		fmt.Println("		},")
 		fmt.Println("		},")
 	}
 	}
@@ -609,40 +564,6 @@ func (tn *preminedTestnet) mine(target NodeID) {
 	fmt.Println("}")
 	fmt.Println("}")
 }
 }
 
 
-func hasDuplicates(slice []*Node) bool {
-	seen := make(map[NodeID]bool)
-	for i, e := range slice {
-		if e == nil {
-			panic(fmt.Sprintf("nil *Node at %d", i))
-		}
-		if seen[e.ID] {
-			return true
-		}
-		seen[e.ID] = true
-	}
-	return false
-}
-
-func sortedByDistanceTo(distbase common.Hash, slice []*Node) bool {
-	var last common.Hash
-	for i, e := range slice {
-		if i > 0 && distcmp(distbase, e.sha, last) < 0 {
-			return false
-		}
-		last = e.sha
-	}
-	return true
-}
-
-func contains(ns []*Node, id NodeID) bool {
-	for _, n := range ns {
-		if n.ID == id {
-			return true
-		}
-	}
-	return false
-}
-
 // gen wraps quick.Value so it's easier to use.
 // gen wraps quick.Value so it's easier to use.
 // it generates a random value of the given value's type.
 // it generates a random value of the given value's type.
 func gen(typ interface{}, rand *rand.Rand) interface{} {
 func gen(typ interface{}, rand *rand.Rand) interface{} {
@@ -653,6 +574,13 @@ func gen(typ interface{}, rand *rand.Rand) interface{} {
 	return v.Interface()
 	return v.Interface()
 }
 }
 
 
+func quickcfg() *quick.Config {
+	return &quick.Config{
+		MaxCount: 5000,
+		Rand:     rand.New(rand.NewSource(time.Now().Unix())),
+	}
+}
+
 func newkey() *ecdsa.PrivateKey {
 func newkey() *ecdsa.PrivateKey {
 	key, err := crypto.GenerateKey()
 	key, err := crypto.GenerateKey()
 	if err != nil {
 	if err != nil {

+ 167 - 0
p2p/discover/table_util_test.go

@@ -0,0 +1,167 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package discover
+
+import (
+	"crypto/ecdsa"
+	"encoding/hex"
+	"fmt"
+	"math/rand"
+	"net"
+	"sync"
+
+	"github.com/ethereum/go-ethereum/p2p/enode"
+	"github.com/ethereum/go-ethereum/p2p/enr"
+)
+
+func newTestTable(t transport) (*Table, *enode.DB) {
+	var r enr.Record
+	r.Set(enr.IP{0, 0, 0, 0})
+	n := enode.SignNull(&r, enode.ID{})
+	db, _ := enode.OpenDB("")
+	tab, _ := newTable(t, n, db, nil)
+	return tab, db
+}
+
+// nodeAtDistance creates a node for which enode.LogDist(base, n.id) == ld.
+func nodeAtDistance(base enode.ID, ld int, ip net.IP) *node {
+	var r enr.Record
+	r.Set(enr.IP(ip))
+	return wrapNode(enode.SignNull(&r, idAtDistance(base, ld)))
+}
+
+// idAtDistance returns a random hash such that enode.LogDist(a, b) == n
+func idAtDistance(a enode.ID, n int) (b enode.ID) {
+	if n == 0 {
+		return a
+	}
+	// flip bit at position n, fill the rest with random bits
+	b = a
+	pos := len(a) - n/8 - 1
+	bit := byte(0x01) << (byte(n%8) - 1)
+	if bit == 0 {
+		pos++
+		bit = 0x80
+	}
+	b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits
+	for i := pos + 1; i < len(a); i++ {
+		b[i] = byte(rand.Intn(255))
+	}
+	return b
+}
+
+func intIP(i int) net.IP {
+	return net.IP{byte(i), 0, 2, byte(i)}
+}
+
+// fillBucket inserts nodes into the given bucket until it is full.
+func fillBucket(tab *Table, n *node) (last *node) {
+	ld := enode.LogDist(tab.self.ID(), n.ID())
+	b := tab.bucket(n.ID())
+	for len(b.entries) < bucketSize {
+		b.entries = append(b.entries, nodeAtDistance(tab.self.ID(), ld, intIP(ld)))
+	}
+	return b.entries[bucketSize-1]
+}
+
+type pingRecorder struct {
+	mu           sync.Mutex
+	dead, pinged map[enode.ID]bool
+}
+
+func newPingRecorder() *pingRecorder {
+	return &pingRecorder{
+		dead:   make(map[enode.ID]bool),
+		pinged: make(map[enode.ID]bool),
+	}
+}
+
+func (t *pingRecorder) findnode(toid enode.ID, toaddr *net.UDPAddr, target encPubkey) ([]*node, error) {
+	return nil, nil
+}
+
+func (t *pingRecorder) waitping(from enode.ID) error {
+	return nil // remote always pings
+}
+
+func (t *pingRecorder) ping(toid enode.ID, toaddr *net.UDPAddr) error {
+	t.mu.Lock()
+	defer t.mu.Unlock()
+
+	t.pinged[toid] = true
+	if t.dead[toid] {
+		return errTimeout
+	} else {
+		return nil
+	}
+}
+
+func (t *pingRecorder) close() {}
+
+func hasDuplicates(slice []*node) bool {
+	seen := make(map[enode.ID]bool)
+	for i, e := range slice {
+		if e == nil {
+			panic(fmt.Sprintf("nil *Node at %d", i))
+		}
+		if seen[e.ID()] {
+			return true
+		}
+		seen[e.ID()] = true
+	}
+	return false
+}
+
+func contains(ns []*node, id enode.ID) bool {
+	for _, n := range ns {
+		if n.ID() == id {
+			return true
+		}
+	}
+	return false
+}
+
+func sortedByDistanceTo(distbase enode.ID, slice []*node) bool {
+	var last enode.ID
+	for i, e := range slice {
+		if i > 0 && enode.DistCmp(distbase, e.ID(), last) < 0 {
+			return false
+		}
+		last = e.ID()
+	}
+	return true
+}
+
+func hexEncPubkey(h string) (ret encPubkey) {
+	b, err := hex.DecodeString(h)
+	if err != nil {
+		panic(err)
+	}
+	if len(b) != len(ret) {
+		panic("invalid length")
+	}
+	copy(ret[:], b)
+	return ret
+}
+
+func hexPubkey(h string) *ecdsa.PublicKey {
+	k, err := decodePubkey(hexEncPubkey(h))
+	if err != nil {
+		panic(err)
+	}
+	return k
+}

+ 72 - 50
p2p/discover/udp.go

@@ -27,6 +27,7 @@ import (
 
 
 	"github.com/ethereum/go-ethereum/crypto"
 	"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/nat"
 	"github.com/ethereum/go-ethereum/p2p/nat"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
@@ -46,8 +47,9 @@ var (
 
 
 // Timeouts
 // Timeouts
 const (
 const (
-	respTimeout = 500 * time.Millisecond
-	expiration  = 20 * time.Second
+	respTimeout    = 500 * time.Millisecond
+	expiration     = 20 * time.Second
+	bondExpiration = 24 * time.Hour
 
 
 	ntpFailureThreshold = 32               // Continuous timeouts after which to check NTP
 	ntpFailureThreshold = 32               // Continuous timeouts after which to check NTP
 	ntpWarningCooldown  = 10 * time.Minute // Minimum amount of time to pass before repeating NTP warning
 	ntpWarningCooldown  = 10 * time.Minute // Minimum amount of time to pass before repeating NTP warning
@@ -87,7 +89,7 @@ type (
 
 
 	// findnode is a query for nodes close to the given target.
 	// findnode is a query for nodes close to the given target.
 	findnode struct {
 	findnode struct {
-		Target     NodeID // doesn't need to be an actual public key
+		Target     encPubkey
 		Expiration uint64
 		Expiration uint64
 		// Ignore additional fields (for forward compatibility).
 		// Ignore additional fields (for forward compatibility).
 		Rest []rlp.RawValue `rlp:"tail"`
 		Rest []rlp.RawValue `rlp:"tail"`
@@ -105,7 +107,7 @@ type (
 		IP  net.IP // len 4 for IPv4 or 16 for IPv6
 		IP  net.IP // len 4 for IPv4 or 16 for IPv6
 		UDP uint16 // for discovery protocol
 		UDP uint16 // for discovery protocol
 		TCP uint16 // for RLPx protocol
 		TCP uint16 // for RLPx protocol
-		ID  NodeID
+		ID  encPubkey
 	}
 	}
 
 
 	rpcEndpoint struct {
 	rpcEndpoint struct {
@@ -123,7 +125,7 @@ func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint {
 	return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort}
 	return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort}
 }
 }
 
 
-func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) {
+func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*node, error) {
 	if rn.UDP <= 1024 {
 	if rn.UDP <= 1024 {
 		return nil, errors.New("low port")
 		return nil, errors.New("low port")
 	}
 	}
@@ -133,17 +135,26 @@ func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) {
 	if t.netrestrict != nil && !t.netrestrict.Contains(rn.IP) {
 	if t.netrestrict != nil && !t.netrestrict.Contains(rn.IP) {
 		return nil, errors.New("not contained in netrestrict whitelist")
 		return nil, errors.New("not contained in netrestrict whitelist")
 	}
 	}
-	n := NewNode(rn.ID, rn.IP, rn.UDP, rn.TCP)
-	err := n.validateComplete()
+	key, err := decodePubkey(rn.ID)
+	if err != nil {
+		return nil, err
+	}
+	n := wrapNode(enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP)))
+	err = n.ValidateComplete()
 	return n, err
 	return n, err
 }
 }
 
 
-func nodeToRPC(n *Node) rpcNode {
-	return rpcNode{ID: n.ID, IP: n.IP, UDP: n.UDP, TCP: n.TCP}
+func nodeToRPC(n *node) rpcNode {
+	var key ecdsa.PublicKey
+	var ekey encPubkey
+	if err := n.Load((*enode.Secp256k1)(&key)); err == nil {
+		ekey = encodePubkey(&key)
+	}
+	return rpcNode{ID: ekey, IP: n.IP(), UDP: uint16(n.UDP()), TCP: uint16(n.TCP())}
 }
 }
 
 
 type packet interface {
 type packet interface {
-	handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error
+	handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error
 	name() string
 	name() string
 }
 }
 
 
@@ -181,7 +192,7 @@ type udp struct {
 // to all the callback functions for that node.
 // to all the callback functions for that node.
 type pending struct {
 type pending struct {
 	// these fields must match in the reply.
 	// these fields must match in the reply.
-	from  NodeID
+	from  enode.ID
 	ptype byte
 	ptype byte
 
 
 	// time when the request must complete
 	// time when the request must complete
@@ -199,7 +210,7 @@ type pending struct {
 }
 }
 
 
 type reply struct {
 type reply struct {
-	from  NodeID
+	from  enode.ID
 	ptype byte
 	ptype byte
 	data  interface{}
 	data  interface{}
 	// loop indicates whether there was
 	// loop indicates whether there was
@@ -222,7 +233,7 @@ type Config struct {
 	AnnounceAddr *net.UDPAddr      // local address announced in the DHT
 	AnnounceAddr *net.UDPAddr      // local address announced in the DHT
 	NodeDBPath   string            // if set, the node database is stored at this filesystem location
 	NodeDBPath   string            // if set, the node database is stored at this filesystem location
 	NetRestrict  *netutil.Netlist  // network whitelist
 	NetRestrict  *netutil.Netlist  // network whitelist
-	Bootnodes    []*Node           // list of bootstrap nodes
+	Bootnodes    []*enode.Node     // list of bootstrap nodes
 	Unhandled    chan<- ReadPacket // unhandled packets are sent on this channel
 	Unhandled    chan<- ReadPacket // unhandled packets are sent on this channel
 }
 }
 
 
@@ -237,6 +248,16 @@ func ListenUDP(c conn, cfg Config) (*Table, error) {
 }
 }
 
 
 func newUDP(c conn, cfg Config) (*Table, *udp, error) {
 func newUDP(c conn, cfg Config) (*Table, *udp, error) {
+	realaddr := c.LocalAddr().(*net.UDPAddr)
+	if cfg.AnnounceAddr != nil {
+		realaddr = cfg.AnnounceAddr
+	}
+	self := enode.NewV4(&cfg.PrivateKey.PublicKey, realaddr.IP, realaddr.Port, realaddr.Port)
+	db, err := enode.OpenDB(cfg.NodeDBPath)
+	if err != nil {
+		return nil, nil, err
+	}
+
 	udp := &udp{
 	udp := &udp{
 		conn:        c,
 		conn:        c,
 		priv:        cfg.PrivateKey,
 		priv:        cfg.PrivateKey,
@@ -245,13 +266,9 @@ func newUDP(c conn, cfg Config) (*Table, *udp, error) {
 		gotreply:    make(chan reply),
 		gotreply:    make(chan reply),
 		addpending:  make(chan *pending),
 		addpending:  make(chan *pending),
 	}
 	}
-	realaddr := c.LocalAddr().(*net.UDPAddr)
-	if cfg.AnnounceAddr != nil {
-		realaddr = cfg.AnnounceAddr
-	}
 	// TODO: separate TCP port
 	// TODO: separate TCP port
 	udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port))
 	udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port))
-	tab, err := newTable(udp, PubkeyID(&cfg.PrivateKey.PublicKey), realaddr, cfg.NodeDBPath, cfg.Bootnodes)
+	tab, err := newTable(udp, self, db, cfg.Bootnodes)
 	if err != nil {
 	if err != nil {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
@@ -265,17 +282,18 @@ func newUDP(c conn, cfg Config) (*Table, *udp, error) {
 func (t *udp) close() {
 func (t *udp) close() {
 	close(t.closing)
 	close(t.closing)
 	t.conn.Close()
 	t.conn.Close()
+	t.db.Close()
 	// TODO: wait for the loops to end.
 	// TODO: wait for the loops to end.
 }
 }
 
 
 // ping sends a ping message to the given node and waits for a reply.
 // ping sends a ping message to the given node and waits for a reply.
-func (t *udp) ping(toid NodeID, toaddr *net.UDPAddr) error {
+func (t *udp) ping(toid enode.ID, toaddr *net.UDPAddr) error {
 	return <-t.sendPing(toid, toaddr, nil)
 	return <-t.sendPing(toid, toaddr, nil)
 }
 }
 
 
 // sendPing sends a ping message to the given node and invokes the callback
 // sendPing sends a ping message to the given node and invokes the callback
 // when the reply arrives.
 // when the reply arrives.
-func (t *udp) sendPing(toid NodeID, toaddr *net.UDPAddr, callback func()) <-chan error {
+func (t *udp) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) <-chan error {
 	req := &ping{
 	req := &ping{
 		Version:    4,
 		Version:    4,
 		From:       t.ourEndpoint,
 		From:       t.ourEndpoint,
@@ -299,21 +317,21 @@ func (t *udp) sendPing(toid NodeID, toaddr *net.UDPAddr, callback func()) <-chan
 	return errc
 	return errc
 }
 }
 
 
-func (t *udp) waitping(from NodeID) error {
+func (t *udp) waitping(from enode.ID) error {
 	return <-t.pending(from, pingPacket, func(interface{}) bool { return true })
 	return <-t.pending(from, pingPacket, func(interface{}) bool { return true })
 }
 }
 
 
 // findnode sends a findnode request to the given node and waits until
 // findnode sends a findnode request to the given node and waits until
 // the node has sent up to k neighbors.
 // the node has sent up to k neighbors.
-func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
+func (t *udp) findnode(toid enode.ID, toaddr *net.UDPAddr, target encPubkey) ([]*node, error) {
 	// If we haven't seen a ping from the destination node for a while, it won't remember
 	// If we haven't seen a ping from the destination node for a while, it won't remember
 	// our endpoint proof and reject findnode. Solicit a ping first.
 	// our endpoint proof and reject findnode. Solicit a ping first.
-	if time.Since(t.db.lastPingReceived(toid)) > nodeDBNodeExpiration {
+	if time.Since(t.db.LastPingReceived(toid)) > bondExpiration {
 		t.ping(toid, toaddr)
 		t.ping(toid, toaddr)
 		t.waitping(toid)
 		t.waitping(toid)
 	}
 	}
 
 
-	nodes := make([]*Node, 0, bucketSize)
+	nodes := make([]*node, 0, bucketSize)
 	nreceived := 0
 	nreceived := 0
 	errc := t.pending(toid, neighborsPacket, func(r interface{}) bool {
 	errc := t.pending(toid, neighborsPacket, func(r interface{}) bool {
 		reply := r.(*neighbors)
 		reply := r.(*neighbors)
@@ -337,7 +355,7 @@ func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node
 
 
 // pending adds a reply callback to the pending reply queue.
 // pending adds a reply callback to the pending reply queue.
 // see the documentation of type pending for a detailed explanation.
 // see the documentation of type pending for a detailed explanation.
-func (t *udp) pending(id NodeID, ptype byte, callback func(interface{}) bool) <-chan error {
+func (t *udp) pending(id enode.ID, ptype byte, callback func(interface{}) bool) <-chan error {
 	ch := make(chan error, 1)
 	ch := make(chan error, 1)
 	p := &pending{from: id, ptype: ptype, callback: callback, errc: ch}
 	p := &pending{from: id, ptype: ptype, callback: callback, errc: ch}
 	select {
 	select {
@@ -349,7 +367,7 @@ func (t *udp) pending(id NodeID, ptype byte, callback func(interface{}) bool) <-
 	return ch
 	return ch
 }
 }
 
 
-func (t *udp) handleReply(from NodeID, ptype byte, req packet) bool {
+func (t *udp) handleReply(from enode.ID, ptype byte, req packet) bool {
 	matched := make(chan bool, 1)
 	matched := make(chan bool, 1)
 	select {
 	select {
 	case t.gotreply <- reply{from, ptype, req, matched}:
 	case t.gotreply <- reply{from, ptype, req, matched}:
@@ -563,19 +581,20 @@ func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error {
 	return err
 	return err
 }
 }
 
 
-func decodePacket(buf []byte) (packet, NodeID, []byte, error) {
+func decodePacket(buf []byte) (packet, encPubkey, []byte, error) {
 	if len(buf) < headSize+1 {
 	if len(buf) < headSize+1 {
-		return nil, NodeID{}, nil, errPacketTooSmall
+		return nil, encPubkey{}, nil, errPacketTooSmall
 	}
 	}
 	hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:]
 	hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:]
 	shouldhash := crypto.Keccak256(buf[macSize:])
 	shouldhash := crypto.Keccak256(buf[macSize:])
 	if !bytes.Equal(hash, shouldhash) {
 	if !bytes.Equal(hash, shouldhash) {
-		return nil, NodeID{}, nil, errBadHash
+		return nil, encPubkey{}, nil, errBadHash
 	}
 	}
-	fromID, err := recoverNodeID(crypto.Keccak256(buf[headSize:]), sig)
+	fromKey, err := recoverNodeKey(crypto.Keccak256(buf[headSize:]), sig)
 	if err != nil {
 	if err != nil {
-		return nil, NodeID{}, hash, err
+		return nil, fromKey, hash, err
 	}
 	}
+
 	var req packet
 	var req packet
 	switch ptype := sigdata[0]; ptype {
 	switch ptype := sigdata[0]; ptype {
 	case pingPacket:
 	case pingPacket:
@@ -587,56 +606,59 @@ func decodePacket(buf []byte) (packet, NodeID, []byte, error) {
 	case neighborsPacket:
 	case neighborsPacket:
 		req = new(neighbors)
 		req = new(neighbors)
 	default:
 	default:
-		return nil, fromID, hash, fmt.Errorf("unknown type: %d", ptype)
+		return nil, fromKey, hash, fmt.Errorf("unknown type: %d", ptype)
 	}
 	}
 	s := rlp.NewStream(bytes.NewReader(sigdata[1:]), 0)
 	s := rlp.NewStream(bytes.NewReader(sigdata[1:]), 0)
 	err = s.Decode(req)
 	err = s.Decode(req)
-	return req, fromID, hash, err
+	return req, fromKey, hash, err
 }
 }
 
 
-func (req *ping) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {
+func (req *ping) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error {
 	if expired(req.Expiration) {
 	if expired(req.Expiration) {
 		return errExpired
 		return errExpired
 	}
 	}
+	key, err := decodePubkey(fromKey)
+	if err != nil {
+		return fmt.Errorf("invalid public key: %v", err)
+	}
 	t.send(from, pongPacket, &pong{
 	t.send(from, pongPacket, &pong{
 		To:         makeEndpoint(from, req.From.TCP),
 		To:         makeEndpoint(from, req.From.TCP),
 		ReplyTok:   mac,
 		ReplyTok:   mac,
 		Expiration: uint64(time.Now().Add(expiration).Unix()),
 		Expiration: uint64(time.Now().Add(expiration).Unix()),
 	})
 	})
-	t.handleReply(fromID, pingPacket, req)
-
-	// Add the node to the table. Before doing so, ensure that we have a recent enough pong
-	// recorded in the database so their findnode requests will be accepted later.
-	n := NewNode(fromID, from.IP, uint16(from.Port), req.From.TCP)
-	if time.Since(t.db.lastPongReceived(fromID)) > nodeDBNodeExpiration {
-		t.sendPing(fromID, from, func() { t.addThroughPing(n) })
+	n := wrapNode(enode.NewV4(key, from.IP, int(req.From.TCP), from.Port))
+	t.handleReply(n.ID(), pingPacket, req)
+	if time.Since(t.db.LastPongReceived(n.ID())) > bondExpiration {
+		t.sendPing(n.ID(), from, func() { t.addThroughPing(n) })
 	} else {
 	} else {
 		t.addThroughPing(n)
 		t.addThroughPing(n)
 	}
 	}
-	t.db.updateLastPingReceived(fromID, time.Now())
+	t.db.UpdateLastPingReceived(n.ID(), time.Now())
 	return nil
 	return nil
 }
 }
 
 
 func (req *ping) name() string { return "PING/v4" }
 func (req *ping) name() string { return "PING/v4" }
 
 
-func (req *pong) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {
+func (req *pong) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error {
 	if expired(req.Expiration) {
 	if expired(req.Expiration) {
 		return errExpired
 		return errExpired
 	}
 	}
+	fromID := fromKey.id()
 	if !t.handleReply(fromID, pongPacket, req) {
 	if !t.handleReply(fromID, pongPacket, req) {
 		return errUnsolicitedReply
 		return errUnsolicitedReply
 	}
 	}
-	t.db.updateLastPongReceived(fromID, time.Now())
+	t.db.UpdateLastPongReceived(fromID, time.Now())
 	return nil
 	return nil
 }
 }
 
 
 func (req *pong) name() string { return "PONG/v4" }
 func (req *pong) name() string { return "PONG/v4" }
 
 
-func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {
+func (req *findnode) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error {
 	if expired(req.Expiration) {
 	if expired(req.Expiration) {
 		return errExpired
 		return errExpired
 	}
 	}
-	if !t.db.hasBond(fromID) {
+	fromID := fromKey.id()
+	if time.Since(t.db.LastPongReceived(fromID)) > bondExpiration {
 		// No endpoint proof pong exists, we don't process the packet. This prevents an
 		// No endpoint proof pong exists, we don't process the packet. This prevents an
 		// attack vector where the discovery protocol could be used to amplify traffic in a
 		// attack vector where the discovery protocol could be used to amplify traffic in a
 		// DDOS attack. A malicious actor would send a findnode request with the IP address
 		// DDOS attack. A malicious actor would send a findnode request with the IP address
@@ -645,7 +667,7 @@ func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte
 		// findnode) to the victim.
 		// findnode) to the victim.
 		return errUnknownNode
 		return errUnknownNode
 	}
 	}
-	target := crypto.Keccak256Hash(req.Target[:])
+	target := enode.ID(crypto.Keccak256Hash(req.Target[:]))
 	t.mutex.Lock()
 	t.mutex.Lock()
 	closest := t.closest(target, bucketSize).entries
 	closest := t.closest(target, bucketSize).entries
 	t.mutex.Unlock()
 	t.mutex.Unlock()
@@ -655,7 +677,7 @@ func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte
 	// Send neighbors in chunks with at most maxNeighbors per packet
 	// Send neighbors in chunks with at most maxNeighbors per packet
 	// to stay below the 1280 byte limit.
 	// to stay below the 1280 byte limit.
 	for _, n := range closest {
 	for _, n := range closest {
-		if netutil.CheckRelayIP(from.IP, n.IP) == nil {
+		if netutil.CheckRelayIP(from.IP, n.IP()) == nil {
 			p.Nodes = append(p.Nodes, nodeToRPC(n))
 			p.Nodes = append(p.Nodes, nodeToRPC(n))
 		}
 		}
 		if len(p.Nodes) == maxNeighbors {
 		if len(p.Nodes) == maxNeighbors {
@@ -672,11 +694,11 @@ func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte
 
 
 func (req *findnode) name() string { return "FINDNODE/v4" }
 func (req *findnode) name() string { return "FINDNODE/v4" }
 
 
-func (req *neighbors) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {
+func (req *neighbors) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error {
 	if expired(req.Expiration) {
 	if expired(req.Expiration) {
 		return errExpired
 		return errExpired
 	}
 	}
-	if !t.handleReply(fromID, neighborsPacket, req) {
+	if !t.handleReply(fromKey.id(), neighborsPacket, req) {
 		return errUnsolicitedReply
 		return errUnsolicitedReply
 	}
 	}
 	return nil
 	return nil

+ 43 - 39
p2p/discover/udp_test.go

@@ -36,6 +36,7 @@ import (
 	"github.com/davecgh/go-spew/spew"
 	"github.com/davecgh/go-spew/spew"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
 )
 )
 
 
@@ -46,7 +47,7 @@ func init() {
 // shared test variables
 // shared test variables
 var (
 var (
 	futureExp          = uint64(time.Now().Add(10 * time.Hour).Unix())
 	futureExp          = uint64(time.Now().Add(10 * time.Hour).Unix())
-	testTarget         = NodeID{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}
+	testTarget         = encPubkey{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}
 	testRemote         = rpcEndpoint{IP: net.ParseIP("1.1.1.1").To4(), UDP: 1, TCP: 2}
 	testRemote         = rpcEndpoint{IP: net.ParseIP("1.1.1.1").To4(), UDP: 1, TCP: 2}
 	testLocalAnnounced = rpcEndpoint{IP: net.ParseIP("2.2.2.2").To4(), UDP: 3, TCP: 4}
 	testLocalAnnounced = rpcEndpoint{IP: net.ParseIP("2.2.2.2").To4(), UDP: 3, TCP: 4}
 	testLocal          = rpcEndpoint{IP: net.ParseIP("3.3.3.3").To4(), UDP: 5, TCP: 6}
 	testLocal          = rpcEndpoint{IP: net.ParseIP("3.3.3.3").To4(), UDP: 5, TCP: 6}
@@ -136,7 +137,7 @@ func TestUDP_pingTimeout(t *testing.T) {
 	defer test.table.Close()
 	defer test.table.Close()
 
 
 	toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
 	toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
-	toid := NodeID{1, 2, 3, 4}
+	toid := enode.ID{1, 2, 3, 4}
 	if err := test.udp.ping(toid, toaddr); err != errTimeout {
 	if err := test.udp.ping(toid, toaddr); err != errTimeout {
 		t.Error("expected timeout error, got", err)
 		t.Error("expected timeout error, got", err)
 	}
 	}
@@ -220,8 +221,8 @@ func TestUDP_findnodeTimeout(t *testing.T) {
 	defer test.table.Close()
 	defer test.table.Close()
 
 
 	toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
 	toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
-	toid := NodeID{1, 2, 3, 4}
-	target := NodeID{4, 5, 6, 7}
+	toid := enode.ID{1, 2, 3, 4}
+	target := encPubkey{4, 5, 6, 7}
 	result, err := test.udp.findnode(toid, toaddr, target)
 	result, err := test.udp.findnode(toid, toaddr, target)
 	if err != errTimeout {
 	if err != errTimeout {
 		t.Error("expected timeout error, got", err)
 		t.Error("expected timeout error, got", err)
@@ -238,28 +239,30 @@ func TestUDP_findnode(t *testing.T) {
 	// put a few nodes into the table. their exact
 	// put a few nodes into the table. their exact
 	// distribution shouldn't matter much, although we need to
 	// distribution shouldn't matter much, although we need to
 	// take care not to overflow any bucket.
 	// take care not to overflow any bucket.
-	targetHash := crypto.Keccak256Hash(testTarget[:])
-	nodes := &nodesByDistance{target: targetHash}
+	nodes := &nodesByDistance{target: testTarget.id()}
 	for i := 0; i < bucketSize; i++ {
 	for i := 0; i < bucketSize; i++ {
-		nodes.push(nodeAtDistance(test.table.self.sha, i+2), bucketSize)
+		key := newkey()
+		n := wrapNode(enode.NewV4(&key.PublicKey, net.IP{10, 13, 0, 1}, 0, i))
+		nodes.push(n, bucketSize)
 	}
 	}
 	test.table.stuff(nodes.entries)
 	test.table.stuff(nodes.entries)
 
 
 	// ensure there's a bond with the test node,
 	// ensure there's a bond with the test node,
 	// findnode won't be accepted otherwise.
 	// findnode won't be accepted otherwise.
-	test.table.db.updateLastPongReceived(PubkeyID(&test.remotekey.PublicKey), time.Now())
+	remoteID := encodePubkey(&test.remotekey.PublicKey).id()
+	test.table.db.UpdateLastPongReceived(remoteID, time.Now())
 
 
 	// check that closest neighbors are returned.
 	// check that closest neighbors are returned.
 	test.packetIn(nil, findnodePacket, &findnode{Target: testTarget, Expiration: futureExp})
 	test.packetIn(nil, findnodePacket, &findnode{Target: testTarget, Expiration: futureExp})
-	expected := test.table.closest(targetHash, bucketSize)
+	expected := test.table.closest(testTarget.id(), bucketSize)
 
 
-	waitNeighbors := func(want []*Node) {
+	waitNeighbors := func(want []*node) {
 		test.waitPacketOut(func(p *neighbors) {
 		test.waitPacketOut(func(p *neighbors) {
 			if len(p.Nodes) != len(want) {
 			if len(p.Nodes) != len(want) {
 				t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize)
 				t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize)
 			}
 			}
 			for i := range p.Nodes {
 			for i := range p.Nodes {
-				if p.Nodes[i].ID != want[i].ID {
+				if p.Nodes[i].ID.id() != want[i].ID() {
 					t.Errorf("result mismatch at %d:\n  got:  %v\n  want: %v", i, p.Nodes[i], expected.entries[i])
 					t.Errorf("result mismatch at %d:\n  got:  %v\n  want: %v", i, p.Nodes[i], expected.entries[i])
 				}
 				}
 			}
 			}
@@ -273,12 +276,13 @@ func TestUDP_findnodeMultiReply(t *testing.T) {
 	test := newUDPTest(t)
 	test := newUDPTest(t)
 	defer test.table.Close()
 	defer test.table.Close()
 
 
-	rid := PubkeyID(&test.remotekey.PublicKey)
-	test.table.db.updateLastPingReceived(rid, time.Now())
+	rid := enode.PubkeyToIDV4(&test.remotekey.PublicKey)
+	test.table.db.UpdateLastPingReceived(rid, time.Now())
 
 
 	// queue a pending findnode request
 	// queue a pending findnode request
-	resultc, errc := make(chan []*Node), make(chan error)
+	resultc, errc := make(chan []*node), make(chan error)
 	go func() {
 	go func() {
+		rid := encodePubkey(&test.remotekey.PublicKey).id()
 		ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget)
 		ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget)
 		if err != nil && len(ns) == 0 {
 		if err != nil && len(ns) == 0 {
 			errc <- err
 			errc <- err
@@ -296,11 +300,11 @@ func TestUDP_findnodeMultiReply(t *testing.T) {
 	})
 	})
 
 
 	// send the reply as two packets.
 	// send the reply as two packets.
-	list := []*Node{
-		MustParseNode("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304"),
-		MustParseNode("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"),
-		MustParseNode("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17"),
-		MustParseNode("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"),
+	list := []*node{
+		wrapNode(enode.MustParseV4("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304")),
+		wrapNode(enode.MustParseV4("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303")),
+		wrapNode(enode.MustParseV4("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17")),
+		wrapNode(enode.MustParseV4("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303")),
 	}
 	}
 	rpclist := make([]rpcNode, len(list))
 	rpclist := make([]rpcNode, len(list))
 	for i := range list {
 	for i := range list {
@@ -325,8 +329,8 @@ func TestUDP_findnodeMultiReply(t *testing.T) {
 
 
 func TestUDP_successfulPing(t *testing.T) {
 func TestUDP_successfulPing(t *testing.T) {
 	test := newUDPTest(t)
 	test := newUDPTest(t)
-	added := make(chan *Node, 1)
-	test.table.nodeAddedHook = func(n *Node) { added <- n }
+	added := make(chan *node, 1)
+	test.table.nodeAddedHook = func(n *node) { added <- n }
 	defer test.table.Close()
 	defer test.table.Close()
 
 
 	// The remote side sends a ping packet to initiate the exchange.
 	// The remote side sends a ping packet to initiate the exchange.
@@ -370,18 +374,18 @@ func TestUDP_successfulPing(t *testing.T) {
 	// pong packet.
 	// pong packet.
 	select {
 	select {
 	case n := <-added:
 	case n := <-added:
-		rid := PubkeyID(&test.remotekey.PublicKey)
-		if n.ID != rid {
-			t.Errorf("node has wrong ID: got %v, want %v", n.ID, rid)
+		rid := encodePubkey(&test.remotekey.PublicKey).id()
+		if n.ID() != rid {
+			t.Errorf("node has wrong ID: got %v, want %v", n.ID(), rid)
 		}
 		}
-		if !n.IP.Equal(test.remoteaddr.IP) {
-			t.Errorf("node has wrong IP: got %v, want: %v", n.IP, test.remoteaddr.IP)
+		if !n.IP().Equal(test.remoteaddr.IP) {
+			t.Errorf("node has wrong IP: got %v, want: %v", n.IP(), test.remoteaddr.IP)
 		}
 		}
-		if int(n.UDP) != test.remoteaddr.Port {
-			t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP, test.remoteaddr.Port)
+		if int(n.UDP()) != test.remoteaddr.Port {
+			t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP(), test.remoteaddr.Port)
 		}
 		}
-		if n.TCP != testRemote.TCP {
-			t.Errorf("node has wrong TCP port: got %v, want: %v", n.TCP, testRemote.TCP)
+		if n.TCP() != int(testRemote.TCP) {
+			t.Errorf("node has wrong TCP port: got %v, want: %v", n.TCP(), testRemote.TCP)
 		}
 		}
 	case <-time.After(2 * time.Second):
 	case <-time.After(2 * time.Second):
 		t.Errorf("node was not added within 2 seconds")
 		t.Errorf("node was not added within 2 seconds")
@@ -434,7 +438,7 @@ var testPackets = []struct {
 	{
 	{
 		input: "c7c44041b9f7c7e41934417ebac9a8e1a4c6298f74553f2fcfdcae6ed6fe53163eb3d2b52e39fe91831b8a927bf4fc222c3902202027e5e9eb812195f95d20061ef5cd31d502e47ecb61183f74a504fe04c51e73df81f25c4d506b26db4517490103f84eb840ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f8443b9a35582999983999999280dc62cc8255c73471e0a61da0c89acdc0e035e260add7fc0c04ad9ebf3919644c91cb247affc82b69bd2ca235c71eab8e49737c937a2c396",
 		input: "c7c44041b9f7c7e41934417ebac9a8e1a4c6298f74553f2fcfdcae6ed6fe53163eb3d2b52e39fe91831b8a927bf4fc222c3902202027e5e9eb812195f95d20061ef5cd31d502e47ecb61183f74a504fe04c51e73df81f25c4d506b26db4517490103f84eb840ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f8443b9a35582999983999999280dc62cc8255c73471e0a61da0c89acdc0e035e260add7fc0c04ad9ebf3919644c91cb247affc82b69bd2ca235c71eab8e49737c937a2c396",
 		wantPacket: &findnode{
 		wantPacket: &findnode{
-			Target:     MustHexID("ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f"),
+			Target:     hexEncPubkey("ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f"),
 			Expiration: 1136239445,
 			Expiration: 1136239445,
 			Rest:       []rlp.RawValue{{0x82, 0x99, 0x99}, {0x83, 0x99, 0x99, 0x99}},
 			Rest:       []rlp.RawValue{{0x82, 0x99, 0x99}, {0x83, 0x99, 0x99, 0x99}},
 		},
 		},
@@ -444,25 +448,25 @@ var testPackets = []struct {
 		wantPacket: &neighbors{
 		wantPacket: &neighbors{
 			Nodes: []rpcNode{
 			Nodes: []rpcNode{
 				{
 				{
-					ID:  MustHexID("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"),
+					ID:  hexEncPubkey("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"),
 					IP:  net.ParseIP("99.33.22.55").To4(),
 					IP:  net.ParseIP("99.33.22.55").To4(),
 					UDP: 4444,
 					UDP: 4444,
 					TCP: 4445,
 					TCP: 4445,
 				},
 				},
 				{
 				{
-					ID:  MustHexID("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"),
+					ID:  hexEncPubkey("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"),
 					IP:  net.ParseIP("1.2.3.4").To4(),
 					IP:  net.ParseIP("1.2.3.4").To4(),
 					UDP: 1,
 					UDP: 1,
 					TCP: 1,
 					TCP: 1,
 				},
 				},
 				{
 				{
-					ID:  MustHexID("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"),
+					ID:  hexEncPubkey("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"),
 					IP:  net.ParseIP("2001:db8:3c4d:15::abcd:ef12"),
 					IP:  net.ParseIP("2001:db8:3c4d:15::abcd:ef12"),
 					UDP: 3333,
 					UDP: 3333,
 					TCP: 3333,
 					TCP: 3333,
 				},
 				},
 				{
 				{
-					ID:  MustHexID("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"),
+					ID:  hexEncPubkey("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"),
 					IP:  net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"),
 					IP:  net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"),
 					UDP: 999,
 					UDP: 999,
 					TCP: 1000,
 					TCP: 1000,
@@ -476,14 +480,14 @@ var testPackets = []struct {
 
 
 func TestForwardCompatibility(t *testing.T) {
 func TestForwardCompatibility(t *testing.T) {
 	testkey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
 	testkey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
-	wantNodeID := PubkeyID(&testkey.PublicKey)
+	wantNodeKey := encodePubkey(&testkey.PublicKey)
 
 
 	for _, test := range testPackets {
 	for _, test := range testPackets {
 		input, err := hex.DecodeString(test.input)
 		input, err := hex.DecodeString(test.input)
 		if err != nil {
 		if err != nil {
 			t.Fatalf("invalid hex: %s", test.input)
 			t.Fatalf("invalid hex: %s", test.input)
 		}
 		}
-		packet, nodeid, _, err := decodePacket(input)
+		packet, nodekey, _, err := decodePacket(input)
 		if err != nil {
 		if err != nil {
 			t.Errorf("did not accept packet %s\n%v", test.input, err)
 			t.Errorf("did not accept packet %s\n%v", test.input, err)
 			continue
 			continue
@@ -491,8 +495,8 @@ func TestForwardCompatibility(t *testing.T) {
 		if !reflect.DeepEqual(packet, test.wantPacket) {
 		if !reflect.DeepEqual(packet, test.wantPacket) {
 			t.Errorf("got %s\nwant %s", spew.Sdump(packet), spew.Sdump(test.wantPacket))
 			t.Errorf("got %s\nwant %s", spew.Sdump(packet), spew.Sdump(test.wantPacket))
 		}
 		}
-		if nodeid != wantNodeID {
-			t.Errorf("got id %v\nwant id %v", nodeid, wantNodeID)
+		if nodekey != wantNodeKey {
+			t.Errorf("got id %v\nwant id %v", nodekey, wantNodeKey)
 		}
 		}
 	}
 	}
 }
 }

+ 86 - 40
p2p/enr/idscheme.go → p2p/enode/idscheme.go

@@ -14,57 +14,38 @@
 // You should have received a copy of the GNU Lesser General Public License
 // You should have received a copy of the GNU Lesser General Public License
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 
 
-package enr
+package enode
 
 
 import (
 import (
 	"crypto/ecdsa"
 	"crypto/ecdsa"
 	"fmt"
 	"fmt"
-	"sync"
+	"io"
 
 
 	"github.com/ethereum/go-ethereum/common/math"
 	"github.com/ethereum/go-ethereum/common/math"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto/sha3"
 	"github.com/ethereum/go-ethereum/crypto/sha3"
+	"github.com/ethereum/go-ethereum/p2p/enr"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
 )
 )
 
 
-// Registry of known identity schemes.
-var schemes sync.Map
-
-// An IdentityScheme is capable of verifying record signatures and
-// deriving node addresses.
-type IdentityScheme interface {
-	Verify(r *Record, sig []byte) error
-	NodeAddr(r *Record) []byte
-}
-
-// RegisterIdentityScheme adds an identity scheme to the global registry.
-func RegisterIdentityScheme(name string, scheme IdentityScheme) {
-	if _, loaded := schemes.LoadOrStore(name, scheme); loaded {
-		panic("identity scheme " + name + " already registered")
-	}
+// List of known secure identity schemes.
+var ValidSchemes = enr.SchemeMap{
+	"v4": V4ID{},
 }
 }
 
 
-// FindIdentityScheme resolves name to an identity scheme in the global registry.
-func FindIdentityScheme(name string) IdentityScheme {
-	s, ok := schemes.Load(name)
-	if !ok {
-		return nil
-	}
-	return s.(IdentityScheme)
+var ValidSchemesForTesting = enr.SchemeMap{
+	"v4":   V4ID{},
+	"null": NullID{},
 }
 }
 
 
 // v4ID is the "v4" identity scheme.
 // v4ID is the "v4" identity scheme.
-type v4ID struct{}
-
-func init() {
-	RegisterIdentityScheme("v4", v4ID{})
-}
+type V4ID struct{}
 
 
 // SignV4 signs a record using the v4 scheme.
 // SignV4 signs a record using the v4 scheme.
-func SignV4(r *Record, privkey *ecdsa.PrivateKey) error {
+func SignV4(r *enr.Record, privkey *ecdsa.PrivateKey) error {
 	// Copy r to avoid modifying it if signing fails.
 	// Copy r to avoid modifying it if signing fails.
 	cpy := *r
 	cpy := *r
-	cpy.Set(ID("v4"))
+	cpy.Set(enr.ID("v4"))
 	cpy.Set(Secp256k1(privkey.PublicKey))
 	cpy.Set(Secp256k1(privkey.PublicKey))
 
 
 	h := sha3.NewKeccak256()
 	h := sha3.NewKeccak256()
@@ -74,18 +55,13 @@ func SignV4(r *Record, privkey *ecdsa.PrivateKey) error {
 		return err
 		return err
 	}
 	}
 	sig = sig[:len(sig)-1] // remove v
 	sig = sig[:len(sig)-1] // remove v
-	if err = cpy.SetSig("v4", sig); err == nil {
+	if err = cpy.SetSig(V4ID{}, sig); err == nil {
 		*r = cpy
 		*r = cpy
 	}
 	}
 	return err
 	return err
 }
 }
 
 
-// s256raw is an unparsed secp256k1 public key entry.
-type s256raw []byte
-
-func (s256raw) ENRKey() string { return "secp256k1" }
-
-func (v4ID) Verify(r *Record, sig []byte) error {
+func (V4ID) Verify(r *enr.Record, sig []byte) error {
 	var entry s256raw
 	var entry s256raw
 	if err := r.Load(&entry); err != nil {
 	if err := r.Load(&entry); err != nil {
 		return err
 		return err
@@ -96,12 +72,12 @@ func (v4ID) Verify(r *Record, sig []byte) error {
 	h := sha3.NewKeccak256()
 	h := sha3.NewKeccak256()
 	rlp.Encode(h, r.AppendElements(nil))
 	rlp.Encode(h, r.AppendElements(nil))
 	if !crypto.VerifySignature(entry, h.Sum(nil), sig) {
 	if !crypto.VerifySignature(entry, h.Sum(nil), sig) {
-		return errInvalidSig
+		return enr.ErrInvalidSig
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-func (v4ID) NodeAddr(r *Record) []byte {
+func (V4ID) NodeAddr(r *enr.Record) []byte {
 	var pubkey Secp256k1
 	var pubkey Secp256k1
 	err := r.Load(&pubkey)
 	err := r.Load(&pubkey)
 	if err != nil {
 	if err != nil {
@@ -112,3 +88,73 @@ func (v4ID) NodeAddr(r *Record) []byte {
 	math.ReadBits(pubkey.Y, buf[32:])
 	math.ReadBits(pubkey.Y, buf[32:])
 	return crypto.Keccak256(buf)
 	return crypto.Keccak256(buf)
 }
 }
+
+// Secp256k1 is the "secp256k1" key, which holds a public key.
+type Secp256k1 ecdsa.PublicKey
+
+func (v Secp256k1) ENRKey() string { return "secp256k1" }
+
+// EncodeRLP implements rlp.Encoder.
+func (v Secp256k1) EncodeRLP(w io.Writer) error {
+	return rlp.Encode(w, crypto.CompressPubkey((*ecdsa.PublicKey)(&v)))
+}
+
+// DecodeRLP implements rlp.Decoder.
+func (v *Secp256k1) DecodeRLP(s *rlp.Stream) error {
+	buf, err := s.Bytes()
+	if err != nil {
+		return err
+	}
+	pk, err := crypto.DecompressPubkey(buf)
+	if err != nil {
+		return err
+	}
+	*v = (Secp256k1)(*pk)
+	return nil
+}
+
+// s256raw is an unparsed secp256k1 public key entry.
+type s256raw []byte
+
+func (s256raw) ENRKey() string { return "secp256k1" }
+
+// v4CompatID is a weaker and insecure version of the "v4" scheme which only checks for the
+// presence of a secp256k1 public key, but doesn't verify the signature.
+type v4CompatID struct {
+	V4ID
+}
+
+func (v4CompatID) Verify(r *enr.Record, sig []byte) error {
+	var pubkey Secp256k1
+	return r.Load(&pubkey)
+}
+
+func signV4Compat(r *enr.Record, pubkey *ecdsa.PublicKey) {
+	r.Set((*Secp256k1)(pubkey))
+	if err := r.SetSig(v4CompatID{}, []byte{}); err != nil {
+		panic(err)
+	}
+}
+
+// NullID is the "null" ENR identity scheme. This scheme stores the node
+// ID in the record without any signature.
+type NullID struct{}
+
+func (NullID) Verify(r *enr.Record, sig []byte) error {
+	return nil
+}
+
+func (NullID) NodeAddr(r *enr.Record) []byte {
+	var id ID
+	r.Load(enr.WithEntry("nulladdr", &id))
+	return id[:]
+}
+
+func SignNull(r *enr.Record, id ID) *Node {
+	r.Set(enr.ID("null"))
+	r.Set(enr.WithEntry("nulladdr", id))
+	if err := r.SetSig(NullID{}, []byte{}); err != nil {
+		panic(err)
+	}
+	return &Node{r: *r, id: id}
+}

+ 74 - 0
p2p/enode/idscheme_test.go

@@ -0,0 +1,74 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package enode
+
+import (
+	"bytes"
+	"crypto/ecdsa"
+	"encoding/hex"
+	"math/big"
+	"testing"
+
+	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/ethereum/go-ethereum/p2p/enr"
+	"github.com/ethereum/go-ethereum/rlp"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+var (
+	privkey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+	pubkey     = &privkey.PublicKey
+)
+
+func TestEmptyNodeID(t *testing.T) {
+	var r enr.Record
+	if addr := ValidSchemes.NodeAddr(&r); addr != nil {
+		t.Errorf("wrong address on empty record: got %v, want %v", addr, nil)
+	}
+
+	require.NoError(t, SignV4(&r, privkey))
+	expected := "a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7"
+	assert.Equal(t, expected, hex.EncodeToString(ValidSchemes.NodeAddr(&r)))
+}
+
+// Checks that failure to sign leaves the record unmodified.
+func TestSignError(t *testing.T) {
+	invalidKey := &ecdsa.PrivateKey{D: new(big.Int), PublicKey: *pubkey}
+
+	var r enr.Record
+	emptyEnc, _ := rlp.EncodeToBytes(&r)
+	if err := SignV4(&r, invalidKey); err == nil {
+		t.Fatal("expected error from SignV4")
+	}
+	newEnc, _ := rlp.EncodeToBytes(&r)
+	if !bytes.Equal(newEnc, emptyEnc) {
+		t.Fatal("record modified even though signing failed")
+	}
+}
+
+// TestGetSetSecp256k1 tests encoding/decoding and setting/getting of the Secp256k1 key.
+func TestGetSetSecp256k1(t *testing.T) {
+	var r enr.Record
+	if err := SignV4(&r, privkey); err != nil {
+		t.Fatal(err)
+	}
+
+	var pk Secp256k1
+	require.NoError(t, r.Load(&pk))
+	assert.EqualValues(t, pubkey, &pk)
+}

+ 248 - 0
p2p/enode/node.go

@@ -0,0 +1,248 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package enode
+
+import (
+	"crypto/ecdsa"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"math/bits"
+	"math/rand"
+	"net"
+	"strings"
+
+	"github.com/ethereum/go-ethereum/p2p/enr"
+)
+
+// Node represents a host on the network.
+type Node struct {
+	r  enr.Record
+	id ID
+}
+
+// New wraps a node record. The record must be valid according to the given
+// identity scheme.
+func New(validSchemes enr.IdentityScheme, r *enr.Record) (*Node, error) {
+	if err := r.VerifySignature(validSchemes); err != nil {
+		return nil, err
+	}
+	node := &Node{r: *r}
+	if n := copy(node.id[:], validSchemes.NodeAddr(&node.r)); n != len(ID{}) {
+		return nil, fmt.Errorf("invalid node ID length %d, need %d", n, len(ID{}))
+	}
+	return node, nil
+}
+
+// ID returns the node identifier.
+func (n *Node) ID() ID {
+	return n.id
+}
+
+// Seq returns the sequence number of the underlying record.
+func (n *Node) Seq() uint64 {
+	return n.r.Seq()
+}
+
+// Incomplete returns true for nodes with no IP address.
+func (n *Node) Incomplete() bool {
+	return n.IP() == nil
+}
+
+// Load retrieves an entry from the underlying record.
+func (n *Node) Load(k enr.Entry) error {
+	return n.r.Load(k)
+}
+
+// IP returns the IP address of the node.
+func (n *Node) IP() net.IP {
+	var ip net.IP
+	n.Load((*enr.IP)(&ip))
+	return ip
+}
+
+// UDP returns the UDP port of the node.
+func (n *Node) UDP() int {
+	var port enr.UDP
+	n.Load(&port)
+	return int(port)
+}
+
+// UDP returns the TCP port of the node.
+func (n *Node) TCP() int {
+	var port enr.TCP
+	n.Load(&port)
+	return int(port)
+}
+
+// Pubkey returns the secp256k1 public key of the node, if present.
+func (n *Node) Pubkey() *ecdsa.PublicKey {
+	var key ecdsa.PublicKey
+	if n.Load((*Secp256k1)(&key)) != nil {
+		return nil
+	}
+	return &key
+}
+
+// checks whether n is a valid complete node.
+func (n *Node) ValidateComplete() error {
+	if n.Incomplete() {
+		return errors.New("incomplete node")
+	}
+	if n.UDP() == 0 {
+		return errors.New("missing UDP port")
+	}
+	ip := n.IP()
+	if ip.IsMulticast() || ip.IsUnspecified() {
+		return errors.New("invalid IP (multicast/unspecified)")
+	}
+	// Validate the node key (on curve, etc.).
+	var key Secp256k1
+	return n.Load(&key)
+}
+
+// The string representation of a Node is a URL.
+// Please see ParseNode for a description of the format.
+func (n *Node) String() string {
+	return n.v4URL()
+}
+
+// MarshalText implements encoding.TextMarshaler.
+func (n *Node) MarshalText() ([]byte, error) {
+	return []byte(n.v4URL()), nil
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (n *Node) UnmarshalText(text []byte) error {
+	dec, err := ParseV4(string(text))
+	if err == nil {
+		*n = *dec
+	}
+	return err
+}
+
+// ID is a unique identifier for each node.
+type ID [32]byte
+
+// Bytes returns a byte slice representation of the ID
+func (n ID) Bytes() []byte {
+	return n[:]
+}
+
+// ID prints as a long hexadecimal number.
+func (n ID) String() string {
+	return fmt.Sprintf("%x", n[:])
+}
+
+// The Go syntax representation of a ID is a call to HexID.
+func (n ID) GoString() string {
+	return fmt.Sprintf("enode.HexID(\"%x\")", n[:])
+}
+
+// TerminalString returns a shortened hex string for terminal logging.
+func (n ID) TerminalString() string {
+	return hex.EncodeToString(n[:8])
+}
+
+// MarshalText implements the encoding.TextMarshaler interface.
+func (n ID) MarshalText() ([]byte, error) {
+	return []byte(hex.EncodeToString(n[:])), nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (n *ID) UnmarshalText(text []byte) error {
+	id, err := parseID(string(text))
+	if err != nil {
+		return err
+	}
+	*n = id
+	return nil
+}
+
+// HexID converts a hex string to an ID.
+// The string may be prefixed with 0x.
+// It panics if the string is not a valid ID.
+func HexID(in string) ID {
+	id, err := parseID(in)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+func parseID(in string) (ID, error) {
+	var id ID
+	b, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
+	if err != nil {
+		return id, err
+	} else if len(b) != len(id) {
+		return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2)
+	}
+	copy(id[:], b)
+	return id, nil
+}
+
+// DistCmp compares the distances a->target and b->target.
+// Returns -1 if a is closer to target, 1 if b is closer to target
+// and 0 if they are equal.
+func DistCmp(target, a, b ID) int {
+	for i := range target {
+		da := a[i] ^ target[i]
+		db := b[i] ^ target[i]
+		if da > db {
+			return 1
+		} else if da < db {
+			return -1
+		}
+	}
+	return 0
+}
+
+// LogDist returns the logarithmic distance between a and b, log2(a ^ b).
+func LogDist(a, b ID) int {
+	lz := 0
+	for i := range a {
+		x := a[i] ^ b[i]
+		if x == 0 {
+			lz += 8
+		} else {
+			lz += bits.LeadingZeros8(x)
+			break
+		}
+	}
+	return len(a)*8 - lz
+}
+
+// RandomID returns a random ID b such that logdist(a, b) == n.
+func RandomID(a ID, n int) (b ID) {
+	if n == 0 {
+		return a
+	}
+	// flip bit at position n, fill the rest with random bits
+	b = a
+	pos := len(a) - n/8 - 1
+	bit := byte(0x01) << (byte(n%8) - 1)
+	if bit == 0 {
+		pos++
+		bit = 0x80
+	}
+	b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits
+	for i := pos + 1; i < len(a); i++ {
+		b[i] = byte(rand.Intn(255))
+	}
+	return b
+}

+ 62 - 0
p2p/enode/node_test.go

@@ -0,0 +1,62 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package enode
+
+import (
+	"encoding/hex"
+	"fmt"
+	"testing"
+
+	"github.com/ethereum/go-ethereum/p2p/enr"
+	"github.com/ethereum/go-ethereum/rlp"
+	"github.com/stretchr/testify/assert"
+)
+
+var pyRecord, _ = hex.DecodeString("f884b8407098ad865b00a582051940cb9cf36836572411a47278783077011599ed5cd16b76f2635f4e234738f30813a89eb9137e3e3df5266e3a1f11df72ecf1145ccb9c01826964827634826970847f00000189736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31388375647082765f")
+
+// TestPythonInterop checks that we can decode and verify a record produced by the Python
+// implementation.
+func TestPythonInterop(t *testing.T) {
+	var r enr.Record
+	if err := rlp.DecodeBytes(pyRecord, &r); err != nil {
+		t.Fatalf("can't decode: %v", err)
+	}
+	n, err := New(ValidSchemes, &r)
+	if err != nil {
+		t.Fatalf("can't verify record: %v", err)
+	}
+
+	var (
+		wantID  = HexID("a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7")
+		wantSeq = uint64(1)
+		wantIP  = enr.IP{127, 0, 0, 1}
+		wantUDP = enr.UDP(30303)
+	)
+	if n.Seq() != wantSeq {
+		t.Errorf("wrong seq: got %d, want %d", n.Seq(), wantSeq)
+	}
+	if n.ID() != wantID {
+		t.Errorf("wrong id: got %x, want %x", n.ID(), wantID)
+	}
+	want := map[enr.Entry]interface{}{new(enr.IP): &wantIP, new(enr.UDP): &wantUDP}
+	for k, v := range want {
+		desc := fmt.Sprintf("loading key %q", k.ENRKey())
+		if assert.NoError(t, n.Load(k), desc) {
+			assert.Equal(t, k, v, desc)
+		}
+	}
+}

+ 69 - 90
p2p/discover/database.go → p2p/enode/nodedb.go

@@ -14,20 +14,17 @@
 // You should have received a copy of the GNU Lesser General Public License
 // You should have received a copy of the GNU Lesser General Public License
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 
 
-// Contains the node database, storing previously seen nodes and any collected
-// metadata about them for QoS purposes.
-
-package discover
+package enode
 
 
 import (
 import (
 	"bytes"
 	"bytes"
 	"crypto/rand"
 	"crypto/rand"
 	"encoding/binary"
 	"encoding/binary"
+	"fmt"
 	"os"
 	"os"
 	"sync"
 	"sync"
 	"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/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/syndtr/goleveldb/leveldb"
 	"github.com/syndtr/goleveldb/leveldb"
@@ -39,16 +36,16 @@ import (
 )
 )
 
 
 var (
 var (
-	nodeDBNilNodeID      = NodeID{}       // Special node ID to use as a nil element.
+	nodeDBNilID          = ID{}           // Special node ID to use as a nil element.
 	nodeDBNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped.
 	nodeDBNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped.
 	nodeDBCleanupCycle   = time.Hour      // Time period for running the expiration task.
 	nodeDBCleanupCycle   = time.Hour      // Time period for running the expiration task.
-	nodeDBVersion        = 5
+	nodeDBVersion        = 6
 )
 )
 
 
-// nodeDB stores all nodes we know about.
-type nodeDB struct {
+// DB is the node database, storing previously seen nodes and any collected metadata about
+// them for QoS purposes.
+type DB struct {
 	lvl    *leveldb.DB   // Interface to the database itself
 	lvl    *leveldb.DB   // Interface to the database itself
-	self   NodeID        // Own node id to prevent adding it into the database
 	runner sync.Once     // Ensures we can start at most one expirer
 	runner sync.Once     // Ensures we can start at most one expirer
 	quit   chan struct{} // Channel to signal the expiring thread to stop
 	quit   chan struct{} // Channel to signal the expiring thread to stop
 }
 }
@@ -64,33 +61,27 @@ var (
 	nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail"
 	nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail"
 )
 )
 
 
-// newNodeDB creates a new node database for storing and retrieving infos about
-// known peers in the network. If no path is given, an in-memory, temporary
-// database is constructed.
-func newNodeDB(path string, version int, self NodeID) (*nodeDB, error) {
+// OpenDB opens a node database for storing and retrieving infos about known peers in the
+// network. If no path is given an in-memory, temporary database is constructed.
+func OpenDB(path string) (*DB, error) {
 	if path == "" {
 	if path == "" {
-		return newMemoryNodeDB(self)
+		return newMemoryDB()
 	}
 	}
-	return newPersistentNodeDB(path, version, self)
+	return newPersistentDB(path)
 }
 }
 
 
-// newMemoryNodeDB creates a new in-memory node database without a persistent
-// backend.
-func newMemoryNodeDB(self NodeID) (*nodeDB, error) {
+// newMemoryNodeDB creates a new in-memory node database without a persistent backend.
+func newMemoryDB() (*DB, error) {
 	db, err := leveldb.Open(storage.NewMemStorage(), nil)
 	db, err := leveldb.Open(storage.NewMemStorage(), nil)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	return &nodeDB{
-		lvl:  db,
-		self: self,
-		quit: make(chan struct{}),
-	}, nil
+	return &DB{lvl: db, quit: make(chan struct{})}, nil
 }
 }
 
 
 // newPersistentNodeDB creates/opens a leveldb backed persistent node database,
 // newPersistentNodeDB creates/opens a leveldb backed persistent node database,
 // also flushing its contents in case of a version mismatch.
 // also flushing its contents in case of a version mismatch.
-func newPersistentNodeDB(path string, version int, self NodeID) (*nodeDB, error) {
+func newPersistentDB(path string) (*DB, error) {
 	opts := &opt.Options{OpenFilesCacheCapacity: 5}
 	opts := &opt.Options{OpenFilesCacheCapacity: 5}
 	db, err := leveldb.OpenFile(path, opts)
 	db, err := leveldb.OpenFile(path, opts)
 	if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted {
 	if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted {
@@ -102,7 +93,7 @@ func newPersistentNodeDB(path string, version int, self NodeID) (*nodeDB, error)
 	// The nodes contained in the cache correspond to a certain protocol version.
 	// The nodes contained in the cache correspond to a certain protocol version.
 	// Flush all nodes if the version doesn't match.
 	// Flush all nodes if the version doesn't match.
 	currentVer := make([]byte, binary.MaxVarintLen64)
 	currentVer := make([]byte, binary.MaxVarintLen64)
-	currentVer = currentVer[:binary.PutVarint(currentVer, int64(version))]
+	currentVer = currentVer[:binary.PutVarint(currentVer, int64(nodeDBVersion))]
 
 
 	blob, err := db.Get(nodeDBVersionKey, nil)
 	blob, err := db.Get(nodeDBVersionKey, nil)
 	switch err {
 	switch err {
@@ -120,30 +111,26 @@ func newPersistentNodeDB(path string, version int, self NodeID) (*nodeDB, error)
 			if err = os.RemoveAll(path); err != nil {
 			if err = os.RemoveAll(path); err != nil {
 				return nil, err
 				return nil, err
 			}
 			}
-			return newPersistentNodeDB(path, version, self)
+			return newPersistentDB(path)
 		}
 		}
 	}
 	}
-	return &nodeDB{
-		lvl:  db,
-		self: self,
-		quit: make(chan struct{}),
-	}, nil
+	return &DB{lvl: db, quit: make(chan struct{})}, nil
 }
 }
 
 
 // makeKey generates the leveldb key-blob from a node id and its particular
 // makeKey generates the leveldb key-blob from a node id and its particular
 // field of interest.
 // field of interest.
-func makeKey(id NodeID, field string) []byte {
-	if bytes.Equal(id[:], nodeDBNilNodeID[:]) {
+func makeKey(id ID, field string) []byte {
+	if bytes.Equal(id[:], nodeDBNilID[:]) {
 		return []byte(field)
 		return []byte(field)
 	}
 	}
 	return append(nodeDBItemPrefix, append(id[:], field...)...)
 	return append(nodeDBItemPrefix, append(id[:], field...)...)
 }
 }
 
 
 // splitKey tries to split a database key into a node id and a field part.
 // splitKey tries to split a database key into a node id and a field part.
-func splitKey(key []byte) (id NodeID, field string) {
+func splitKey(key []byte) (id ID, field string) {
 	// If the key is not of a node, return it plainly
 	// If the key is not of a node, return it plainly
 	if !bytes.HasPrefix(key, nodeDBItemPrefix) {
 	if !bytes.HasPrefix(key, nodeDBItemPrefix) {
-		return NodeID{}, string(key)
+		return ID{}, string(key)
 	}
 	}
 	// Otherwise split the id and field
 	// Otherwise split the id and field
 	item := key[len(nodeDBItemPrefix):]
 	item := key[len(nodeDBItemPrefix):]
@@ -155,7 +142,7 @@ func splitKey(key []byte) (id NodeID, field string) {
 
 
 // fetchInt64 retrieves an integer instance associated with a particular
 // fetchInt64 retrieves an integer instance associated with a particular
 // database key.
 // database key.
-func (db *nodeDB) fetchInt64(key []byte) int64 {
+func (db *DB) fetchInt64(key []byte) int64 {
 	blob, err := db.lvl.Get(key, nil)
 	blob, err := db.lvl.Get(key, nil)
 	if err != nil {
 	if err != nil {
 		return 0
 		return 0
@@ -169,39 +156,43 @@ func (db *nodeDB) fetchInt64(key []byte) int64 {
 
 
 // storeInt64 update a specific database entry to the current time instance as a
 // storeInt64 update a specific database entry to the current time instance as a
 // unix timestamp.
 // unix timestamp.
-func (db *nodeDB) storeInt64(key []byte, n int64) error {
+func (db *DB) storeInt64(key []byte, n int64) error {
 	blob := make([]byte, binary.MaxVarintLen64)
 	blob := make([]byte, binary.MaxVarintLen64)
 	blob = blob[:binary.PutVarint(blob, n)]
 	blob = blob[:binary.PutVarint(blob, n)]
 
 
 	return db.lvl.Put(key, blob, nil)
 	return db.lvl.Put(key, blob, nil)
 }
 }
 
 
-// node retrieves a node with a given id from the database.
-func (db *nodeDB) node(id NodeID) *Node {
+// Node retrieves a node with a given id from the database.
+func (db *DB) Node(id ID) *Node {
 	blob, err := db.lvl.Get(makeKey(id, nodeDBDiscoverRoot), nil)
 	blob, err := db.lvl.Get(makeKey(id, nodeDBDiscoverRoot), nil)
 	if err != nil {
 	if err != nil {
 		return nil
 		return nil
 	}
 	}
+	return mustDecodeNode(id[:], blob)
+}
+
+func mustDecodeNode(id, data []byte) *Node {
 	node := new(Node)
 	node := new(Node)
-	if err := rlp.DecodeBytes(blob, node); err != nil {
-		log.Error("Failed to decode node RLP", "err", err)
-		return nil
+	if err := rlp.DecodeBytes(data, &node.r); err != nil {
+		panic(fmt.Errorf("p2p/enode: can't decode node %x in DB: %v", id, err))
 	}
 	}
-	node.sha = crypto.Keccak256Hash(node.ID[:])
+	// Restore node id cache.
+	copy(node.id[:], id)
 	return node
 	return node
 }
 }
 
 
-// updateNode inserts - potentially overwriting - a node into the peer database.
-func (db *nodeDB) updateNode(node *Node) error {
-	blob, err := rlp.EncodeToBytes(node)
+// UpdateNode inserts - potentially overwriting - a node into the peer database.
+func (db *DB) UpdateNode(node *Node) error {
+	blob, err := rlp.EncodeToBytes(&node.r)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	return db.lvl.Put(makeKey(node.ID, nodeDBDiscoverRoot), blob, nil)
+	return db.lvl.Put(makeKey(node.ID(), nodeDBDiscoverRoot), blob, nil)
 }
 }
 
 
-// deleteNode deletes all information/keys associated with a node.
-func (db *nodeDB) deleteNode(id NodeID) error {
+// DeleteNode deletes all information/keys associated with a node.
+func (db *DB) DeleteNode(id ID) error {
 	deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil)
 	deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil)
 	for deleter.Next() {
 	for deleter.Next() {
 		if err := db.lvl.Delete(deleter.Key(), nil); err != nil {
 		if err := db.lvl.Delete(deleter.Key(), nil); err != nil {
@@ -220,13 +211,13 @@ func (db *nodeDB) deleteNode(id NodeID) error {
 // it would require significant overhead to exactly trace the first successful
 // it would require significant overhead to exactly trace the first successful
 // convergence, it's simpler to "ensure" the correct state when an appropriate
 // convergence, it's simpler to "ensure" the correct state when an appropriate
 // condition occurs (i.e. a successful bonding), and discard further events.
 // condition occurs (i.e. a successful bonding), and discard further events.
-func (db *nodeDB) ensureExpirer() {
+func (db *DB) ensureExpirer() {
 	db.runner.Do(func() { go db.expirer() })
 	db.runner.Do(func() { go db.expirer() })
 }
 }
 
 
 // expirer should be started in a go routine, and is responsible for looping ad
 // expirer should be started in a go routine, and is responsible for looping ad
 // infinitum and dropping stale data from the database.
 // infinitum and dropping stale data from the database.
-func (db *nodeDB) expirer() {
+func (db *DB) expirer() {
 	tick := time.NewTicker(nodeDBCleanupCycle)
 	tick := time.NewTicker(nodeDBCleanupCycle)
 	defer tick.Stop()
 	defer tick.Stop()
 	for {
 	for {
@@ -243,7 +234,7 @@ func (db *nodeDB) expirer() {
 
 
 // expireNodes iterates over the database and deletes all nodes that have not
 // expireNodes iterates over the database and deletes all nodes that have not
 // been seen (i.e. received a pong from) for some allotted time.
 // been seen (i.e. received a pong from) for some allotted time.
-func (db *nodeDB) expireNodes() error {
+func (db *DB) expireNodes() error {
 	threshold := time.Now().Add(-nodeDBNodeExpiration)
 	threshold := time.Now().Add(-nodeDBNodeExpiration)
 
 
 	// Find discovered nodes that are older than the allowance
 	// Find discovered nodes that are older than the allowance
@@ -257,60 +248,56 @@ func (db *nodeDB) expireNodes() error {
 			continue
 			continue
 		}
 		}
 		// Skip the node if not expired yet (and not self)
 		// Skip the node if not expired yet (and not self)
-		if !bytes.Equal(id[:], db.self[:]) {
-			if seen := db.lastPongReceived(id); seen.After(threshold) {
-				continue
-			}
+		if seen := db.LastPongReceived(id); seen.After(threshold) {
+			continue
 		}
 		}
 		// Otherwise delete all associated information
 		// Otherwise delete all associated information
-		db.deleteNode(id)
+		db.DeleteNode(id)
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-// lastPingReceived retrieves the time of the last ping packet sent by the remote node.
-func (db *nodeDB) lastPingReceived(id NodeID) time.Time {
+// LastPingReceived retrieves the time of the last ping packet received from
+// a remote node.
+func (db *DB) LastPingReceived(id ID) time.Time {
 	return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPing)), 0)
 	return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPing)), 0)
 }
 }
 
 
-// updateLastPing updates the last time remote node pinged us.
-func (db *nodeDB) updateLastPingReceived(id NodeID, instance time.Time) error {
+// UpdateLastPingReceived updates the last time we tried contacting a remote node.
+func (db *DB) UpdateLastPingReceived(id ID, instance time.Time) error {
 	return db.storeInt64(makeKey(id, nodeDBDiscoverPing), instance.Unix())
 	return db.storeInt64(makeKey(id, nodeDBDiscoverPing), instance.Unix())
 }
 }
 
 
-// lastPongReceived retrieves the time of the last successful pong from remote node.
-func (db *nodeDB) lastPongReceived(id NodeID) time.Time {
+// LastPongReceived retrieves the time of the last successful pong from remote node.
+func (db *DB) LastPongReceived(id ID) time.Time {
+	// Launch expirer
+	db.ensureExpirer()
 	return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPong)), 0)
 	return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPong)), 0)
 }
 }
 
 
-// hasBond reports whether the given node is considered bonded.
-func (db *nodeDB) hasBond(id NodeID) bool {
-	return time.Since(db.lastPongReceived(id)) < nodeDBNodeExpiration
-}
-
-// updateLastPongReceived updates the last pong time of a node.
-func (db *nodeDB) updateLastPongReceived(id NodeID, instance time.Time) error {
+// UpdateLastPongReceived updates the last pong time of a node.
+func (db *DB) UpdateLastPongReceived(id ID, instance time.Time) error {
 	return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix())
 	return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix())
 }
 }
 
 
-// findFails retrieves the number of findnode failures since bonding.
-func (db *nodeDB) findFails(id NodeID) int {
+// FindFails retrieves the number of findnode failures since bonding.
+func (db *DB) FindFails(id ID) int {
 	return int(db.fetchInt64(makeKey(id, nodeDBDiscoverFindFails)))
 	return int(db.fetchInt64(makeKey(id, nodeDBDiscoverFindFails)))
 }
 }
 
 
-// updateFindFails updates the number of findnode failures since bonding.
-func (db *nodeDB) updateFindFails(id NodeID, fails int) error {
+// UpdateFindFails updates the number of findnode failures since bonding.
+func (db *DB) UpdateFindFails(id ID, fails int) error {
 	return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails))
 	return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails))
 }
 }
 
 
-// querySeeds retrieves random nodes to be used as potential seed nodes
+// QuerySeeds retrieves random nodes to be used as potential seed nodes
 // for bootstrapping.
 // for bootstrapping.
-func (db *nodeDB) querySeeds(n int, maxAge time.Duration) []*Node {
+func (db *DB) QuerySeeds(n int, maxAge time.Duration) []*Node {
 	var (
 	var (
 		now   = time.Now()
 		now   = time.Now()
 		nodes = make([]*Node, 0, n)
 		nodes = make([]*Node, 0, n)
 		it    = db.lvl.NewIterator(nil, nil)
 		it    = db.lvl.NewIterator(nil, nil)
-		id    NodeID
+		id    ID
 	)
 	)
 	defer it.Release()
 	defer it.Release()
 
 
@@ -329,14 +316,11 @@ seek:
 			id[0] = 0
 			id[0] = 0
 			continue seek // iterator exhausted
 			continue seek // iterator exhausted
 		}
 		}
-		if n.ID == db.self {
-			continue seek
-		}
-		if now.Sub(db.lastPongReceived(n.ID)) > maxAge {
+		if now.Sub(db.LastPongReceived(n.ID())) > maxAge {
 			continue seek
 			continue seek
 		}
 		}
 		for i := range nodes {
 		for i := range nodes {
-			if nodes[i].ID == n.ID {
+			if nodes[i].ID() == n.ID() {
 				continue seek // duplicate
 				continue seek // duplicate
 			}
 			}
 		}
 		}
@@ -353,18 +337,13 @@ func nextNode(it iterator.Iterator) *Node {
 		if field != nodeDBDiscoverRoot {
 		if field != nodeDBDiscoverRoot {
 			continue
 			continue
 		}
 		}
-		var n Node
-		if err := rlp.DecodeBytes(it.Value(), &n); err != nil {
-			log.Warn("Failed to decode node RLP", "id", id, "err", err)
-			continue
-		}
-		return &n
+		return mustDecodeNode(id[:], it.Value())
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
 // close flushes and closes the database files.
 // close flushes and closes the database files.
-func (db *nodeDB) close() {
+func (db *DB) Close() {
 	close(db.quit)
 	close(db.quit)
 	db.lvl.Close()
 	db.lvl.Close()
 }
 }

+ 105 - 113
p2p/discover/database_test.go → p2p/enode/nodedb_test.go

@@ -14,10 +14,11 @@
 // You should have received a copy of the GNU Lesser General Public License
 // You should have received a copy of the GNU Lesser General Public License
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 
 
-package discover
+package enode
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"fmt"
 	"io/ioutil"
 	"io/ioutil"
 	"net"
 	"net"
 	"os"
 	"os"
@@ -28,24 +29,21 @@ import (
 )
 )
 
 
 var nodeDBKeyTests = []struct {
 var nodeDBKeyTests = []struct {
-	id    NodeID
+	id    ID
 	field string
 	field string
 	key   []byte
 	key   []byte
 }{
 }{
 	{
 	{
-		id:    NodeID{},
+		id:    ID{},
 		field: "version",
 		field: "version",
 		key:   []byte{0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e}, // field
 		key:   []byte{0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e}, // field
 	},
 	},
 	{
 	{
-		id:    MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		id:    HexID("51232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
 		field: ":discover",
 		field: ":discover",
-		key: []byte{0x6e, 0x3a, // prefix
-			0x1d, 0xd9, 0xd6, 0x5c, 0x45, 0x52, 0xb5, 0xeb, // node id
-			0x43, 0xd5, 0xad, 0x55, 0xa2, 0xee, 0x3f, 0x56, //
-			0xc6, 0xcb, 0xc1, 0xc6, 0x4a, 0x5c, 0x8d, 0x65, //
-			0x9f, 0x51, 0xfc, 0xd5, 0x1b, 0xac, 0xe2, 0x43, //
-			0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, //
+		key: []byte{
+			0x6e, 0x3a, // prefix
+			0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, // node id
 			0x2b, 0x29, 0xb5, 0x4b, 0x81, 0xcd, 0xef, 0xb9, //
 			0x2b, 0x29, 0xb5, 0x4b, 0x81, 0xcd, 0xef, 0xb9, //
 			0xb3, 0xe9, 0xc3, 0x7d, 0x7f, 0xd5, 0xf6, 0x32, //
 			0xb3, 0xe9, 0xc3, 0x7d, 0x7f, 0xd5, 0xf6, 0x32, //
 			0x70, 0xbc, 0xc9, 0xe1, 0xa6, 0xf6, 0xa4, 0x39, //
 			0x70, 0xbc, 0xc9, 0xe1, 0xa6, 0xf6, 0xa4, 0x39, //
@@ -54,7 +52,7 @@ var nodeDBKeyTests = []struct {
 	},
 	},
 }
 }
 
 
-func TestNodeDBKeys(t *testing.T) {
+func TestDBKeys(t *testing.T) {
 	for i, tt := range nodeDBKeyTests {
 	for i, tt := range nodeDBKeyTests {
 		if key := makeKey(tt.id, tt.field); !bytes.Equal(key, tt.key) {
 		if key := makeKey(tt.id, tt.field); !bytes.Equal(key, tt.key) {
 			t.Errorf("make test %d: key mismatch: have 0x%x, want 0x%x", i, key, tt.key)
 			t.Errorf("make test %d: key mismatch: have 0x%x, want 0x%x", i, key, tt.key)
@@ -78,9 +76,9 @@ var nodeDBInt64Tests = []struct {
 	{key: []byte{0x03}, value: 3},
 	{key: []byte{0x03}, value: 3},
 }
 }
 
 
-func TestNodeDBInt64(t *testing.T) {
-	db, _ := newNodeDB("", nodeDBVersion, NodeID{})
-	defer db.close()
+func TestDBInt64(t *testing.T) {
+	db, _ := OpenDB("")
+	defer db.Close()
 
 
 	tests := nodeDBInt64Tests
 	tests := nodeDBInt64Tests
 	for i := 0; i < len(tests); i++ {
 	for i := 0; i < len(tests); i++ {
@@ -101,9 +99,9 @@ func TestNodeDBInt64(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func TestNodeDBFetchStore(t *testing.T) {
-	node := NewNode(
-		MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+func TestDBFetchStore(t *testing.T) {
+	node := NewV4(
+		hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
 		net.IP{192, 168, 0, 1},
 		net.IP{192, 168, 0, 1},
 		30303,
 		30303,
 		30303,
 		30303,
@@ -111,47 +109,47 @@ func TestNodeDBFetchStore(t *testing.T) {
 	inst := time.Now()
 	inst := time.Now()
 	num := 314
 	num := 314
 
 
-	db, _ := newNodeDB("", nodeDBVersion, NodeID{})
-	defer db.close()
+	db, _ := OpenDB("")
+	defer db.Close()
 
 
 	// Check fetch/store operations on a node ping object
 	// Check fetch/store operations on a node ping object
-	if stored := db.lastPingReceived(node.ID); stored.Unix() != 0 {
+	if stored := db.LastPingReceived(node.ID()); stored.Unix() != 0 {
 		t.Errorf("ping: non-existing object: %v", stored)
 		t.Errorf("ping: non-existing object: %v", stored)
 	}
 	}
-	if err := db.updateLastPingReceived(node.ID, inst); err != nil {
+	if err := db.UpdateLastPingReceived(node.ID(), inst); err != nil {
 		t.Errorf("ping: failed to update: %v", err)
 		t.Errorf("ping: failed to update: %v", err)
 	}
 	}
-	if stored := db.lastPingReceived(node.ID); stored.Unix() != inst.Unix() {
+	if stored := db.LastPingReceived(node.ID()); stored.Unix() != inst.Unix() {
 		t.Errorf("ping: value mismatch: have %v, want %v", stored, inst)
 		t.Errorf("ping: value mismatch: have %v, want %v", stored, inst)
 	}
 	}
 	// Check fetch/store operations on a node pong object
 	// Check fetch/store operations on a node pong object
-	if stored := db.lastPongReceived(node.ID); stored.Unix() != 0 {
+	if stored := db.LastPongReceived(node.ID()); stored.Unix() != 0 {
 		t.Errorf("pong: non-existing object: %v", stored)
 		t.Errorf("pong: non-existing object: %v", stored)
 	}
 	}
-	if err := db.updateLastPongReceived(node.ID, inst); err != nil {
+	if err := db.UpdateLastPongReceived(node.ID(), inst); err != nil {
 		t.Errorf("pong: failed to update: %v", err)
 		t.Errorf("pong: failed to update: %v", err)
 	}
 	}
-	if stored := db.lastPongReceived(node.ID); stored.Unix() != inst.Unix() {
+	if stored := db.LastPongReceived(node.ID()); stored.Unix() != inst.Unix() {
 		t.Errorf("pong: value mismatch: have %v, want %v", stored, inst)
 		t.Errorf("pong: value mismatch: have %v, want %v", stored, inst)
 	}
 	}
 	// Check fetch/store operations on a node findnode-failure object
 	// Check fetch/store operations on a node findnode-failure object
-	if stored := db.findFails(node.ID); stored != 0 {
+	if stored := db.FindFails(node.ID()); stored != 0 {
 		t.Errorf("find-node fails: non-existing object: %v", stored)
 		t.Errorf("find-node fails: non-existing object: %v", stored)
 	}
 	}
-	if err := db.updateFindFails(node.ID, num); err != nil {
+	if err := db.UpdateFindFails(node.ID(), num); err != nil {
 		t.Errorf("find-node fails: failed to update: %v", err)
 		t.Errorf("find-node fails: failed to update: %v", err)
 	}
 	}
-	if stored := db.findFails(node.ID); stored != num {
+	if stored := db.FindFails(node.ID()); stored != num {
 		t.Errorf("find-node fails: value mismatch: have %v, want %v", stored, num)
 		t.Errorf("find-node fails: value mismatch: have %v, want %v", stored, num)
 	}
 	}
 	// Check fetch/store operations on an actual node object
 	// Check fetch/store operations on an actual node object
-	if stored := db.node(node.ID); stored != nil {
+	if stored := db.Node(node.ID()); stored != nil {
 		t.Errorf("node: non-existing object: %v", stored)
 		t.Errorf("node: non-existing object: %v", stored)
 	}
 	}
-	if err := db.updateNode(node); err != nil {
+	if err := db.UpdateNode(node); err != nil {
 		t.Errorf("node: failed to update: %v", err)
 		t.Errorf("node: failed to update: %v", err)
 	}
 	}
-	if stored := db.node(node.ID); stored == nil {
+	if stored := db.Node(node.ID()); stored == nil {
 		t.Errorf("node: not found")
 		t.Errorf("node: not found")
 	} else if !reflect.DeepEqual(stored, node) {
 	} else if !reflect.DeepEqual(stored, node) {
 		t.Errorf("node: data mismatch: have %v, want %v", stored, node)
 		t.Errorf("node: data mismatch: have %v, want %v", stored, node)
@@ -165,8 +163,8 @@ var nodeDBSeedQueryNodes = []struct {
 	// This one should not be in the result set because its last
 	// This one should not be in the result set because its last
 	// pong time is too far in the past.
 	// pong time is too far in the past.
 	{
 	{
-		node: NewNode(
-			MustHexID("0x84d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		node: NewV4(
+			hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
 			net.IP{127, 0, 0, 3},
 			net.IP{127, 0, 0, 3},
 			30303,
 			30303,
 			30303,
 			30303,
@@ -176,8 +174,8 @@ var nodeDBSeedQueryNodes = []struct {
 	// This one shouldn't be in in the result set because its
 	// This one shouldn't be in in the result set because its
 	// nodeID is the local node's ID.
 	// nodeID is the local node's ID.
 	{
 	{
-		node: NewNode(
-			MustHexID("0x57d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		node: NewV4(
+			hexPubkey("ff93ff820abacd4351b0f14e47b324bc82ff014c226f3f66a53535734a3c150e7e38ca03ef0964ba55acddc768f5e99cd59dea95ddd4defbab1339c92fa319b2"),
 			net.IP{127, 0, 0, 3},
 			net.IP{127, 0, 0, 3},
 			30303,
 			30303,
 			30303,
 			30303,
@@ -187,8 +185,8 @@ var nodeDBSeedQueryNodes = []struct {
 
 
 	// These should be in the result set.
 	// These should be in the result set.
 	{
 	{
-		node: NewNode(
-			MustHexID("0x22d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		node: NewV4(
+			hexPubkey("c2b5eb3f5dde05f815b63777809ee3e7e0cbb20035a6b00ce327191e6eaa8f26a8d461c9112b7ab94698e7361fa19fd647e603e73239002946d76085b6f928d6"),
 			net.IP{127, 0, 0, 1},
 			net.IP{127, 0, 0, 1},
 			30303,
 			30303,
 			30303,
 			30303,
@@ -196,8 +194,8 @@ var nodeDBSeedQueryNodes = []struct {
 		pong: time.Now().Add(-2 * time.Second),
 		pong: time.Now().Add(-2 * time.Second),
 	},
 	},
 	{
 	{
-		node: NewNode(
-			MustHexID("0x44d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		node: NewV4(
+			hexPubkey("6ca1d400c8ddf8acc94bcb0dd254911ad71a57bed5e0ae5aa205beed59b28c2339908e97990c493499613cff8ecf6c3dc7112a8ead220cdcd00d8847ca3db755"),
 			net.IP{127, 0, 0, 2},
 			net.IP{127, 0, 0, 2},
 			30303,
 			30303,
 			30303,
 			30303,
@@ -205,56 +203,91 @@ var nodeDBSeedQueryNodes = []struct {
 		pong: time.Now().Add(-3 * time.Second),
 		pong: time.Now().Add(-3 * time.Second),
 	},
 	},
 	{
 	{
-		node: NewNode(
-			MustHexID("0xe2d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		node: NewV4(
+			hexPubkey("234dc63fe4d131212b38236c4c3411288d7bec61cbf7b120ff12c43dc60c96182882f4291d209db66f8a38e986c9c010ff59231a67f9515c7d1668b86b221a47"),
 			net.IP{127, 0, 0, 3},
 			net.IP{127, 0, 0, 3},
 			30303,
 			30303,
 			30303,
 			30303,
 		),
 		),
 		pong: time.Now().Add(-1 * time.Second),
 		pong: time.Now().Add(-1 * time.Second),
 	},
 	},
+	{
+		node: NewV4(
+			hexPubkey("c013a50b4d1ebce5c377d8af8cb7114fd933ffc9627f96ad56d90fef5b7253ec736fd07ef9a81dc2955a997e54b7bf50afd0aa9f110595e2bec5bb7ce1657004"),
+			net.IP{127, 0, 0, 3},
+			30303,
+			30303,
+		),
+		pong: time.Now().Add(-2 * time.Second),
+	},
+	{
+		node: NewV4(
+			hexPubkey("f141087e3e08af1aeec261ff75f48b5b1637f594ea9ad670e50051646b0416daa3b134c28788cbe98af26992a47652889cd8577ccc108ac02c6a664db2dc1283"),
+			net.IP{127, 0, 0, 3},
+			30303,
+			30303,
+		),
+		pong: time.Now().Add(-2 * time.Second),
+	},
+}
+
+func TestDBSeedQuery(t *testing.T) {
+	// Querying seeds uses seeks an might not find all nodes
+	// every time when the database is small. Run the test multiple
+	// times to avoid flakes.
+	const attempts = 15
+	var err error
+	for i := 0; i < attempts; i++ {
+		if err = testSeedQuery(); err == nil {
+			return
+		}
+	}
+	if err != nil {
+		t.Errorf("no successful run in %d attempts: %v", attempts, err)
+	}
 }
 }
 
 
-func TestNodeDBSeedQuery(t *testing.T) {
-	db, _ := newNodeDB("", nodeDBVersion, nodeDBSeedQueryNodes[1].node.ID)
-	defer db.close()
+func testSeedQuery() error {
+	db, _ := OpenDB("")
+	defer db.Close()
 
 
 	// Insert a batch of nodes for querying
 	// Insert a batch of nodes for querying
 	for i, seed := range nodeDBSeedQueryNodes {
 	for i, seed := range nodeDBSeedQueryNodes {
-		if err := db.updateNode(seed.node); err != nil {
-			t.Fatalf("node %d: failed to insert: %v", i, err)
+		if err := db.UpdateNode(seed.node); err != nil {
+			return fmt.Errorf("node %d: failed to insert: %v", i, err)
 		}
 		}
-		if err := db.updateLastPongReceived(seed.node.ID, seed.pong); err != nil {
-			t.Fatalf("node %d: failed to insert bondTime: %v", i, err)
+		if err := db.UpdateLastPongReceived(seed.node.ID(), seed.pong); err != nil {
+			return fmt.Errorf("node %d: failed to insert bondTime: %v", i, err)
 		}
 		}
 	}
 	}
 
 
 	// Retrieve the entire batch and check for duplicates
 	// Retrieve the entire batch and check for duplicates
-	seeds := db.querySeeds(len(nodeDBSeedQueryNodes)*2, time.Hour)
-	have := make(map[NodeID]struct{})
+	seeds := db.QuerySeeds(len(nodeDBSeedQueryNodes)*2, time.Hour)
+	have := make(map[ID]struct{})
 	for _, seed := range seeds {
 	for _, seed := range seeds {
-		have[seed.ID] = struct{}{}
+		have[seed.ID()] = struct{}{}
 	}
 	}
-	want := make(map[NodeID]struct{})
-	for _, seed := range nodeDBSeedQueryNodes[2:] {
-		want[seed.node.ID] = struct{}{}
+	want := make(map[ID]struct{})
+	for _, seed := range nodeDBSeedQueryNodes[1:] {
+		want[seed.node.ID()] = struct{}{}
 	}
 	}
 	if len(seeds) != len(want) {
 	if len(seeds) != len(want) {
-		t.Errorf("seed count mismatch: have %v, want %v", len(seeds), len(want))
+		return fmt.Errorf("seed count mismatch: have %v, want %v", len(seeds), len(want))
 	}
 	}
 	for id := range have {
 	for id := range have {
 		if _, ok := want[id]; !ok {
 		if _, ok := want[id]; !ok {
-			t.Errorf("extra seed: %v", id)
+			return fmt.Errorf("extra seed: %v", id)
 		}
 		}
 	}
 	}
 	for id := range want {
 	for id := range want {
 		if _, ok := have[id]; !ok {
 		if _, ok := have[id]; !ok {
-			t.Errorf("missing seed: %v", id)
+			return fmt.Errorf("missing seed: %v", id)
 		}
 		}
 	}
 	}
+	return nil
 }
 }
 
 
-func TestNodeDBPersistency(t *testing.T) {
+func TestDBPersistency(t *testing.T) {
 	root, err := ioutil.TempDir("", "nodedb-")
 	root, err := ioutil.TempDir("", "nodedb-")
 	if err != nil {
 	if err != nil {
 		t.Fatalf("failed to create temporary data folder: %v", err)
 		t.Fatalf("failed to create temporary data folder: %v", err)
@@ -267,34 +300,24 @@ func TestNodeDBPersistency(t *testing.T) {
 	)
 	)
 
 
 	// Create a persistent database and store some values
 	// Create a persistent database and store some values
-	db, err := newNodeDB(filepath.Join(root, "database"), nodeDBVersion, NodeID{})
+	db, err := OpenDB(filepath.Join(root, "database"))
 	if err != nil {
 	if err != nil {
 		t.Fatalf("failed to create persistent database: %v", err)
 		t.Fatalf("failed to create persistent database: %v", err)
 	}
 	}
 	if err := db.storeInt64(testKey, testInt); err != nil {
 	if err := db.storeInt64(testKey, testInt); err != nil {
 		t.Fatalf("failed to store value: %v.", err)
 		t.Fatalf("failed to store value: %v.", err)
 	}
 	}
-	db.close()
+	db.Close()
 
 
 	// Reopen the database and check the value
 	// Reopen the database and check the value
-	db, err = newNodeDB(filepath.Join(root, "database"), nodeDBVersion, NodeID{})
+	db, err = OpenDB(filepath.Join(root, "database"))
 	if err != nil {
 	if err != nil {
 		t.Fatalf("failed to open persistent database: %v", err)
 		t.Fatalf("failed to open persistent database: %v", err)
 	}
 	}
 	if val := db.fetchInt64(testKey); val != testInt {
 	if val := db.fetchInt64(testKey); val != testInt {
 		t.Fatalf("value mismatch: have %v, want %v", val, testInt)
 		t.Fatalf("value mismatch: have %v, want %v", val, testInt)
 	}
 	}
-	db.close()
-
-	// Change the database version and check flush
-	db, err = newNodeDB(filepath.Join(root, "database"), nodeDBVersion+1, NodeID{})
-	if err != nil {
-		t.Fatalf("failed to open persistent database: %v", err)
-	}
-	if val := db.fetchInt64(testKey); val != 0 {
-		t.Fatalf("value mismatch: have %v, want %v", val, 0)
-	}
-	db.close()
+	db.Close()
 }
 }
 
 
 var nodeDBExpirationNodes = []struct {
 var nodeDBExpirationNodes = []struct {
@@ -303,8 +326,8 @@ var nodeDBExpirationNodes = []struct {
 	exp  bool
 	exp  bool
 }{
 }{
 	{
 	{
-		node: NewNode(
-			MustHexID("0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		node: NewV4(
+			hexPubkey("8d110e2ed4b446d9b5fb50f117e5f37fb7597af455e1dab0e6f045a6eeaa786a6781141659020d38bdc5e698ed3d4d2bafa8b5061810dfa63e8ac038db2e9b67"),
 			net.IP{127, 0, 0, 1},
 			net.IP{127, 0, 0, 1},
 			30303,
 			30303,
 			30303,
 			30303,
@@ -312,8 +335,8 @@ var nodeDBExpirationNodes = []struct {
 		pong: time.Now().Add(-nodeDBNodeExpiration + time.Minute),
 		pong: time.Now().Add(-nodeDBNodeExpiration + time.Minute),
 		exp:  false,
 		exp:  false,
 	}, {
 	}, {
-		node: NewNode(
-			MustHexID("0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		node: NewV4(
+			hexPubkey("913a205579c32425b220dfba999d215066e5bdbf900226b11da1907eae5e93eb40616d47412cf819664e9eacbdfcca6b0c6e07e09847a38472d4be46ab0c3672"),
 			net.IP{127, 0, 0, 2},
 			net.IP{127, 0, 0, 2},
 			30303,
 			30303,
 			30303,
 			30303,
@@ -323,16 +346,16 @@ var nodeDBExpirationNodes = []struct {
 	},
 	},
 }
 }
 
 
-func TestNodeDBExpiration(t *testing.T) {
-	db, _ := newNodeDB("", nodeDBVersion, NodeID{})
-	defer db.close()
+func TestDBExpiration(t *testing.T) {
+	db, _ := OpenDB("")
+	defer db.Close()
 
 
 	// Add all the test nodes and set their last pong time
 	// Add all the test nodes and set their last pong time
 	for i, seed := range nodeDBExpirationNodes {
 	for i, seed := range nodeDBExpirationNodes {
-		if err := db.updateNode(seed.node); err != nil {
+		if err := db.UpdateNode(seed.node); err != nil {
 			t.Fatalf("node %d: failed to insert: %v", i, err)
 			t.Fatalf("node %d: failed to insert: %v", i, err)
 		}
 		}
-		if err := db.updateLastPongReceived(seed.node.ID, seed.pong); err != nil {
+		if err := db.UpdateLastPongReceived(seed.node.ID(), seed.pong); err != nil {
 			t.Fatalf("node %d: failed to update bondTime: %v", i, err)
 			t.Fatalf("node %d: failed to update bondTime: %v", i, err)
 		}
 		}
 	}
 	}
@@ -341,40 +364,9 @@ func TestNodeDBExpiration(t *testing.T) {
 		t.Fatalf("failed to expire nodes: %v", err)
 		t.Fatalf("failed to expire nodes: %v", err)
 	}
 	}
 	for i, seed := range nodeDBExpirationNodes {
 	for i, seed := range nodeDBExpirationNodes {
-		node := db.node(seed.node.ID)
+		node := db.Node(seed.node.ID())
 		if (node == nil && !seed.exp) || (node != nil && seed.exp) {
 		if (node == nil && !seed.exp) || (node != nil && seed.exp) {
 			t.Errorf("node %d: expiration mismatch: have %v, want %v", i, node, seed.exp)
 			t.Errorf("node %d: expiration mismatch: have %v, want %v", i, node, seed.exp)
 		}
 		}
 	}
 	}
 }
 }
-
-func TestNodeDBSelfExpiration(t *testing.T) {
-	// Find a node in the tests that shouldn't expire, and assign it as self
-	var self NodeID
-	for _, node := range nodeDBExpirationNodes {
-		if !node.exp {
-			self = node.node.ID
-			break
-		}
-	}
-	db, _ := newNodeDB("", nodeDBVersion, self)
-	defer db.close()
-
-	// Add all the test nodes and set their last pong time
-	for i, seed := range nodeDBExpirationNodes {
-		if err := db.updateNode(seed.node); err != nil {
-			t.Fatalf("node %d: failed to insert: %v", i, err)
-		}
-		if err := db.updateLastPongReceived(seed.node.ID, seed.pong); err != nil {
-			t.Fatalf("node %d: failed to update bondTime: %v", i, err)
-		}
-	}
-	// Expire the nodes and make sure self has been evacuated too
-	if err := db.expireNodes(); err != nil {
-		t.Fatalf("failed to expire nodes: %v", err)
-	}
-	node := db.node(self)
-	if node != nil {
-		t.Errorf("self not evacuated")
-	}
-}

+ 194 - 0
p2p/enode/urlv4.go

@@ -0,0 +1,194 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package enode
+
+import (
+	"crypto/ecdsa"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"net"
+	"net/url"
+	"regexp"
+	"strconv"
+
+	"github.com/ethereum/go-ethereum/common/math"
+	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/ethereum/go-ethereum/p2p/enr"
+)
+
+var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$")
+
+// MustParseV4 parses a node URL. It panics if the URL is not valid.
+func MustParseV4(rawurl string) *Node {
+	n, err := ParseV4(rawurl)
+	if err != nil {
+		panic("invalid node URL: " + err.Error())
+	}
+	return n
+}
+
+// ParseV4 parses a node URL.
+//
+// There are two basic forms of node URLs:
+//
+//   - incomplete nodes, which only have the public key (node ID)
+//   - complete nodes, which contain the public key and IP/Port information
+//
+// For incomplete nodes, the designator must look like one of these
+//
+//    enode://<hex node id>
+//    <hex node id>
+//
+// For complete nodes, the node ID is encoded in the username portion
+// of the URL, separated from the host by an @ sign. The hostname can
+// only be given as an IP address, DNS domain names are not allowed.
+// The port in the host name section is the TCP listening port. If the
+// TCP and UDP (discovery) ports differ, the UDP port is specified as
+// query parameter "discport".
+//
+// In the following example, the node URL describes
+// a node with IP address 10.3.58.6, TCP listening port 30303
+// and UDP discovery port 30301.
+//
+//    enode://<hex node id>@10.3.58.6:30303?discport=30301
+func ParseV4(rawurl string) (*Node, error) {
+	if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil {
+		id, err := parsePubkey(m[1])
+		if err != nil {
+			return nil, fmt.Errorf("invalid node ID (%v)", err)
+		}
+		return NewV4(id, nil, 0, 0), nil
+	}
+	return parseComplete(rawurl)
+}
+
+// NewV4 creates a node from discovery v4 node information. The record
+// contained in the node has a zero-length signature.
+func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp int) *Node {
+	var r enr.Record
+	if ip != nil {
+		r.Set(enr.IP(ip))
+	}
+	if udp != 0 {
+		r.Set(enr.UDP(udp))
+	}
+	if tcp != 0 {
+		r.Set(enr.TCP(tcp))
+	}
+	signV4Compat(&r, pubkey)
+	n, err := New(v4CompatID{}, &r)
+	if err != nil {
+		panic(err)
+	}
+	return n
+}
+
+func parseComplete(rawurl string) (*Node, error) {
+	var (
+		id               *ecdsa.PublicKey
+		ip               net.IP
+		tcpPort, udpPort uint64
+	)
+	u, err := url.Parse(rawurl)
+	if err != nil {
+		return nil, err
+	}
+	if u.Scheme != "enode" {
+		return nil, errors.New("invalid URL scheme, want \"enode\"")
+	}
+	// Parse the Node ID from the user portion.
+	if u.User == nil {
+		return nil, errors.New("does not contain node ID")
+	}
+	if id, err = parsePubkey(u.User.String()); err != nil {
+		return nil, fmt.Errorf("invalid node ID (%v)", err)
+	}
+	// Parse the IP address.
+	host, port, err := net.SplitHostPort(u.Host)
+	if err != nil {
+		return nil, fmt.Errorf("invalid host: %v", err)
+	}
+	if ip = net.ParseIP(host); ip == nil {
+		return nil, errors.New("invalid IP address")
+	}
+	// Ensure the IP is 4 bytes long for IPv4 addresses.
+	if ipv4 := ip.To4(); ipv4 != nil {
+		ip = ipv4
+	}
+	// Parse the port numbers.
+	if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil {
+		return nil, errors.New("invalid port")
+	}
+	udpPort = tcpPort
+	qv := u.Query()
+	if qv.Get("discport") != "" {
+		udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16)
+		if err != nil {
+			return nil, errors.New("invalid discport in query")
+		}
+	}
+	return NewV4(id, ip, int(tcpPort), int(udpPort)), nil
+}
+
+// parsePubkey parses a hex-encoded secp256k1 public key.
+func parsePubkey(in string) (*ecdsa.PublicKey, error) {
+	b, err := hex.DecodeString(in)
+	if err != nil {
+		return nil, err
+	} else if len(b) != 64 {
+		return nil, fmt.Errorf("wrong length, want %d hex chars", 128)
+	}
+	b = append([]byte{0x4}, b...)
+	return crypto.UnmarshalPubkey(b)
+}
+
+func (n *Node) v4URL() string {
+	var (
+		scheme enr.ID
+		nodeid string
+		key    ecdsa.PublicKey
+	)
+	n.Load(&scheme)
+	n.Load((*Secp256k1)(&key))
+	switch {
+	case scheme == "v4" || key != ecdsa.PublicKey{}:
+		nodeid = fmt.Sprintf("%x", crypto.FromECDSAPub(&key)[1:])
+	default:
+		nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:])
+	}
+	u := url.URL{Scheme: "enode"}
+	if n.Incomplete() {
+		u.Host = nodeid
+	} else {
+		addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()}
+		u.User = url.User(nodeid)
+		u.Host = addr.String()
+		if n.UDP() != n.TCP() {
+			u.RawQuery = "discport=" + strconv.Itoa(n.UDP())
+		}
+	}
+	return u.String()
+}
+
+// PubkeyToIDV4 derives the v4 node address from the given public key.
+func PubkeyToIDV4(key *ecdsa.PublicKey) ID {
+	e := make([]byte, 64)
+	math.ReadBits(key.X, e[:len(e)/2])
+	math.ReadBits(key.Y, e[len(e)/2:])
+	return ID(crypto.Keccak256Hash(e))
+}

+ 49 - 141
p2p/discover/node_test.go → p2p/enode/urlv4_test.go

@@ -1,4 +1,4 @@
-// Copyright 2015 The go-ethereum Authors
+// Copyright 2018 The go-ethereum Authors
 // This file is part of the go-ethereum library.
 // This file is part of the go-ethereum library.
 //
 //
 // The go-ethereum library is free software: you can redistribute it and/or modify
 // The go-ethereum library is free software: you can redistribute it and/or modify
@@ -14,45 +14,19 @@
 // You should have received a copy of the GNU Lesser General Public License
 // You should have received a copy of the GNU Lesser General Public License
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 
 
-package discover
+package enode
 
 
 import (
 import (
 	"bytes"
 	"bytes"
-	"fmt"
+	"crypto/ecdsa"
 	"math/big"
 	"math/big"
-	"math/rand"
 	"net"
 	"net"
 	"reflect"
 	"reflect"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 	"testing/quick"
 	"testing/quick"
-	"time"
-
-	"github.com/ethereum/go-ethereum/common"
-	"github.com/ethereum/go-ethereum/crypto"
 )
 )
 
 
-func ExampleNewNode() {
-	id := MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
-
-	// Complete nodes contain UDP and TCP endpoints:
-	n1 := NewNode(id, net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 52150, 30303)
-	fmt.Println("n1:", n1)
-	fmt.Println("n1.Incomplete() ->", n1.Incomplete())
-
-	// An incomplete node can be created by passing zero values
-	// for all parameters except id.
-	n2 := NewNode(id, nil, 0, 0)
-	fmt.Println("n2:", n2)
-	fmt.Println("n2.Incomplete() ->", n2.Incomplete())
-
-	// Output:
-	// n1: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:30303?discport=52150
-	// n1.Incomplete() -> false
-	// n2: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439
-	// n2.Incomplete() -> true
-}
-
 var parseNodeTests = []struct {
 var parseNodeTests = []struct {
 	rawurl     string
 	rawurl     string
 	wantError  string
 	wantError  string
@@ -81,8 +55,8 @@ var parseNodeTests = []struct {
 	},
 	},
 	{
 	{
 		rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150",
 		rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150",
-		wantResult: NewNode(
-			MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		wantResult: NewV4(
+			hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
 			net.IP{0x7f, 0x0, 0x0, 0x1},
 			net.IP{0x7f, 0x0, 0x0, 0x1},
 			52150,
 			52150,
 			52150,
 			52150,
@@ -90,8 +64,8 @@ var parseNodeTests = []struct {
 	},
 	},
 	{
 	{
 		rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150",
 		rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150",
-		wantResult: NewNode(
-			MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		wantResult: NewV4(
+			hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
 			net.ParseIP("::"),
 			net.ParseIP("::"),
 			52150,
 			52150,
 			52150,
 			52150,
@@ -99,8 +73,8 @@ var parseNodeTests = []struct {
 	},
 	},
 	{
 	{
 		rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150",
 		rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150",
-		wantResult: NewNode(
-			MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		wantResult: NewV4(
+			hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
 			net.ParseIP("2001:db8:3c4d:15::abcd:ef12"),
 			net.ParseIP("2001:db8:3c4d:15::abcd:ef12"),
 			52150,
 			52150,
 			52150,
 			52150,
@@ -108,25 +82,25 @@ var parseNodeTests = []struct {
 	},
 	},
 	{
 	{
 		rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334",
 		rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334",
-		wantResult: NewNode(
-			MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		wantResult: NewV4(
+			hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
 			net.IP{0x7f, 0x0, 0x0, 0x1},
 			net.IP{0x7f, 0x0, 0x0, 0x1},
-			22334,
 			52150,
 			52150,
+			22334,
 		),
 		),
 	},
 	},
 	// Incomplete nodes with no address.
 	// Incomplete nodes with no address.
 	{
 	{
 		rawurl: "1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439",
 		rawurl: "1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439",
-		wantResult: NewNode(
-			MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		wantResult: NewV4(
+			hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
 			nil, 0, 0,
 			nil, 0, 0,
 		),
 		),
 	},
 	},
 	{
 	{
 		rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439",
 		rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439",
-		wantResult: NewNode(
-			MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+		wantResult: NewV4(
+			hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
 			nil, 0, 0,
 			nil, 0, 0,
 		),
 		),
 	},
 	},
@@ -146,9 +120,17 @@ var parseNodeTests = []struct {
 	},
 	},
 }
 }
 
 
+func hexPubkey(h string) *ecdsa.PublicKey {
+	k, err := parsePubkey(h)
+	if err != nil {
+		panic(err)
+	}
+	return k
+}
+
 func TestParseNode(t *testing.T) {
 func TestParseNode(t *testing.T) {
 	for _, test := range parseNodeTests {
 	for _, test := range parseNodeTests {
-		n, err := ParseNode(test.rawurl)
+		n, err := ParseV4(test.rawurl)
 		if test.wantError != "" {
 		if test.wantError != "" {
 			if err == nil {
 			if err == nil {
 				t.Errorf("test %q:\n  got nil error, expected %#q", test.rawurl, test.wantError)
 				t.Errorf("test %q:\n  got nil error, expected %#q", test.rawurl, test.wantError)
@@ -163,7 +145,7 @@ func TestParseNode(t *testing.T) {
 				continue
 				continue
 			}
 			}
 			if !reflect.DeepEqual(n, test.wantResult) {
 			if !reflect.DeepEqual(n, test.wantResult) {
-				t.Errorf("test %q:\n  result mismatch:\ngot:  %#v, want: %#v", test.rawurl, n, test.wantResult)
+				t.Errorf("test %q:\n  result mismatch:\ngot:  %#v\nwant: %#v", test.rawurl, n, test.wantResult)
 			}
 			}
 		}
 		}
 	}
 	}
@@ -181,9 +163,9 @@ func TestNodeString(t *testing.T) {
 }
 }
 
 
 func TestHexID(t *testing.T) {
 func TestHexID(t *testing.T) {
-	ref := NodeID{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 106, 217, 182, 31, 165, 174, 1, 67, 7, 235, 220, 150, 66, 83, 173, 205, 159, 44, 10, 57, 42, 161, 26, 188}
-	id1 := MustHexID("0x000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
-	id2 := MustHexID("000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
+	ref := ID{0, 0, 0, 0, 0, 0, 0, 128, 106, 217, 182, 31, 165, 174, 1, 67, 7, 235, 220, 150, 66, 83, 173, 205, 159, 44, 10, 57, 42, 161, 26, 188}
+	id1 := HexID("0x00000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
+	id2 := HexID("00000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
 
 
 	if id1 != ref {
 	if id1 != ref {
 		t.Errorf("wrong id1\ngot  %v\nwant %v", id1[:], ref[:])
 		t.Errorf("wrong id1\ngot  %v\nwant %v", id1[:], ref[:])
@@ -193,17 +175,14 @@ func TestHexID(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func TestNodeID_textEncoding(t *testing.T) {
-	ref := NodeID{
+func TestID_textEncoding(t *testing.T) {
+	ref := ID{
 		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
 		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
 		0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20,
 		0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20,
 		0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30,
 		0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30,
-		0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40,
-		0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50,
-		0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x60,
-		0x61, 0x62, 0x63, 0x64,
+		0x31, 0x32,
 	}
 	}
-	hex := "01020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364"
+	hex := "0102030405060708091011121314151617181920212223242526272829303132"
 
 
 	text, err := ref.MarshalText()
 	text, err := ref.MarshalText()
 	if err != nil {
 	if err != nil {
@@ -213,7 +192,7 @@ func TestNodeID_textEncoding(t *testing.T) {
 		t.Fatalf("text encoding did not match\nexpected: %s\ngot:      %s", hex, text)
 		t.Fatalf("text encoding did not match\nexpected: %s\ngot:      %s", hex, text)
 	}
 	}
 
 
-	id := new(NodeID)
+	id := new(ID)
 	if err := id.UnmarshalText(text); err != nil {
 	if err := id.UnmarshalText(text); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -222,114 +201,43 @@ func TestNodeID_textEncoding(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func TestNodeID_recover(t *testing.T) {
-	prv := newkey()
-	hash := make([]byte, 32)
-	sig, err := crypto.Sign(hash, prv)
-	if err != nil {
-		t.Fatalf("signing error: %v", err)
-	}
-
-	pub := PubkeyID(&prv.PublicKey)
-	recpub, err := recoverNodeID(hash, sig)
-	if err != nil {
-		t.Fatalf("recovery error: %v", err)
-	}
-	if pub != recpub {
-		t.Errorf("recovered wrong pubkey:\ngot:  %v\nwant: %v", recpub, pub)
-	}
-
-	ecdsa, err := pub.Pubkey()
-	if err != nil {
-		t.Errorf("Pubkey error: %v", err)
-	}
-	if !reflect.DeepEqual(ecdsa, &prv.PublicKey) {
-		t.Errorf("Pubkey mismatch:\n  got:  %#v\n  want: %#v", ecdsa, &prv.PublicKey)
-	}
-}
-
-func TestNodeID_pubkeyBad(t *testing.T) {
-	ecdsa, err := NodeID{}.Pubkey()
-	if err == nil {
-		t.Error("expected error for zero ID")
-	}
-	if ecdsa != nil {
-		t.Error("expected nil result")
-	}
-}
-
 func TestNodeID_distcmp(t *testing.T) {
 func TestNodeID_distcmp(t *testing.T) {
-	distcmpBig := func(target, a, b common.Hash) int {
+	distcmpBig := func(target, a, b ID) int {
 		tbig := new(big.Int).SetBytes(target[:])
 		tbig := new(big.Int).SetBytes(target[:])
 		abig := new(big.Int).SetBytes(a[:])
 		abig := new(big.Int).SetBytes(a[:])
 		bbig := new(big.Int).SetBytes(b[:])
 		bbig := new(big.Int).SetBytes(b[:])
 		return new(big.Int).Xor(tbig, abig).Cmp(new(big.Int).Xor(tbig, bbig))
 		return new(big.Int).Xor(tbig, abig).Cmp(new(big.Int).Xor(tbig, bbig))
 	}
 	}
-	if err := quick.CheckEqual(distcmp, distcmpBig, quickcfg()); err != nil {
+	if err := quick.CheckEqual(DistCmp, distcmpBig, nil); err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
 }
 }
 
 
-// the random tests is likely to miss the case where they're equal.
+// The random tests is likely to miss the case where a and b are equal,
+// this test checks it explicitly.
 func TestNodeID_distcmpEqual(t *testing.T) {
 func TestNodeID_distcmpEqual(t *testing.T) {
-	base := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
-	x := common.Hash{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
-	if distcmp(base, x, x) != 0 {
-		t.Errorf("distcmp(base, x, x) != 0")
+	base := ID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
+	x := ID{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
+	if DistCmp(base, x, x) != 0 {
+		t.Errorf("DistCmp(base, x, x) != 0")
 	}
 	}
 }
 }
 
 
 func TestNodeID_logdist(t *testing.T) {
 func TestNodeID_logdist(t *testing.T) {
-	logdistBig := func(a, b common.Hash) int {
+	logdistBig := func(a, b ID) int {
 		abig, bbig := new(big.Int).SetBytes(a[:]), new(big.Int).SetBytes(b[:])
 		abig, bbig := new(big.Int).SetBytes(a[:]), new(big.Int).SetBytes(b[:])
 		return new(big.Int).Xor(abig, bbig).BitLen()
 		return new(big.Int).Xor(abig, bbig).BitLen()
 	}
 	}
-	if err := quick.CheckEqual(logdist, logdistBig, quickcfg()); err != nil {
+	if err := quick.CheckEqual(LogDist, logdistBig, nil); err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
 }
 }
 
 
-// the random tests is likely to miss the case where they're equal.
+// The random tests is likely to miss the case where a and b are equal,
+// this test checks it explicitly.
 func TestNodeID_logdistEqual(t *testing.T) {
 func TestNodeID_logdistEqual(t *testing.T) {
-	x := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
-	if logdist(x, x) != 0 {
-		t.Errorf("logdist(x, x) != 0")
-	}
-}
-
-func TestNodeID_hashAtDistance(t *testing.T) {
-	// we don't use quick.Check here because its output isn't
-	// very helpful when the test fails.
-	cfg := quickcfg()
-	for i := 0; i < cfg.MaxCount; i++ {
-		a := gen(common.Hash{}, cfg.Rand).(common.Hash)
-		dist := cfg.Rand.Intn(len(common.Hash{}) * 8)
-		result := hashAtDistance(a, dist)
-		actualdist := logdist(result, a)
-
-		if dist != actualdist {
-			t.Log("a:     ", a)
-			t.Log("result:", result)
-			t.Fatalf("#%d: distance of result is %d, want %d", i, actualdist, dist)
-		}
-	}
-}
-
-func quickcfg() *quick.Config {
-	return &quick.Config{
-		MaxCount: 5000,
-		Rand:     rand.New(rand.NewSource(time.Now().Unix())),
-	}
-}
-
-// TODO: The Generate method can be dropped when we require Go >= 1.5
-// because testing/quick learned to generate arrays in 1.5.
-
-func (NodeID) Generate(rand *rand.Rand, size int) reflect.Value {
-	var id NodeID
-	m := rand.Intn(len(id))
-	for i := len(id) - 1; i > m; i-- {
-		id[i] = byte(rand.Uint32())
+	x := ID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
+	if LogDist(x, x) != 0 {
+		t.Errorf("LogDist(x, x) != 0")
 	}
 	}
-	return reflect.ValueOf(id)
 }
 }

+ 95 - 73
p2p/enr/enr.go

@@ -15,14 +15,20 @@
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 
 
 // Package enr implements Ethereum Node Records as defined in EIP-778. A node record holds
 // Package enr implements Ethereum Node Records as defined in EIP-778. A node record holds
-// arbitrary information about a node on the peer-to-peer network.
-//
-// Records contain named keys. To store and retrieve key/values in a record, use the Entry
+// arbitrary information about a node on the peer-to-peer network. Node information is
+// stored in key/value pairs. To store and retrieve key/values in a record, use the Entry
 // interface.
 // interface.
 //
 //
-// Records must be signed before transmitting them to another node. Decoding a record verifies
-// its signature. When creating a record, set the entries you want, then call Sign to add the
-// signature. Modifying a record invalidates the signature.
+// Signature Handling
+//
+// Records must be signed before transmitting them to another node.
+//
+// Decoding a record doesn't check its signature. Code working with records from an
+// untrusted source must always verify two things: that the record uses an identity scheme
+// deemed secure, and that the signature is valid according to the declared scheme.
+//
+// When creating a record, set the entries you want and use a signing function provided by
+// the identity scheme to add the signature. Modifying a record invalidates the signature.
 //
 //
 // Package enr supports the "secp256k1-keccak" identity scheme.
 // Package enr supports the "secp256k1-keccak" identity scheme.
 package enr
 package enr
@@ -40,8 +46,7 @@ import (
 const SizeLimit = 300 // maximum encoded size of a node record in bytes
 const SizeLimit = 300 // maximum encoded size of a node record in bytes
 
 
 var (
 var (
-	errNoID           = errors.New("unknown or unspecified identity scheme")
-	errInvalidSig     = errors.New("invalid signature")
+	ErrInvalidSig     = errors.New("invalid signature on node record")
 	errNotSorted      = errors.New("record key/value pairs are not sorted by key")
 	errNotSorted      = errors.New("record key/value pairs are not sorted by key")
 	errDuplicateKey   = errors.New("record contains duplicate key")
 	errDuplicateKey   = errors.New("record contains duplicate key")
 	errIncompletePair = errors.New("record contains incomplete k/v pair")
 	errIncompletePair = errors.New("record contains incomplete k/v pair")
@@ -50,6 +55,32 @@ var (
 	errNotFound       = errors.New("no such key in record")
 	errNotFound       = errors.New("no such key in record")
 )
 )
 
 
+// An IdentityScheme is capable of verifying record signatures and
+// deriving node addresses.
+type IdentityScheme interface {
+	Verify(r *Record, sig []byte) error
+	NodeAddr(r *Record) []byte
+}
+
+// SchemeMap is a registry of named identity schemes.
+type SchemeMap map[string]IdentityScheme
+
+func (m SchemeMap) Verify(r *Record, sig []byte) error {
+	s := m[r.IdentityScheme()]
+	if s == nil {
+		return ErrInvalidSig
+	}
+	return s.Verify(r, sig)
+}
+
+func (m SchemeMap) NodeAddr(r *Record) []byte {
+	s := m[r.IdentityScheme()]
+	if s == nil {
+		return nil
+	}
+	return s.NodeAddr(r)
+}
+
 // Record represents a node record. The zero value is an empty record.
 // Record represents a node record. The zero value is an empty record.
 type Record struct {
 type Record struct {
 	seq       uint64 // sequence number
 	seq       uint64 // sequence number
@@ -64,11 +95,6 @@ type pair struct {
 	v rlp.RawValue
 	v rlp.RawValue
 }
 }
 
 
-// Signed reports whether the record has a valid signature.
-func (r *Record) Signed() bool {
-	return r.signature != nil
-}
-
 // Seq returns the sequence number.
 // Seq returns the sequence number.
 func (r *Record) Seq() uint64 {
 func (r *Record) Seq() uint64 {
 	return r.seq
 	return r.seq
@@ -140,7 +166,7 @@ func (r *Record) invalidate() {
 // EncodeRLP implements rlp.Encoder. Encoding fails if
 // EncodeRLP implements rlp.Encoder. Encoding fails if
 // the record is unsigned.
 // the record is unsigned.
 func (r Record) EncodeRLP(w io.Writer) error {
 func (r Record) EncodeRLP(w io.Writer) error {
-	if !r.Signed() {
+	if r.signature == nil {
 		return errEncodeUnsigned
 		return errEncodeUnsigned
 	}
 	}
 	_, err := w.Write(r.raw)
 	_, err := w.Write(r.raw)
@@ -149,25 +175,34 @@ func (r Record) EncodeRLP(w io.Writer) error {
 
 
 // DecodeRLP implements rlp.Decoder. Decoding verifies the signature.
 // DecodeRLP implements rlp.Decoder. Decoding verifies the signature.
 func (r *Record) DecodeRLP(s *rlp.Stream) error {
 func (r *Record) DecodeRLP(s *rlp.Stream) error {
-	raw, err := s.Raw()
+	dec, raw, err := decodeRecord(s)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+	*r = dec
+	r.raw = raw
+	return nil
+}
+
+func decodeRecord(s *rlp.Stream) (dec Record, raw []byte, err error) {
+	raw, err = s.Raw()
+	if err != nil {
+		return dec, raw, err
+	}
 	if len(raw) > SizeLimit {
 	if len(raw) > SizeLimit {
-		return errTooBig
+		return dec, raw, errTooBig
 	}
 	}
 
 
 	// Decode the RLP container.
 	// Decode the RLP container.
-	dec := Record{raw: raw}
 	s = rlp.NewStream(bytes.NewReader(raw), 0)
 	s = rlp.NewStream(bytes.NewReader(raw), 0)
 	if _, err := s.List(); err != nil {
 	if _, err := s.List(); err != nil {
-		return err
+		return dec, raw, err
 	}
 	}
 	if err = s.Decode(&dec.signature); err != nil {
 	if err = s.Decode(&dec.signature); err != nil {
-		return err
+		return dec, raw, err
 	}
 	}
 	if err = s.Decode(&dec.seq); err != nil {
 	if err = s.Decode(&dec.seq); err != nil {
-		return err
+		return dec, raw, err
 	}
 	}
 	// The rest of the record contains sorted k/v pairs.
 	// The rest of the record contains sorted k/v pairs.
 	var prevkey string
 	var prevkey string
@@ -177,73 +212,68 @@ func (r *Record) DecodeRLP(s *rlp.Stream) error {
 			if err == rlp.EOL {
 			if err == rlp.EOL {
 				break
 				break
 			}
 			}
-			return err
+			return dec, raw, err
 		}
 		}
 		if err := s.Decode(&kv.v); err != nil {
 		if err := s.Decode(&kv.v); err != nil {
 			if err == rlp.EOL {
 			if err == rlp.EOL {
-				return errIncompletePair
+				return dec, raw, errIncompletePair
 			}
 			}
-			return err
+			return dec, raw, err
 		}
 		}
 		if i > 0 {
 		if i > 0 {
 			if kv.k == prevkey {
 			if kv.k == prevkey {
-				return errDuplicateKey
+				return dec, raw, errDuplicateKey
 			}
 			}
 			if kv.k < prevkey {
 			if kv.k < prevkey {
-				return errNotSorted
+				return dec, raw, errNotSorted
 			}
 			}
 		}
 		}
 		dec.pairs = append(dec.pairs, kv)
 		dec.pairs = append(dec.pairs, kv)
 		prevkey = kv.k
 		prevkey = kv.k
 	}
 	}
-	if err := s.ListEnd(); err != nil {
-		return err
-	}
+	return dec, raw, s.ListEnd()
+}
 
 
-	_, scheme := dec.idScheme()
-	if scheme == nil {
-		return errNoID
-	}
-	if err := scheme.Verify(&dec, dec.signature); err != nil {
-		return err
-	}
-	*r = dec
-	return nil
+// IdentityScheme returns the name of the identity scheme in the record.
+func (r *Record) IdentityScheme() string {
+	var id ID
+	r.Load(&id)
+	return string(id)
 }
 }
 
 
-// NodeAddr returns the node address. The return value will be nil if the record is
-// unsigned or uses an unknown identity scheme.
-func (r *Record) NodeAddr() []byte {
-	_, scheme := r.idScheme()
-	if scheme == nil {
-		return nil
-	}
-	return scheme.NodeAddr(r)
+// VerifySignature checks whether the record is signed using the given identity scheme.
+func (r *Record) VerifySignature(s IdentityScheme) error {
+	return s.Verify(r, r.signature)
 }
 }
 
 
 // SetSig sets the record signature. It returns an error if the encoded record is larger
 // SetSig sets the record signature. It returns an error if the encoded record is larger
 // than the size limit or if the signature is invalid according to the passed scheme.
 // than the size limit or if the signature is invalid according to the passed scheme.
-func (r *Record) SetSig(idscheme string, sig []byte) error {
-	// Check that "id" is set and matches the given scheme. This panics because
-	// inconsitencies here are always implementation bugs in the signing function calling
-	// this method.
-	id, s := r.idScheme()
-	if s == nil {
-		panic(errNoID)
-	}
-	if id != idscheme {
-		panic(fmt.Errorf("identity scheme mismatch in Sign: record has %s, want %s", id, idscheme))
-	}
-
-	// Verify against the scheme.
-	if err := s.Verify(r, sig); err != nil {
-		return err
-	}
-	raw, err := r.encode(sig)
-	if err != nil {
-		return err
+//
+// You can also use SetSig to remove the signature explicitly by passing a nil scheme
+// and signature.
+//
+// SetSig panics when either the scheme or the signature (but not both) are nil.
+func (r *Record) SetSig(s IdentityScheme, sig []byte) error {
+	switch {
+	// Prevent storing invalid data.
+	case s == nil && sig != nil:
+		panic("enr: invalid call to SetSig with non-nil signature but nil scheme")
+	case s != nil && sig == nil:
+		panic("enr: invalid call to SetSig with nil signature but non-nil scheme")
+	// Verify if we have a scheme.
+	case s != nil:
+		if err := s.Verify(r, sig); err != nil {
+			return err
+		}
+		raw, err := r.encode(sig)
+		if err != nil {
+			return err
+		}
+		r.signature, r.raw = sig, raw
+	// Reset otherwise.
+	default:
+		r.signature, r.raw = nil, nil
 	}
 	}
-	r.signature, r.raw = sig, raw
 	return nil
 	return nil
 }
 }
 
 
@@ -268,11 +298,3 @@ func (r *Record) encode(sig []byte) (raw []byte, err error) {
 	}
 	}
 	return raw, nil
 	return raw, nil
 }
 }
-
-func (r *Record) idScheme() (string, IdentityScheme) {
-	var id ID
-	if err := r.Load(&id); err != nil {
-		return "", nil
-	}
-	return string(id), FindIdentityScheme(string(id))
-}

+ 45 - 79
p2p/enr/enr_test.go

@@ -18,23 +18,17 @@ package enr
 
 
 import (
 import (
 	"bytes"
 	"bytes"
-	"encoding/hex"
+	"encoding/binary"
 	"fmt"
 	"fmt"
 	"math/rand"
 	"math/rand"
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
-	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/stretchr/testify/require"
 )
 )
 
 
-var (
-	privkey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
-	pubkey     = &privkey.PublicKey
-)
-
 var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
 var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
 
 
 func randomString(strlen int) string {
 func randomString(strlen int) string {
@@ -87,18 +81,6 @@ func TestGetSetUDP(t *testing.T) {
 	assert.Equal(t, port, port2)
 	assert.Equal(t, port, port2)
 }
 }
 
 
-// TestGetSetSecp256k1 tests encoding/decoding and setting/getting of the Secp256k1 key.
-func TestGetSetSecp256k1(t *testing.T) {
-	var r Record
-	if err := SignV4(&r, privkey); err != nil {
-		t.Fatal(err)
-	}
-
-	var pk Secp256k1
-	require.NoError(t, r.Load(&pk))
-	assert.EqualValues(t, pubkey, &pk)
-}
-
 func TestLoadErrors(t *testing.T) {
 func TestLoadErrors(t *testing.T) {
 	var r Record
 	var r Record
 	ip4 := IP{127, 0, 0, 1}
 	ip4 := IP{127, 0, 0, 1}
@@ -167,23 +149,20 @@ func TestSortedGetAndSet(t *testing.T) {
 func TestDirty(t *testing.T) {
 func TestDirty(t *testing.T) {
 	var r Record
 	var r Record
 
 
-	if r.Signed() {
-		t.Error("Signed returned true for zero record")
-	}
 	if _, err := rlp.EncodeToBytes(r); err != errEncodeUnsigned {
 	if _, err := rlp.EncodeToBytes(r); err != errEncodeUnsigned {
 		t.Errorf("expected errEncodeUnsigned, got %#v", err)
 		t.Errorf("expected errEncodeUnsigned, got %#v", err)
 	}
 	}
 
 
-	require.NoError(t, SignV4(&r, privkey))
-	if !r.Signed() {
-		t.Error("Signed return false for signed record")
+	require.NoError(t, signTest([]byte{5}, &r))
+	if len(r.signature) == 0 {
+		t.Error("record is not signed")
 	}
 	}
 	_, err := rlp.EncodeToBytes(r)
 	_, err := rlp.EncodeToBytes(r)
 	assert.NoError(t, err)
 	assert.NoError(t, err)
 
 
 	r.SetSeq(3)
 	r.SetSeq(3)
-	if r.Signed() {
-		t.Error("Signed returned true for modified record")
+	if len(r.signature) != 0 {
+		t.Error("signature still set after modification")
 	}
 	}
 	if _, err := rlp.EncodeToBytes(r); err != errEncodeUnsigned {
 	if _, err := rlp.EncodeToBytes(r); err != errEncodeUnsigned {
 		t.Errorf("expected errEncodeUnsigned, got %#v", err)
 		t.Errorf("expected errEncodeUnsigned, got %#v", err)
@@ -210,7 +189,7 @@ func TestSignEncodeAndDecode(t *testing.T) {
 	var r Record
 	var r Record
 	r.Set(UDP(30303))
 	r.Set(UDP(30303))
 	r.Set(IP{127, 0, 0, 1})
 	r.Set(IP{127, 0, 0, 1})
-	require.NoError(t, SignV4(&r, privkey))
+	require.NoError(t, signTest([]byte{5}, &r))
 
 
 	blob, err := rlp.EncodeToBytes(r)
 	blob, err := rlp.EncodeToBytes(r)
 	require.NoError(t, err)
 	require.NoError(t, err)
@@ -224,48 +203,6 @@ func TestSignEncodeAndDecode(t *testing.T) {
 	assert.Equal(t, blob, blob2)
 	assert.Equal(t, blob, blob2)
 }
 }
 
 
-func TestNodeAddr(t *testing.T) {
-	var r Record
-	if addr := r.NodeAddr(); addr != nil {
-		t.Errorf("wrong address on empty record: got %v, want %v", addr, nil)
-	}
-
-	require.NoError(t, SignV4(&r, privkey))
-	expected := "a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7"
-	assert.Equal(t, expected, hex.EncodeToString(r.NodeAddr()))
-}
-
-var pyRecord, _ = hex.DecodeString("f884b8407098ad865b00a582051940cb9cf36836572411a47278783077011599ed5cd16b76f2635f4e234738f30813a89eb9137e3e3df5266e3a1f11df72ecf1145ccb9c01826964827634826970847f00000189736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31388375647082765f")
-
-// TestPythonInterop checks that we can decode and verify a record produced by the Python
-// implementation.
-func TestPythonInterop(t *testing.T) {
-	var r Record
-	if err := rlp.DecodeBytes(pyRecord, &r); err != nil {
-		t.Fatalf("can't decode: %v", err)
-	}
-
-	var (
-		wantAddr, _ = hex.DecodeString("a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7")
-		wantSeq     = uint64(1)
-		wantIP      = IP{127, 0, 0, 1}
-		wantUDP     = UDP(30303)
-	)
-	if r.Seq() != wantSeq {
-		t.Errorf("wrong seq: got %d, want %d", r.Seq(), wantSeq)
-	}
-	if addr := r.NodeAddr(); !bytes.Equal(addr, wantAddr) {
-		t.Errorf("wrong addr: got %x, want %x", addr, wantAddr)
-	}
-	want := map[Entry]interface{}{new(IP): &wantIP, new(UDP): &wantUDP}
-	for k, v := range want {
-		desc := fmt.Sprintf("loading key %q", k.ENRKey())
-		if assert.NoError(t, r.Load(k), desc) {
-			assert.Equal(t, k, v, desc)
-		}
-	}
-}
-
 // TestRecordTooBig tests that records bigger than SizeLimit bytes cannot be signed.
 // TestRecordTooBig tests that records bigger than SizeLimit bytes cannot be signed.
 func TestRecordTooBig(t *testing.T) {
 func TestRecordTooBig(t *testing.T) {
 	var r Record
 	var r Record
@@ -273,13 +210,13 @@ func TestRecordTooBig(t *testing.T) {
 
 
 	// set a big value for random key, expect error
 	// set a big value for random key, expect error
 	r.Set(WithEntry(key, randomString(SizeLimit)))
 	r.Set(WithEntry(key, randomString(SizeLimit)))
-	if err := SignV4(&r, privkey); err != errTooBig {
+	if err := signTest([]byte{5}, &r); err != errTooBig {
 		t.Fatalf("expected to get errTooBig, got %#v", err)
 		t.Fatalf("expected to get errTooBig, got %#v", err)
 	}
 	}
 
 
 	// set an acceptable value for random key, expect no error
 	// set an acceptable value for random key, expect no error
 	r.Set(WithEntry(key, randomString(100)))
 	r.Set(WithEntry(key, randomString(100)))
-	require.NoError(t, SignV4(&r, privkey))
+	require.NoError(t, signTest([]byte{5}, &r))
 }
 }
 
 
 // TestSignEncodeAndDecodeRandom tests encoding/decoding of records containing random key/value pairs.
 // TestSignEncodeAndDecodeRandom tests encoding/decoding of records containing random key/value pairs.
@@ -295,7 +232,7 @@ func TestSignEncodeAndDecodeRandom(t *testing.T) {
 		r.Set(WithEntry(key, &value))
 		r.Set(WithEntry(key, &value))
 	}
 	}
 
 
-	require.NoError(t, SignV4(&r, privkey))
+	require.NoError(t, signTest([]byte{5}, &r))
 	_, err := rlp.EncodeToBytes(r)
 	_, err := rlp.EncodeToBytes(r)
 	require.NoError(t, err)
 	require.NoError(t, err)
 
 
@@ -308,11 +245,40 @@ func TestSignEncodeAndDecodeRandom(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func BenchmarkDecode(b *testing.B) {
-	var r Record
-	for i := 0; i < b.N; i++ {
-		rlp.DecodeBytes(pyRecord, &r)
+type testSig struct{}
+
+type testID []byte
+
+func (id testID) ENRKey() string { return "testid" }
+
+func signTest(id []byte, r *Record) error {
+	r.Set(ID("test"))
+	r.Set(testID(id))
+	return r.SetSig(testSig{}, makeTestSig(id, r.Seq()))
+}
+
+func makeTestSig(id []byte, seq uint64) []byte {
+	sig := make([]byte, 8, len(id)+8)
+	binary.BigEndian.PutUint64(sig[:8], seq)
+	sig = append(sig, id...)
+	return sig
+}
+
+func (testSig) Verify(r *Record, sig []byte) error {
+	var id []byte
+	if err := r.Load((*testID)(&id)); err != nil {
+		return err
+	}
+	if !bytes.Equal(sig, makeTestSig(id, r.Seq())) {
+		return ErrInvalidSig
+	}
+	return nil
+}
+
+func (testSig) NodeAddr(r *Record) []byte {
+	var id []byte
+	if err := r.Load((*testID)(&id)); err != nil {
+		return nil
 	}
 	}
-	b.StopTimer()
-	r.NodeAddr()
+	return id
 }
 }

+ 0 - 26
p2p/enr/entries.go

@@ -17,12 +17,10 @@
 package enr
 package enr
 
 
 import (
 import (
-	"crypto/ecdsa"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"net"
 	"net"
 
 
-	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
 )
 )
 
 
@@ -98,30 +96,6 @@ func (v *IP) DecodeRLP(s *rlp.Stream) error {
 	return nil
 	return nil
 }
 }
 
 
-// Secp256k1 is the "secp256k1" key, which holds a public key.
-type Secp256k1 ecdsa.PublicKey
-
-func (v Secp256k1) ENRKey() string { return "secp256k1" }
-
-// EncodeRLP implements rlp.Encoder.
-func (v Secp256k1) EncodeRLP(w io.Writer) error {
-	return rlp.Encode(w, crypto.CompressPubkey((*ecdsa.PublicKey)(&v)))
-}
-
-// DecodeRLP implements rlp.Decoder.
-func (v *Secp256k1) DecodeRLP(s *rlp.Stream) error {
-	buf, err := s.Bytes()
-	if err != nil {
-		return err
-	}
-	pk, err := crypto.DecompressPubkey(buf)
-	if err != nil {
-		return err
-	}
-	*v = (Secp256k1)(*pk)
-	return nil
-}
-
 // KeyError is an error related to a key.
 // KeyError is an error related to a key.
 type KeyError struct {
 type KeyError struct {
 	Key string
 	Key string

+ 0 - 36
p2p/enr/idscheme_test.go

@@ -1,36 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package enr
-
-import (
-	"crypto/ecdsa"
-	"math/big"
-	"testing"
-)
-
-// Checks that failure to sign leaves the record unmodified.
-func TestSignError(t *testing.T) {
-	invalidKey := &ecdsa.PrivateKey{D: new(big.Int), PublicKey: *pubkey}
-
-	var r Record
-	if err := SignV4(&r, invalidKey); err == nil {
-		t.Fatal("expected error from SignV4")
-	}
-	if len(r.pairs) > 0 {
-		t.Fatal("expected empty record, have", r.pairs)
-	}
-}

+ 3 - 3
p2p/message.go

@@ -26,7 +26,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/event"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
 )
 )
 
 
@@ -253,13 +253,13 @@ type msgEventer struct {
 	MsgReadWriter
 	MsgReadWriter
 
 
 	feed     *event.Feed
 	feed     *event.Feed
-	peerID   discover.NodeID
+	peerID   enode.ID
 	Protocol string
 	Protocol string
 }
 }
 
 
 // newMsgEventer returns a msgEventer which sends message events to the given
 // newMsgEventer returns a msgEventer which sends message events to the given
 // feed
 // feed
-func newMsgEventer(rw MsgReadWriter, feed *event.Feed, peerID discover.NodeID, proto string) *msgEventer {
+func newMsgEventer(rw MsgReadWriter, feed *event.Feed, peerID enode.ID, proto string) *msgEventer {
 	return &msgEventer{
 	return &msgEventer{
 		MsgReadWriter: rw,
 		MsgReadWriter: rw,
 		feed:          feed,
 		feed:          feed,

+ 22 - 14
p2p/peer.go

@@ -28,7 +28,8 @@ import (
 	"github.com/ethereum/go-ethereum/common/mclock"
 	"github.com/ethereum/go-ethereum/common/mclock"
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
+	"github.com/ethereum/go-ethereum/p2p/enr"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
 )
 )
 
 
@@ -60,7 +61,7 @@ type protoHandshake struct {
 	Name       string
 	Name       string
 	Caps       []Cap
 	Caps       []Cap
 	ListenPort uint64
 	ListenPort uint64
-	ID         discover.NodeID
+	ID         []byte // secp256k1 public key
 
 
 	// Ignore additional fields (for forward compatibility).
 	// Ignore additional fields (for forward compatibility).
 	Rest []rlp.RawValue `rlp:"tail"`
 	Rest []rlp.RawValue `rlp:"tail"`
@@ -90,12 +91,12 @@ const (
 // PeerEvent is an event emitted when peers are either added or dropped from
 // PeerEvent is an event emitted when peers are either added or dropped from
 // a p2p.Server or when a message is sent or received on a peer connection
 // a p2p.Server or when a message is sent or received on a peer connection
 type PeerEvent struct {
 type PeerEvent struct {
-	Type     PeerEventType   `json:"type"`
-	Peer     discover.NodeID `json:"peer"`
-	Error    string          `json:"error,omitempty"`
-	Protocol string          `json:"protocol,omitempty"`
-	MsgCode  *uint64         `json:"msg_code,omitempty"`
-	MsgSize  *uint32         `json:"msg_size,omitempty"`
+	Type     PeerEventType `json:"type"`
+	Peer     enode.ID      `json:"peer"`
+	Error    string        `json:"error,omitempty"`
+	Protocol string        `json:"protocol,omitempty"`
+	MsgCode  *uint64       `json:"msg_code,omitempty"`
+	MsgSize  *uint32       `json:"msg_size,omitempty"`
 }
 }
 
 
 // Peer represents a connected remote node.
 // Peer represents a connected remote node.
@@ -115,17 +116,23 @@ type Peer struct {
 }
 }
 
 
 // NewPeer returns a peer for testing purposes.
 // NewPeer returns a peer for testing purposes.
-func NewPeer(id discover.NodeID, name string, caps []Cap) *Peer {
+func NewPeer(id enode.ID, name string, caps []Cap) *Peer {
 	pipe, _ := net.Pipe()
 	pipe, _ := net.Pipe()
-	conn := &conn{fd: pipe, transport: nil, id: id, caps: caps, name: name}
+	node := enode.SignNull(new(enr.Record), id)
+	conn := &conn{fd: pipe, transport: nil, node: node, caps: caps, name: name}
 	peer := newPeer(conn, nil)
 	peer := newPeer(conn, nil)
 	close(peer.closed) // ensures Disconnect doesn't block
 	close(peer.closed) // ensures Disconnect doesn't block
 	return peer
 	return peer
 }
 }
 
 
 // ID returns the node's public key.
 // ID returns the node's public key.
-func (p *Peer) ID() discover.NodeID {
-	return p.rw.id
+func (p *Peer) ID() enode.ID {
+	return p.rw.node.ID()
+}
+
+// Node returns the peer's node descriptor.
+func (p *Peer) Node() *enode.Node {
+	return p.rw.node
 }
 }
 
 
 // Name returns the node name that the remote node advertised.
 // Name returns the node name that the remote node advertised.
@@ -160,7 +167,8 @@ func (p *Peer) Disconnect(reason DiscReason) {
 
 
 // String implements fmt.Stringer.
 // String implements fmt.Stringer.
 func (p *Peer) String() string {
 func (p *Peer) String() string {
-	return fmt.Sprintf("Peer %x %v", p.rw.id[:8], p.RemoteAddr())
+	id := p.ID()
+	return fmt.Sprintf("Peer %x %v", id[:8], p.RemoteAddr())
 }
 }
 
 
 // Inbound returns true if the peer is an inbound connection
 // Inbound returns true if the peer is an inbound connection
@@ -177,7 +185,7 @@ func newPeer(conn *conn, protocols []Protocol) *Peer {
 		disc:     make(chan DiscReason),
 		disc:     make(chan DiscReason),
 		protoErr: make(chan error, len(protomap)+1), // protocols + pingLoop
 		protoErr: make(chan error, len(protomap)+1), // protocols + pingLoop
 		closed:   make(chan struct{}),
 		closed:   make(chan struct{}),
-		log:      log.New("id", conn.id, "conn", conn.flags),
+		log:      log.New("id", conn.node.ID(), "conn", conn.flags),
 	}
 	}
 	return p
 	return p
 }
 }

+ 2 - 2
p2p/peer_test.go

@@ -45,8 +45,8 @@ var discard = Protocol{
 
 
 func testPeer(protos []Protocol) (func(), *conn, *Peer, <-chan error) {
 func testPeer(protos []Protocol) (func(), *conn, *Peer, <-chan error) {
 	fd1, fd2 := net.Pipe()
 	fd1, fd2 := net.Pipe()
-	c1 := &conn{fd: fd1, transport: newTestTransport(randomID(), fd1)}
-	c2 := &conn{fd: fd2, transport: newTestTransport(randomID(), fd2)}
+	c1 := &conn{fd: fd1, node: newNode(randomID(), nil), transport: newTestTransport(&newkey().PublicKey, fd1)}
+	c2 := &conn{fd: fd2, node: newNode(randomID(), nil), transport: newTestTransport(&newkey().PublicKey, fd2)}
 	for _, p := range protos {
 	for _, p := range protos {
 		c1.caps = append(c1.caps, p.cap())
 		c1.caps = append(c1.caps, p.cap())
 		c2.caps = append(c2.caps, p.cap())
 		c2.caps = append(c2.caps, p.cap())

+ 2 - 2
p2p/protocol.go

@@ -19,7 +19,7 @@ package p2p
 import (
 import (
 	"fmt"
 	"fmt"
 
 
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 )
 
 
 // Protocol represents a P2P subprotocol implementation.
 // Protocol represents a P2P subprotocol implementation.
@@ -51,7 +51,7 @@ type Protocol struct {
 	// PeerInfo is an optional helper method to retrieve protocol specific metadata
 	// PeerInfo is an optional helper method to retrieve protocol specific metadata
 	// about a certain peer in the network. If an info retrieval function is set,
 	// about a certain peer in the network. If an info retrieval function is set,
 	// but returns nil, it is assumed that the protocol handshake is still running.
 	// but returns nil, it is assumed that the protocol handshake is still running.
-	PeerInfo func(id discover.NodeID) interface{}
+	PeerInfo func(id enode.ID) interface{}
 }
 }
 
 
 func (p Protocol) cap() Cap {
 func (p Protocol) cap() Cap {

+ 22 - 22
p2p/protocols/protocol_test.go

@@ -24,7 +24,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
 	p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
 )
 )
@@ -36,7 +36,7 @@ type hs0 struct {
 
 
 // message to kill/drop the peer with nodeID
 // message to kill/drop the peer with nodeID
 type kill struct {
 type kill struct {
-	C discover.NodeID
+	C enode.ID
 }
 }
 
 
 // message to drop connection
 // message to drop connection
@@ -144,7 +144,7 @@ func protocolTester(t *testing.T, pp *p2ptest.TestPeerPool) *p2ptest.ProtocolTes
 	return p2ptest.NewProtocolTester(t, conf.ID, 2, newProtocol(pp))
 	return p2ptest.NewProtocolTester(t, conf.ID, 2, newProtocol(pp))
 }
 }
 
 
-func protoHandshakeExchange(id discover.NodeID, proto *protoHandshake) []p2ptest.Exchange {
+func protoHandshakeExchange(id enode.ID, proto *protoHandshake) []p2ptest.Exchange {
 
 
 	return []p2ptest.Exchange{
 	return []p2ptest.Exchange{
 		{
 		{
@@ -172,13 +172,13 @@ func runProtoHandshake(t *testing.T, proto *protoHandshake, errs ...error) {
 	pp := p2ptest.NewTestPeerPool()
 	pp := p2ptest.NewTestPeerPool()
 	s := protocolTester(t, pp)
 	s := protocolTester(t, pp)
 	// TODO: make this more than one handshake
 	// TODO: make this more than one handshake
-	id := s.IDs[0]
-	if err := s.TestExchanges(protoHandshakeExchange(id, proto)...); err != nil {
+	node := s.Nodes[0]
+	if err := s.TestExchanges(protoHandshakeExchange(node.ID(), proto)...); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	var disconnects []*p2ptest.Disconnect
 	var disconnects []*p2ptest.Disconnect
 	for i, err := range errs {
 	for i, err := range errs {
-		disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.IDs[i], Error: err})
+		disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err})
 	}
 	}
 	if err := s.TestDisconnected(disconnects...); err != nil {
 	if err := s.TestDisconnected(disconnects...); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -197,7 +197,7 @@ func TestProtoHandshakeSuccess(t *testing.T) {
 	runProtoHandshake(t, &protoHandshake{42, "420"})
 	runProtoHandshake(t, &protoHandshake{42, "420"})
 }
 }
 
 
-func moduleHandshakeExchange(id discover.NodeID, resp uint) []p2ptest.Exchange {
+func moduleHandshakeExchange(id enode.ID, resp uint) []p2ptest.Exchange {
 
 
 	return []p2ptest.Exchange{
 	return []p2ptest.Exchange{
 		{
 		{
@@ -224,16 +224,16 @@ func moduleHandshakeExchange(id discover.NodeID, resp uint) []p2ptest.Exchange {
 func runModuleHandshake(t *testing.T, resp uint, errs ...error) {
 func runModuleHandshake(t *testing.T, resp uint, errs ...error) {
 	pp := p2ptest.NewTestPeerPool()
 	pp := p2ptest.NewTestPeerPool()
 	s := protocolTester(t, pp)
 	s := protocolTester(t, pp)
-	id := s.IDs[0]
-	if err := s.TestExchanges(protoHandshakeExchange(id, &protoHandshake{42, "420"})...); err != nil {
+	node := s.Nodes[0]
+	if err := s.TestExchanges(protoHandshakeExchange(node.ID(), &protoHandshake{42, "420"})...); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if err := s.TestExchanges(moduleHandshakeExchange(id, resp)...); err != nil {
+	if err := s.TestExchanges(moduleHandshakeExchange(node.ID(), resp)...); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	var disconnects []*p2ptest.Disconnect
 	var disconnects []*p2ptest.Disconnect
 	for i, err := range errs {
 	for i, err := range errs {
-		disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.IDs[i], Error: err})
+		disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err})
 	}
 	}
 	if err := s.TestDisconnected(disconnects...); err != nil {
 	if err := s.TestDisconnected(disconnects...); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -249,7 +249,7 @@ func TestModuleHandshakeSuccess(t *testing.T) {
 }
 }
 
 
 // testing complex interactions over multiple peers, relaying, dropping
 // testing complex interactions over multiple peers, relaying, dropping
-func testMultiPeerSetup(a, b discover.NodeID) []p2ptest.Exchange {
+func testMultiPeerSetup(a, b enode.ID) []p2ptest.Exchange {
 
 
 	return []p2ptest.Exchange{
 	return []p2ptest.Exchange{
 		{
 		{
@@ -305,7 +305,7 @@ func runMultiplePeers(t *testing.T, peer int, errs ...error) {
 	pp := p2ptest.NewTestPeerPool()
 	pp := p2ptest.NewTestPeerPool()
 	s := protocolTester(t, pp)
 	s := protocolTester(t, pp)
 
 
-	if err := s.TestExchanges(testMultiPeerSetup(s.IDs[0], s.IDs[1])...); err != nil {
+	if err := s.TestExchanges(testMultiPeerSetup(s.Nodes[0].ID(), s.Nodes[1].ID())...); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	// after some exchanges of messages, we can test state changes
 	// after some exchanges of messages, we can test state changes
@@ -318,15 +318,15 @@ WAIT:
 	for {
 	for {
 		select {
 		select {
 		case <-tick.C:
 		case <-tick.C:
-			if pp.Has(s.IDs[0]) {
+			if pp.Has(s.Nodes[0].ID()) {
 				break WAIT
 				break WAIT
 			}
 			}
 		case <-timeout.C:
 		case <-timeout.C:
 			t.Fatal("timeout")
 			t.Fatal("timeout")
 		}
 		}
 	}
 	}
-	if !pp.Has(s.IDs[1]) {
-		t.Fatalf("missing peer test-1: %v (%v)", pp, s.IDs)
+	if !pp.Has(s.Nodes[1].ID()) {
+		t.Fatalf("missing peer test-1: %v (%v)", pp, s.Nodes)
 	}
 	}
 
 
 	// peer 0 sends kill request for peer with index <peer>
 	// peer 0 sends kill request for peer with index <peer>
@@ -334,8 +334,8 @@ WAIT:
 		Triggers: []p2ptest.Trigger{
 		Triggers: []p2ptest.Trigger{
 			{
 			{
 				Code: 2,
 				Code: 2,
-				Msg:  &kill{s.IDs[peer]},
-				Peer: s.IDs[0],
+				Msg:  &kill{s.Nodes[peer].ID()},
+				Peer: s.Nodes[0].ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -350,7 +350,7 @@ WAIT:
 			{
 			{
 				Code: 3,
 				Code: 3,
 				Msg:  &drop{},
 				Msg:  &drop{},
-				Peer: s.IDs[(peer+1)%2],
+				Peer: s.Nodes[(peer+1)%2].ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -362,14 +362,14 @@ WAIT:
 	// check the actual discconnect errors on the individual peers
 	// check the actual discconnect errors on the individual peers
 	var disconnects []*p2ptest.Disconnect
 	var disconnects []*p2ptest.Disconnect
 	for i, err := range errs {
 	for i, err := range errs {
-		disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.IDs[i], Error: err})
+		disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err})
 	}
 	}
 	if err := s.TestDisconnected(disconnects...); err != nil {
 	if err := s.TestDisconnected(disconnects...); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	// test if disconnected peers have been removed from peerPool
 	// test if disconnected peers have been removed from peerPool
-	if pp.Has(s.IDs[peer]) {
-		t.Fatalf("peer test-%v not dropped: %v (%v)", peer, pp, s.IDs)
+	if pp.Has(s.Nodes[peer].ID()) {
+		t.Fatalf("peer test-%v not dropped: %v (%v)", peer, pp, s.Nodes)
 	}
 	}
 
 
 }
 }

+ 24 - 31
p2p/rlpx.go

@@ -35,11 +35,11 @@ import (
 	"sync"
 	"sync"
 	"time"
 	"time"
 
 
+	"github.com/ethereum/go-ethereum/common/bitutil"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto/ecies"
 	"github.com/ethereum/go-ethereum/crypto/ecies"
 	"github.com/ethereum/go-ethereum/crypto/secp256k1"
 	"github.com/ethereum/go-ethereum/crypto/secp256k1"
 	"github.com/ethereum/go-ethereum/crypto/sha3"
 	"github.com/ethereum/go-ethereum/crypto/sha3"
-	"github.com/ethereum/go-ethereum/p2p/discover"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/golang/snappy"
 	"github.com/golang/snappy"
 )
 )
@@ -165,7 +165,7 @@ func readProtocolHandshake(rw MsgReader, our *protoHandshake) (*protoHandshake,
 	if err := msg.Decode(&hs); err != nil {
 	if err := msg.Decode(&hs); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	if (hs.ID == discover.NodeID{}) {
+	if len(hs.ID) != 64 || !bitutil.TestBytes(hs.ID) {
 		return nil, DiscInvalidIdentity
 		return nil, DiscInvalidIdentity
 	}
 	}
 	return &hs, nil
 	return &hs, nil
@@ -175,7 +175,7 @@ func readProtocolHandshake(rw MsgReader, our *protoHandshake) (*protoHandshake,
 // messages. the protocol handshake is the first authenticated message
 // messages. the protocol handshake is the first authenticated message
 // and also verifies whether the encryption handshake 'worked' and the
 // and also verifies whether the encryption handshake 'worked' and the
 // remote side actually provided the right public key.
 // remote side actually provided the right public key.
-func (t *rlpx) doEncHandshake(prv *ecdsa.PrivateKey, dial *discover.Node) (discover.NodeID, error) {
+func (t *rlpx) doEncHandshake(prv *ecdsa.PrivateKey, dial *ecdsa.PublicKey) (*ecdsa.PublicKey, error) {
 	var (
 	var (
 		sec secrets
 		sec secrets
 		err error
 		err error
@@ -183,23 +183,21 @@ func (t *rlpx) doEncHandshake(prv *ecdsa.PrivateKey, dial *discover.Node) (disco
 	if dial == nil {
 	if dial == nil {
 		sec, err = receiverEncHandshake(t.fd, prv)
 		sec, err = receiverEncHandshake(t.fd, prv)
 	} else {
 	} else {
-		sec, err = initiatorEncHandshake(t.fd, prv, dial.ID)
+		sec, err = initiatorEncHandshake(t.fd, prv, dial)
 	}
 	}
 	if err != nil {
 	if err != nil {
-		return discover.NodeID{}, err
+		return nil, err
 	}
 	}
 	t.wmu.Lock()
 	t.wmu.Lock()
 	t.rw = newRLPXFrameRW(t.fd, sec)
 	t.rw = newRLPXFrameRW(t.fd, sec)
 	t.wmu.Unlock()
 	t.wmu.Unlock()
-	return sec.RemoteID, nil
+	return sec.Remote.ExportECDSA(), nil
 }
 }
 
 
 // encHandshake contains the state of the encryption handshake.
 // encHandshake contains the state of the encryption handshake.
 type encHandshake struct {
 type encHandshake struct {
-	initiator bool
-	remoteID  discover.NodeID
-
-	remotePub            *ecies.PublicKey  // remote-pubk
+	initiator            bool
+	remote               *ecies.PublicKey  // remote-pubk
 	initNonce, respNonce []byte            // nonce
 	initNonce, respNonce []byte            // nonce
 	randomPrivKey        *ecies.PrivateKey // ecdhe-random
 	randomPrivKey        *ecies.PrivateKey // ecdhe-random
 	remoteRandomPub      *ecies.PublicKey  // ecdhe-random-pubk
 	remoteRandomPub      *ecies.PublicKey  // ecdhe-random-pubk
@@ -208,7 +206,7 @@ type encHandshake struct {
 // secrets represents the connection secrets
 // secrets represents the connection secrets
 // which are negotiated during the encryption handshake.
 // which are negotiated during the encryption handshake.
 type secrets struct {
 type secrets struct {
-	RemoteID              discover.NodeID
+	Remote                *ecies.PublicKey
 	AES, MAC              []byte
 	AES, MAC              []byte
 	EgressMAC, IngressMAC hash.Hash
 	EgressMAC, IngressMAC hash.Hash
 	Token                 []byte
 	Token                 []byte
@@ -249,9 +247,9 @@ func (h *encHandshake) secrets(auth, authResp []byte) (secrets, error) {
 	sharedSecret := crypto.Keccak256(ecdheSecret, crypto.Keccak256(h.respNonce, h.initNonce))
 	sharedSecret := crypto.Keccak256(ecdheSecret, crypto.Keccak256(h.respNonce, h.initNonce))
 	aesSecret := crypto.Keccak256(ecdheSecret, sharedSecret)
 	aesSecret := crypto.Keccak256(ecdheSecret, sharedSecret)
 	s := secrets{
 	s := secrets{
-		RemoteID: h.remoteID,
-		AES:      aesSecret,
-		MAC:      crypto.Keccak256(ecdheSecret, aesSecret),
+		Remote: h.remote,
+		AES:    aesSecret,
+		MAC:    crypto.Keccak256(ecdheSecret, aesSecret),
 	}
 	}
 
 
 	// setup sha3 instances for the MACs
 	// setup sha3 instances for the MACs
@@ -273,15 +271,15 @@ func (h *encHandshake) secrets(auth, authResp []byte) (secrets, error) {
 // staticSharedSecret returns the static shared secret, the result
 // staticSharedSecret returns the static shared secret, the result
 // of key agreement between the local and remote static node key.
 // of key agreement between the local and remote static node key.
 func (h *encHandshake) staticSharedSecret(prv *ecdsa.PrivateKey) ([]byte, error) {
 func (h *encHandshake) staticSharedSecret(prv *ecdsa.PrivateKey) ([]byte, error) {
-	return ecies.ImportECDSA(prv).GenerateShared(h.remotePub, sskLen, sskLen)
+	return ecies.ImportECDSA(prv).GenerateShared(h.remote, sskLen, sskLen)
 }
 }
 
 
 // initiatorEncHandshake negotiates a session token on conn.
 // initiatorEncHandshake negotiates a session token on conn.
 // it should be called on the dialing side of the connection.
 // it should be called on the dialing side of the connection.
 //
 //
 // prv is the local client's private key.
 // prv is the local client's private key.
-func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID discover.NodeID) (s secrets, err error) {
-	h := &encHandshake{initiator: true, remoteID: remoteID}
+func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remote *ecdsa.PublicKey) (s secrets, err error) {
+	h := &encHandshake{initiator: true, remote: ecies.ImportECDSAPublic(remote)}
 	authMsg, err := h.makeAuthMsg(prv)
 	authMsg, err := h.makeAuthMsg(prv)
 	if err != nil {
 	if err != nil {
 		return s, err
 		return s, err
@@ -307,14 +305,10 @@ func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID d
 
 
 // makeAuthMsg creates the initiator handshake message.
 // makeAuthMsg creates the initiator handshake message.
 func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey) (*authMsgV4, error) {
 func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey) (*authMsgV4, error) {
-	rpub, err := h.remoteID.Pubkey()
-	if err != nil {
-		return nil, fmt.Errorf("bad remoteID: %v", err)
-	}
-	h.remotePub = ecies.ImportECDSAPublic(rpub)
 	// Generate random initiator nonce.
 	// Generate random initiator nonce.
 	h.initNonce = make([]byte, shaLen)
 	h.initNonce = make([]byte, shaLen)
-	if _, err := rand.Read(h.initNonce); err != nil {
+	_, err := rand.Read(h.initNonce)
+	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	// Generate random keypair to for ECDH.
 	// Generate random keypair to for ECDH.
@@ -384,13 +378,12 @@ func receiverEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey) (s secrets,
 
 
 func (h *encHandshake) handleAuthMsg(msg *authMsgV4, prv *ecdsa.PrivateKey) error {
 func (h *encHandshake) handleAuthMsg(msg *authMsgV4, prv *ecdsa.PrivateKey) error {
 	// Import the remote identity.
 	// Import the remote identity.
-	h.initNonce = msg.Nonce[:]
-	h.remoteID = msg.InitiatorPubkey
-	rpub, err := h.remoteID.Pubkey()
+	rpub, err := importPublicKey(msg.InitiatorPubkey[:])
 	if err != nil {
 	if err != nil {
-		return fmt.Errorf("bad remoteID: %#v", err)
+		return err
 	}
 	}
-	h.remotePub = ecies.ImportECDSAPublic(rpub)
+	h.initNonce = msg.Nonce[:]
+	h.remote = rpub
 
 
 	// Generate random keypair for ECDH.
 	// Generate random keypair for ECDH.
 	// If a private key is already set, use it instead of generating one (for testing).
 	// If a private key is already set, use it instead of generating one (for testing).
@@ -436,7 +429,7 @@ func (msg *authMsgV4) sealPlain(h *encHandshake) ([]byte, error) {
 	n += copy(buf[n:], msg.InitiatorPubkey[:])
 	n += copy(buf[n:], msg.InitiatorPubkey[:])
 	n += copy(buf[n:], msg.Nonce[:])
 	n += copy(buf[n:], msg.Nonce[:])
 	buf[n] = 0 // token-flag
 	buf[n] = 0 // token-flag
-	return ecies.Encrypt(rand.Reader, h.remotePub, buf, nil, nil)
+	return ecies.Encrypt(rand.Reader, h.remote, buf, nil, nil)
 }
 }
 
 
 func (msg *authMsgV4) decodePlain(input []byte) {
 func (msg *authMsgV4) decodePlain(input []byte) {
@@ -452,7 +445,7 @@ func (msg *authRespV4) sealPlain(hs *encHandshake) ([]byte, error) {
 	buf := make([]byte, authRespLen)
 	buf := make([]byte, authRespLen)
 	n := copy(buf, msg.RandomPubkey[:])
 	n := copy(buf, msg.RandomPubkey[:])
 	copy(buf[n:], msg.Nonce[:])
 	copy(buf[n:], msg.Nonce[:])
-	return ecies.Encrypt(rand.Reader, hs.remotePub, buf, nil, nil)
+	return ecies.Encrypt(rand.Reader, hs.remote, buf, nil, nil)
 }
 }
 
 
 func (msg *authRespV4) decodePlain(input []byte) {
 func (msg *authRespV4) decodePlain(input []byte) {
@@ -475,7 +468,7 @@ func sealEIP8(msg interface{}, h *encHandshake) ([]byte, error) {
 	prefix := make([]byte, 2)
 	prefix := make([]byte, 2)
 	binary.BigEndian.PutUint16(prefix, uint16(buf.Len()+eciesOverhead))
 	binary.BigEndian.PutUint16(prefix, uint16(buf.Len()+eciesOverhead))
 
 
-	enc, err := ecies.Encrypt(rand.Reader, h.remotePub, buf.Bytes(), nil, prefix)
+	enc, err := ecies.Encrypt(rand.Reader, h.remote, buf.Bytes(), nil, prefix)
 	return append(prefix, enc...), err
 	return append(prefix, enc...), err
 }
 }
 
 

+ 20 - 23
p2p/rlpx_test.go

@@ -18,6 +18,7 @@ package p2p
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"crypto/ecdsa"
 	"crypto/rand"
 	"crypto/rand"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
@@ -34,7 +35,6 @@ import (
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto/ecies"
 	"github.com/ethereum/go-ethereum/crypto/ecies"
 	"github.com/ethereum/go-ethereum/crypto/sha3"
 	"github.com/ethereum/go-ethereum/crypto/sha3"
-	"github.com/ethereum/go-ethereum/p2p/discover"
 	"github.com/ethereum/go-ethereum/p2p/simulations/pipes"
 	"github.com/ethereum/go-ethereum/p2p/simulations/pipes"
 	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
 )
 )
@@ -80,9 +80,9 @@ func TestEncHandshake(t *testing.T) {
 
 
 func testEncHandshake(token []byte) error {
 func testEncHandshake(token []byte) error {
 	type result struct {
 	type result struct {
-		side string
-		id   discover.NodeID
-		err  error
+		side   string
+		pubkey *ecdsa.PublicKey
+		err    error
 	}
 	}
 	var (
 	var (
 		prv0, _  = crypto.GenerateKey()
 		prv0, _  = crypto.GenerateKey()
@@ -97,14 +97,12 @@ func testEncHandshake(token []byte) error {
 		defer func() { output <- r }()
 		defer func() { output <- r }()
 		defer fd0.Close()
 		defer fd0.Close()
 
 
-		dest := &discover.Node{ID: discover.PubkeyID(&prv1.PublicKey)}
-		r.id, r.err = c0.doEncHandshake(prv0, dest)
+		r.pubkey, r.err = c0.doEncHandshake(prv0, &prv1.PublicKey)
 		if r.err != nil {
 		if r.err != nil {
 			return
 			return
 		}
 		}
-		id1 := discover.PubkeyID(&prv1.PublicKey)
-		if r.id != id1 {
-			r.err = fmt.Errorf("remote ID mismatch: got %v, want: %v", r.id, id1)
+		if !reflect.DeepEqual(r.pubkey, &prv1.PublicKey) {
+			r.err = fmt.Errorf("remote pubkey mismatch: got %v, want: %v", r.pubkey, &prv1.PublicKey)
 		}
 		}
 	}()
 	}()
 	go func() {
 	go func() {
@@ -112,13 +110,12 @@ func testEncHandshake(token []byte) error {
 		defer func() { output <- r }()
 		defer func() { output <- r }()
 		defer fd1.Close()
 		defer fd1.Close()
 
 
-		r.id, r.err = c1.doEncHandshake(prv1, nil)
+		r.pubkey, r.err = c1.doEncHandshake(prv1, nil)
 		if r.err != nil {
 		if r.err != nil {
 			return
 			return
 		}
 		}
-		id0 := discover.PubkeyID(&prv0.PublicKey)
-		if r.id != id0 {
-			r.err = fmt.Errorf("remote ID mismatch: got %v, want: %v", r.id, id0)
+		if !reflect.DeepEqual(r.pubkey, &prv0.PublicKey) {
+			r.err = fmt.Errorf("remote ID mismatch: got %v, want: %v", r.pubkey, &prv0.PublicKey)
 		}
 		}
 	}()
 	}()
 
 
@@ -150,12 +147,12 @@ func testEncHandshake(token []byte) error {
 func TestProtocolHandshake(t *testing.T) {
 func TestProtocolHandshake(t *testing.T) {
 	var (
 	var (
 		prv0, _ = crypto.GenerateKey()
 		prv0, _ = crypto.GenerateKey()
-		node0   = &discover.Node{ID: discover.PubkeyID(&prv0.PublicKey), IP: net.IP{1, 2, 3, 4}, TCP: 33}
-		hs0     = &protoHandshake{Version: 3, ID: node0.ID, Caps: []Cap{{"a", 0}, {"b", 2}}}
+		pub0    = crypto.FromECDSAPub(&prv0.PublicKey)[1:]
+		hs0     = &protoHandshake{Version: 3, ID: pub0, Caps: []Cap{{"a", 0}, {"b", 2}}}
 
 
 		prv1, _ = crypto.GenerateKey()
 		prv1, _ = crypto.GenerateKey()
-		node1   = &discover.Node{ID: discover.PubkeyID(&prv1.PublicKey), IP: net.IP{5, 6, 7, 8}, TCP: 44}
-		hs1     = &protoHandshake{Version: 3, ID: node1.ID, Caps: []Cap{{"c", 1}, {"d", 3}}}
+		pub1    = crypto.FromECDSAPub(&prv1.PublicKey)[1:]
+		hs1     = &protoHandshake{Version: 3, ID: pub1, Caps: []Cap{{"c", 1}, {"d", 3}}}
 
 
 		wg sync.WaitGroup
 		wg sync.WaitGroup
 	)
 	)
@@ -170,13 +167,13 @@ func TestProtocolHandshake(t *testing.T) {
 		defer wg.Done()
 		defer wg.Done()
 		defer fd0.Close()
 		defer fd0.Close()
 		rlpx := newRLPX(fd0)
 		rlpx := newRLPX(fd0)
-		remid, err := rlpx.doEncHandshake(prv0, node1)
+		rpubkey, err := rlpx.doEncHandshake(prv0, &prv1.PublicKey)
 		if err != nil {
 		if err != nil {
 			t.Errorf("dial side enc handshake failed: %v", err)
 			t.Errorf("dial side enc handshake failed: %v", err)
 			return
 			return
 		}
 		}
-		if remid != node1.ID {
-			t.Errorf("dial side remote id mismatch: got %v, want %v", remid, node1.ID)
+		if !reflect.DeepEqual(rpubkey, &prv1.PublicKey) {
+			t.Errorf("dial side remote pubkey mismatch: got %v, want %v", rpubkey, &prv1.PublicKey)
 			return
 			return
 		}
 		}
 
 
@@ -196,13 +193,13 @@ func TestProtocolHandshake(t *testing.T) {
 		defer wg.Done()
 		defer wg.Done()
 		defer fd1.Close()
 		defer fd1.Close()
 		rlpx := newRLPX(fd1)
 		rlpx := newRLPX(fd1)
-		remid, err := rlpx.doEncHandshake(prv1, nil)
+		rpubkey, err := rlpx.doEncHandshake(prv1, nil)
 		if err != nil {
 		if err != nil {
 			t.Errorf("listen side enc handshake failed: %v", err)
 			t.Errorf("listen side enc handshake failed: %v", err)
 			return
 			return
 		}
 		}
-		if remid != node0.ID {
-			t.Errorf("listen side remote id mismatch: got %v, want %v", remid, node0.ID)
+		if !reflect.DeepEqual(rpubkey, &prv0.PublicKey) {
+			t.Errorf("listen side remote pubkey mismatch: got %v, want %v", rpubkey, &prv0.PublicKey)
 			return
 			return
 		}
 		}
 
 

+ 116 - 81
p2p/server.go

@@ -18,6 +18,7 @@
 package p2p
 package p2p
 
 
 import (
 import (
+	"bytes"
 	"crypto/ecdsa"
 	"crypto/ecdsa"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
@@ -28,10 +29,12 @@ import (
 
 
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common/mclock"
 	"github.com/ethereum/go-ethereum/common/mclock"
+	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/p2p/discover"
 	"github.com/ethereum/go-ethereum/p2p/discover"
 	"github.com/ethereum/go-ethereum/p2p/discv5"
 	"github.com/ethereum/go-ethereum/p2p/discv5"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/nat"
 	"github.com/ethereum/go-ethereum/p2p/nat"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 	"github.com/ethereum/go-ethereum/p2p/netutil"
 )
 )
@@ -87,7 +90,7 @@ type Config struct {
 
 
 	// BootstrapNodes are used to establish connectivity
 	// BootstrapNodes are used to establish connectivity
 	// with the rest of the network.
 	// with the rest of the network.
-	BootstrapNodes []*discover.Node
+	BootstrapNodes []*enode.Node
 
 
 	// BootstrapNodesV5 are used to establish connectivity
 	// BootstrapNodesV5 are used to establish connectivity
 	// with the rest of the network using the V5 discovery
 	// with the rest of the network using the V5 discovery
@@ -96,11 +99,11 @@ type Config struct {
 
 
 	// Static nodes are used as pre-configured connections which are always
 	// Static nodes are used as pre-configured connections which are always
 	// maintained and re-connected on disconnects.
 	// maintained and re-connected on disconnects.
-	StaticNodes []*discover.Node
+	StaticNodes []*enode.Node
 
 
 	// Trusted nodes are used as pre-configured connections which are always
 	// Trusted nodes are used as pre-configured connections which are always
 	// allowed to connect, even above the peer limit.
 	// allowed to connect, even above the peer limit.
-	TrustedNodes []*discover.Node
+	TrustedNodes []*enode.Node
 
 
 	// Connectivity can be restricted to certain IP networks.
 	// Connectivity can be restricted to certain IP networks.
 	// If this option is set to a non-nil value, only hosts which match one of the
 	// If this option is set to a non-nil value, only hosts which match one of the
@@ -168,10 +171,10 @@ type Server struct {
 	peerOpDone chan struct{}
 	peerOpDone chan struct{}
 
 
 	quit          chan struct{}
 	quit          chan struct{}
-	addstatic     chan *discover.Node
-	removestatic  chan *discover.Node
-	addtrusted    chan *discover.Node
-	removetrusted chan *discover.Node
+	addstatic     chan *enode.Node
+	removestatic  chan *enode.Node
+	addtrusted    chan *enode.Node
+	removetrusted chan *enode.Node
 	posthandshake chan *conn
 	posthandshake chan *conn
 	addpeer       chan *conn
 	addpeer       chan *conn
 	delpeer       chan peerDrop
 	delpeer       chan peerDrop
@@ -180,7 +183,7 @@ type Server struct {
 	log           log.Logger
 	log           log.Logger
 }
 }
 
 
-type peerOpFunc func(map[discover.NodeID]*Peer)
+type peerOpFunc func(map[enode.ID]*Peer)
 
 
 type peerDrop struct {
 type peerDrop struct {
 	*Peer
 	*Peer
@@ -202,16 +205,16 @@ const (
 type conn struct {
 type conn struct {
 	fd net.Conn
 	fd net.Conn
 	transport
 	transport
+	node  *enode.Node
 	flags connFlag
 	flags connFlag
-	cont  chan error      // The run loop uses cont to signal errors to SetupConn.
-	id    discover.NodeID // valid after the encryption handshake
-	caps  []Cap           // valid after the protocol handshake
-	name  string          // valid after the protocol handshake
+	cont  chan error // The run loop uses cont to signal errors to SetupConn.
+	caps  []Cap      // valid after the protocol handshake
+	name  string     // valid after the protocol handshake
 }
 }
 
 
 type transport interface {
 type transport interface {
 	// The two handshakes.
 	// The two handshakes.
-	doEncHandshake(prv *ecdsa.PrivateKey, dialDest *discover.Node) (discover.NodeID, error)
+	doEncHandshake(prv *ecdsa.PrivateKey, dialDest *ecdsa.PublicKey) (*ecdsa.PublicKey, error)
 	doProtoHandshake(our *protoHandshake) (*protoHandshake, error)
 	doProtoHandshake(our *protoHandshake) (*protoHandshake, error)
 	// The MsgReadWriter can only be used after the encryption
 	// The MsgReadWriter can only be used after the encryption
 	// handshake has completed. The code uses conn.id to track this
 	// handshake has completed. The code uses conn.id to track this
@@ -225,8 +228,8 @@ type transport interface {
 
 
 func (c *conn) String() string {
 func (c *conn) String() string {
 	s := c.flags.String()
 	s := c.flags.String()
-	if (c.id != discover.NodeID{}) {
-		s += " " + c.id.String()
+	if (c.node.ID() != enode.ID{}) {
+		s += " " + c.node.ID().String()
 	}
 	}
 	s += " " + c.fd.RemoteAddr().String()
 	s += " " + c.fd.RemoteAddr().String()
 	return s
 	return s
@@ -279,7 +282,7 @@ func (srv *Server) Peers() []*Peer {
 	// Note: We'd love to put this function into a variable but
 	// Note: We'd love to put this function into a variable but
 	// that seems to cause a weird compiler error in some
 	// that seems to cause a weird compiler error in some
 	// environments.
 	// environments.
-	case srv.peerOp <- func(peers map[discover.NodeID]*Peer) {
+	case srv.peerOp <- func(peers map[enode.ID]*Peer) {
 		for _, p := range peers {
 		for _, p := range peers {
 			ps = append(ps, p)
 			ps = append(ps, p)
 		}
 		}
@@ -294,7 +297,7 @@ func (srv *Server) Peers() []*Peer {
 func (srv *Server) PeerCount() int {
 func (srv *Server) PeerCount() int {
 	var count int
 	var count int
 	select {
 	select {
-	case srv.peerOp <- func(ps map[discover.NodeID]*Peer) { count = len(ps) }:
+	case srv.peerOp <- func(ps map[enode.ID]*Peer) { count = len(ps) }:
 		<-srv.peerOpDone
 		<-srv.peerOpDone
 	case <-srv.quit:
 	case <-srv.quit:
 	}
 	}
@@ -304,7 +307,7 @@ func (srv *Server) PeerCount() int {
 // AddPeer connects to the given node and maintains the connection until the
 // AddPeer connects to the given node and maintains the connection until the
 // server is shut down. If the connection fails for any reason, the server will
 // server is shut down. If the connection fails for any reason, the server will
 // attempt to reconnect the peer.
 // attempt to reconnect the peer.
-func (srv *Server) AddPeer(node *discover.Node) {
+func (srv *Server) AddPeer(node *enode.Node) {
 	select {
 	select {
 	case srv.addstatic <- node:
 	case srv.addstatic <- node:
 	case <-srv.quit:
 	case <-srv.quit:
@@ -312,7 +315,7 @@ func (srv *Server) AddPeer(node *discover.Node) {
 }
 }
 
 
 // RemovePeer disconnects from the given node
 // RemovePeer disconnects from the given node
-func (srv *Server) RemovePeer(node *discover.Node) {
+func (srv *Server) RemovePeer(node *enode.Node) {
 	select {
 	select {
 	case srv.removestatic <- node:
 	case srv.removestatic <- node:
 	case <-srv.quit:
 	case <-srv.quit:
@@ -321,7 +324,7 @@ func (srv *Server) RemovePeer(node *discover.Node) {
 
 
 // AddTrustedPeer adds the given node to a reserved whitelist which allows the
 // AddTrustedPeer adds the given node to a reserved whitelist which allows the
 // node to always connect, even if the slot are full.
 // node to always connect, even if the slot are full.
-func (srv *Server) AddTrustedPeer(node *discover.Node) {
+func (srv *Server) AddTrustedPeer(node *enode.Node) {
 	select {
 	select {
 	case srv.addtrusted <- node:
 	case srv.addtrusted <- node:
 	case <-srv.quit:
 	case <-srv.quit:
@@ -329,7 +332,7 @@ func (srv *Server) AddTrustedPeer(node *discover.Node) {
 }
 }
 
 
 // RemoveTrustedPeer removes the given node from the trusted peer set.
 // RemoveTrustedPeer removes the given node from the trusted peer set.
-func (srv *Server) RemoveTrustedPeer(node *discover.Node) {
+func (srv *Server) RemoveTrustedPeer(node *enode.Node) {
 	select {
 	select {
 	case srv.removetrusted <- node:
 	case srv.removetrusted <- node:
 	case <-srv.quit:
 	case <-srv.quit:
@@ -342,36 +345,47 @@ func (srv *Server) SubscribeEvents(ch chan *PeerEvent) event.Subscription {
 }
 }
 
 
 // Self returns the local node's endpoint information.
 // Self returns the local node's endpoint information.
-func (srv *Server) Self() *discover.Node {
+func (srv *Server) Self() *enode.Node {
 	srv.lock.Lock()
 	srv.lock.Lock()
-	defer srv.lock.Unlock()
+	running, listener, ntab := srv.running, srv.listener, srv.ntab
+	srv.lock.Unlock()
 
 
-	if !srv.running {
-		return &discover.Node{IP: net.ParseIP("0.0.0.0")}
+	if !running {
+		return enode.NewV4(&srv.PrivateKey.PublicKey, net.ParseIP("0.0.0.0"), 0, 0)
 	}
 	}
-	return srv.makeSelf(srv.listener, srv.ntab)
+	return srv.makeSelf(listener, ntab)
 }
 }
 
 
-func (srv *Server) makeSelf(listener net.Listener, ntab discoverTable) *discover.Node {
-	// If the server's not running, return an empty node.
+func (srv *Server) makeSelf(listener net.Listener, ntab discoverTable) *enode.Node {
 	// If the node is running but discovery is off, manually assemble the node infos.
 	// If the node is running but discovery is off, manually assemble the node infos.
 	if ntab == nil {
 	if ntab == nil {
-		// Inbound connections disabled, use zero address.
-		if listener == nil {
-			return &discover.Node{IP: net.ParseIP("0.0.0.0"), ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)}
-		}
-		// Otherwise inject the listener address too
-		addr := listener.Addr().(*net.TCPAddr)
-		return &discover.Node{
-			ID:  discover.PubkeyID(&srv.PrivateKey.PublicKey),
-			IP:  addr.IP,
-			TCP: uint16(addr.Port),
-		}
+		addr := srv.tcpAddr(listener)
+		return enode.NewV4(&srv.PrivateKey.PublicKey, addr.IP, addr.Port, 0)
 	}
 	}
 	// Otherwise return the discovery node.
 	// Otherwise return the discovery node.
 	return ntab.Self()
 	return ntab.Self()
 }
 }
 
 
+func (srv *Server) tcpAddr(listener net.Listener) net.TCPAddr {
+	addr := net.TCPAddr{IP: net.IP{0, 0, 0, 0}}
+	if listener == nil {
+		return addr // Inbound connections disabled, use zero address.
+	}
+	// Otherwise inject the listener address too.
+	if a, ok := listener.Addr().(*net.TCPAddr); ok {
+		addr = *a
+	}
+	if srv.NAT != nil {
+		if ip, err := srv.NAT.ExternalIP(); err == nil {
+			addr.IP = ip
+		}
+	}
+	if addr.IP.IsUnspecified() {
+		addr.IP = net.IP{127, 0, 0, 1}
+	}
+	return addr
+}
+
 // Stop terminates the server and all active peer connections.
 // Stop terminates the server and all active peer connections.
 // It blocks until all active connections have been closed.
 // It blocks until all active connections have been closed.
 func (srv *Server) Stop() {
 func (srv *Server) Stop() {
@@ -445,10 +459,10 @@ func (srv *Server) Start() (err error) {
 	srv.addpeer = make(chan *conn)
 	srv.addpeer = make(chan *conn)
 	srv.delpeer = make(chan peerDrop)
 	srv.delpeer = make(chan peerDrop)
 	srv.posthandshake = make(chan *conn)
 	srv.posthandshake = make(chan *conn)
-	srv.addstatic = make(chan *discover.Node)
-	srv.removestatic = make(chan *discover.Node)
-	srv.addtrusted = make(chan *discover.Node)
-	srv.removetrusted = make(chan *discover.Node)
+	srv.addstatic = make(chan *enode.Node)
+	srv.removestatic = make(chan *enode.Node)
+	srv.addtrusted = make(chan *enode.Node)
+	srv.removetrusted = make(chan *enode.Node)
 	srv.peerOp = make(chan peerOpFunc)
 	srv.peerOp = make(chan peerOpFunc)
 	srv.peerOpDone = make(chan struct{})
 	srv.peerOpDone = make(chan struct{})
 
 
@@ -525,7 +539,8 @@ func (srv *Server) Start() (err error) {
 	dialer := newDialState(srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict)
 	dialer := newDialState(srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict)
 
 
 	// handshake
 	// handshake
-	srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)}
+	pubkey := crypto.FromECDSAPub(&srv.PrivateKey.PublicKey)
+	srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: pubkey[1:]}
 	for _, p := range srv.Protocols {
 	for _, p := range srv.Protocols {
 		srv.ourHandshake.Caps = append(srv.ourHandshake.Caps, p.cap())
 		srv.ourHandshake.Caps = append(srv.ourHandshake.Caps, p.cap())
 	}
 	}
@@ -541,7 +556,6 @@ func (srv *Server) Start() (err error) {
 
 
 	srv.loopWG.Add(1)
 	srv.loopWG.Add(1)
 	go srv.run(dialer)
 	go srv.run(dialer)
-	srv.running = true
 	return nil
 	return nil
 }
 }
 
 
@@ -568,18 +582,18 @@ func (srv *Server) startListening() error {
 }
 }
 
 
 type dialer interface {
 type dialer interface {
-	newTasks(running int, peers map[discover.NodeID]*Peer, now time.Time) []task
+	newTasks(running int, peers map[enode.ID]*Peer, now time.Time) []task
 	taskDone(task, time.Time)
 	taskDone(task, time.Time)
-	addStatic(*discover.Node)
-	removeStatic(*discover.Node)
+	addStatic(*enode.Node)
+	removeStatic(*enode.Node)
 }
 }
 
 
 func (srv *Server) run(dialstate dialer) {
 func (srv *Server) run(dialstate dialer) {
 	defer srv.loopWG.Done()
 	defer srv.loopWG.Done()
 	var (
 	var (
-		peers        = make(map[discover.NodeID]*Peer)
+		peers        = make(map[enode.ID]*Peer)
 		inboundCount = 0
 		inboundCount = 0
-		trusted      = make(map[discover.NodeID]bool, len(srv.TrustedNodes))
+		trusted      = make(map[enode.ID]bool, len(srv.TrustedNodes))
 		taskdone     = make(chan task, maxActiveDialTasks)
 		taskdone     = make(chan task, maxActiveDialTasks)
 		runningTasks []task
 		runningTasks []task
 		queuedTasks  []task // tasks that can't run yet
 		queuedTasks  []task // tasks that can't run yet
@@ -587,7 +601,7 @@ func (srv *Server) run(dialstate dialer) {
 	// Put trusted nodes into a map to speed up checks.
 	// Put trusted nodes into a map to speed up checks.
 	// Trusted peers are loaded on startup or added via AddTrustedPeer RPC.
 	// Trusted peers are loaded on startup or added via AddTrustedPeer RPC.
 	for _, n := range srv.TrustedNodes {
 	for _, n := range srv.TrustedNodes {
-		trusted[n.ID] = true
+		trusted[n.ID()] = true
 	}
 	}
 
 
 	// removes t from runningTasks
 	// removes t from runningTasks
@@ -640,27 +654,27 @@ running:
 			// stop keeping the node connected.
 			// stop keeping the node connected.
 			srv.log.Trace("Removing static node", "node", n)
 			srv.log.Trace("Removing static node", "node", n)
 			dialstate.removeStatic(n)
 			dialstate.removeStatic(n)
-			if p, ok := peers[n.ID]; ok {
+			if p, ok := peers[n.ID()]; ok {
 				p.Disconnect(DiscRequested)
 				p.Disconnect(DiscRequested)
 			}
 			}
 		case n := <-srv.addtrusted:
 		case n := <-srv.addtrusted:
 			// This channel is used by AddTrustedPeer to add an enode
 			// This channel is used by AddTrustedPeer to add an enode
 			// to the trusted node set.
 			// to the trusted node set.
 			srv.log.Trace("Adding trusted node", "node", n)
 			srv.log.Trace("Adding trusted node", "node", n)
-			trusted[n.ID] = true
+			trusted[n.ID()] = true
 			// Mark any already-connected peer as trusted
 			// Mark any already-connected peer as trusted
-			if p, ok := peers[n.ID]; ok {
+			if p, ok := peers[n.ID()]; ok {
 				p.rw.set(trustedConn, true)
 				p.rw.set(trustedConn, true)
 			}
 			}
 		case n := <-srv.removetrusted:
 		case n := <-srv.removetrusted:
 			// This channel is used by RemoveTrustedPeer to remove an enode
 			// This channel is used by RemoveTrustedPeer to remove an enode
 			// from the trusted node set.
 			// from the trusted node set.
 			srv.log.Trace("Removing trusted node", "node", n)
 			srv.log.Trace("Removing trusted node", "node", n)
-			if _, ok := trusted[n.ID]; ok {
-				delete(trusted, n.ID)
+			if _, ok := trusted[n.ID()]; ok {
+				delete(trusted, n.ID())
 			}
 			}
 			// Unmark any already-connected peer as trusted
 			// Unmark any already-connected peer as trusted
-			if p, ok := peers[n.ID]; ok {
+			if p, ok := peers[n.ID()]; ok {
 				p.rw.set(trustedConn, false)
 				p.rw.set(trustedConn, false)
 			}
 			}
 		case op := <-srv.peerOp:
 		case op := <-srv.peerOp:
@@ -677,7 +691,7 @@ running:
 		case c := <-srv.posthandshake:
 		case c := <-srv.posthandshake:
 			// A connection has passed the encryption handshake so
 			// A connection has passed the encryption handshake so
 			// the remote identity is known (but hasn't been verified yet).
 			// the remote identity is known (but hasn't been verified yet).
-			if trusted[c.id] {
+			if trusted[c.node.ID()] {
 				// Ensure that the trusted flag is set before checking against MaxPeers.
 				// Ensure that the trusted flag is set before checking against MaxPeers.
 				c.flags |= trustedConn
 				c.flags |= trustedConn
 			}
 			}
@@ -702,7 +716,7 @@ running:
 				name := truncateName(c.name)
 				name := truncateName(c.name)
 				srv.log.Debug("Adding p2p peer", "name", name, "addr", c.fd.RemoteAddr(), "peers", len(peers)+1)
 				srv.log.Debug("Adding p2p peer", "name", name, "addr", c.fd.RemoteAddr(), "peers", len(peers)+1)
 				go srv.runPeer(p)
 				go srv.runPeer(p)
-				peers[c.id] = p
+				peers[c.node.ID()] = p
 				if p.Inbound() {
 				if p.Inbound() {
 					inboundCount++
 					inboundCount++
 				}
 				}
@@ -749,7 +763,7 @@ running:
 	}
 	}
 }
 }
 
 
-func (srv *Server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, inboundCount int, c *conn) error {
+func (srv *Server) protoHandshakeChecks(peers map[enode.ID]*Peer, inboundCount int, c *conn) error {
 	// Drop connections with no matching protocols.
 	// Drop connections with no matching protocols.
 	if len(srv.Protocols) > 0 && countMatchingProtocols(srv.Protocols, c.caps) == 0 {
 	if len(srv.Protocols) > 0 && countMatchingProtocols(srv.Protocols, c.caps) == 0 {
 		return DiscUselessPeer
 		return DiscUselessPeer
@@ -759,15 +773,15 @@ func (srv *Server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, inbound
 	return srv.encHandshakeChecks(peers, inboundCount, c)
 	return srv.encHandshakeChecks(peers, inboundCount, c)
 }
 }
 
 
-func (srv *Server) encHandshakeChecks(peers map[discover.NodeID]*Peer, inboundCount int, c *conn) error {
+func (srv *Server) encHandshakeChecks(peers map[enode.ID]*Peer, inboundCount int, c *conn) error {
 	switch {
 	switch {
 	case !c.is(trustedConn|staticDialedConn) && len(peers) >= srv.MaxPeers:
 	case !c.is(trustedConn|staticDialedConn) && len(peers) >= srv.MaxPeers:
 		return DiscTooManyPeers
 		return DiscTooManyPeers
 	case !c.is(trustedConn) && c.is(inboundConn) && inboundCount >= srv.maxInboundConns():
 	case !c.is(trustedConn) && c.is(inboundConn) && inboundCount >= srv.maxInboundConns():
 		return DiscTooManyPeers
 		return DiscTooManyPeers
-	case peers[c.id] != nil:
+	case peers[c.node.ID()] != nil:
 		return DiscAlreadyConnected
 		return DiscAlreadyConnected
-	case c.id == srv.Self().ID:
+	case c.node.ID() == srv.Self().ID():
 		return DiscSelf
 		return DiscSelf
 	default:
 	default:
 		return nil
 		return nil
@@ -777,7 +791,6 @@ func (srv *Server) encHandshakeChecks(peers map[discover.NodeID]*Peer, inboundCo
 func (srv *Server) maxInboundConns() int {
 func (srv *Server) maxInboundConns() int {
 	return srv.MaxPeers - srv.maxDialedConns()
 	return srv.MaxPeers - srv.maxDialedConns()
 }
 }
-
 func (srv *Server) maxDialedConns() int {
 func (srv *Server) maxDialedConns() int {
 	if srv.NoDiscovery || srv.NoDial {
 	if srv.NoDiscovery || srv.NoDial {
 		return 0
 		return 0
@@ -797,7 +810,7 @@ type tempError interface {
 // inbound connections.
 // inbound connections.
 func (srv *Server) listenLoop() {
 func (srv *Server) listenLoop() {
 	defer srv.loopWG.Done()
 	defer srv.loopWG.Done()
-	srv.log.Info("RLPx listener up", "self", srv.makeSelf(srv.listener, srv.ntab))
+	srv.log.Info("RLPx listener up", "self", srv.Self())
 
 
 	tokens := defaultMaxPendingPeers
 	tokens := defaultMaxPendingPeers
 	if srv.MaxPendingPeers > 0 {
 	if srv.MaxPendingPeers > 0 {
@@ -850,7 +863,7 @@ func (srv *Server) listenLoop() {
 // SetupConn runs the handshakes and attempts to add the connection
 // SetupConn runs the handshakes and attempts to add the connection
 // as a peer. It returns when the connection has been added as a peer
 // as a peer. It returns when the connection has been added as a peer
 // or the handshakes have failed.
 // or the handshakes have failed.
-func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) error {
+func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *enode.Node) error {
 	self := srv.Self()
 	self := srv.Self()
 	if self == nil {
 	if self == nil {
 		return errors.New("shutdown")
 		return errors.New("shutdown")
@@ -859,12 +872,12 @@ func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Nod
 	err := srv.setupConn(c, flags, dialDest)
 	err := srv.setupConn(c, flags, dialDest)
 	if err != nil {
 	if err != nil {
 		c.close(err)
 		c.close(err)
-		srv.log.Trace("Setting up connection failed", "id", c.id, "err", err)
+		srv.log.Trace("Setting up connection failed", "addr", fd.RemoteAddr(), "err", err)
 	}
 	}
 	return err
 	return err
 }
 }
 
 
-func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) error {
+func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *enode.Node) error {
 	// Prevent leftover pending conns from entering the handshake.
 	// Prevent leftover pending conns from entering the handshake.
 	srv.lock.Lock()
 	srv.lock.Lock()
 	running := srv.running
 	running := srv.running
@@ -872,18 +885,30 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) e
 	if !running {
 	if !running {
 		return errServerStopped
 		return errServerStopped
 	}
 	}
+	// If dialing, figure out the remote public key.
+	var dialPubkey *ecdsa.PublicKey
+	if dialDest != nil {
+		dialPubkey = new(ecdsa.PublicKey)
+		if err := dialDest.Load((*enode.Secp256k1)(dialPubkey)); err != nil {
+			return fmt.Errorf("dial destination doesn't have a secp256k1 public key")
+		}
+	}
 	// Run the encryption handshake.
 	// Run the encryption handshake.
-	var err error
-	if c.id, err = c.doEncHandshake(srv.PrivateKey, dialDest); err != nil {
+	remotePubkey, err := c.doEncHandshake(srv.PrivateKey, dialPubkey)
+	if err != nil {
 		srv.log.Trace("Failed RLPx handshake", "addr", c.fd.RemoteAddr(), "conn", c.flags, "err", err)
 		srv.log.Trace("Failed RLPx handshake", "addr", c.fd.RemoteAddr(), "conn", c.flags, "err", err)
 		return err
 		return err
 	}
 	}
-	clog := srv.log.New("id", c.id, "addr", c.fd.RemoteAddr(), "conn", c.flags)
-	// For dialed connections, check that the remote public key matches.
-	if dialDest != nil && c.id != dialDest.ID {
-		clog.Trace("Dialed identity mismatch", "want", c, dialDest.ID)
-		return DiscUnexpectedIdentity
+	if dialDest != nil {
+		// For dialed connections, check that the remote public key matches.
+		if dialPubkey.X.Cmp(remotePubkey.X) != 0 || dialPubkey.Y.Cmp(remotePubkey.Y) != 0 {
+			return DiscUnexpectedIdentity
+		}
+		c.node = dialDest
+	} else {
+		c.node = nodeFromConn(remotePubkey, c.fd)
 	}
 	}
+	clog := srv.log.New("id", c.node.ID(), "addr", c.fd.RemoteAddr(), "conn", c.flags)
 	err = srv.checkpoint(c, srv.posthandshake)
 	err = srv.checkpoint(c, srv.posthandshake)
 	if err != nil {
 	if err != nil {
 		clog.Trace("Rejected peer before protocol handshake", "err", err)
 		clog.Trace("Rejected peer before protocol handshake", "err", err)
@@ -895,8 +920,8 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) e
 		clog.Trace("Failed proto handshake", "err", err)
 		clog.Trace("Failed proto handshake", "err", err)
 		return err
 		return err
 	}
 	}
-	if phs.ID != c.id {
-		clog.Trace("Wrong devp2p handshake identity", "err", phs.ID)
+	if id := c.node.ID(); !bytes.Equal(crypto.Keccak256(phs.ID), id[:]) {
+		clog.Trace("Wrong devp2p handshake identity", "phsid", fmt.Sprintf("%x", phs.ID))
 		return DiscUnexpectedIdentity
 		return DiscUnexpectedIdentity
 	}
 	}
 	c.caps, c.name = phs.Caps, phs.Name
 	c.caps, c.name = phs.Caps, phs.Name
@@ -911,6 +936,16 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) e
 	return nil
 	return nil
 }
 }
 
 
+func nodeFromConn(pubkey *ecdsa.PublicKey, conn net.Conn) *enode.Node {
+	var ip net.IP
+	var port int
+	if tcp, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
+		ip = tcp.IP
+		port = tcp.Port
+	}
+	return enode.NewV4(pubkey, ip, port, port)
+}
+
 func truncateName(s string) string {
 func truncateName(s string) string {
 	if len(s) > 20 {
 	if len(s) > 20 {
 		return s[:20] + "..."
 		return s[:20] + "..."
@@ -985,13 +1020,13 @@ func (srv *Server) NodeInfo() *NodeInfo {
 	info := &NodeInfo{
 	info := &NodeInfo{
 		Name:       srv.Name,
 		Name:       srv.Name,
 		Enode:      node.String(),
 		Enode:      node.String(),
-		ID:         node.ID.String(),
-		IP:         node.IP.String(),
+		ID:         node.ID().String(),
+		IP:         node.IP().String(),
 		ListenAddr: srv.ListenAddr,
 		ListenAddr: srv.ListenAddr,
 		Protocols:  make(map[string]interface{}),
 		Protocols:  make(map[string]interface{}),
 	}
 	}
-	info.Ports.Discovery = int(node.UDP)
-	info.Ports.Listener = int(node.TCP)
+	info.Ports.Discovery = node.UDP()
+	info.Ports.Listener = node.TCP()
 
 
 	// Gather all the running protocol infos (only once per protocol type)
 	// Gather all the running protocol infos (only once per protocol type)
 	for _, proto := range srv.Protocols {
 	for _, proto := range srv.Protocols {

+ 66 - 64
p2p/server_test.go

@@ -28,21 +28,22 @@ import (
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto/sha3"
 	"github.com/ethereum/go-ethereum/crypto/sha3"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
+	"github.com/ethereum/go-ethereum/p2p/enr"
 )
 )
 
 
-func init() {
-	// log.Root().SetHandler(log.LvlFilterHandler(log.LvlError, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
-}
+// func init() {
+// 	log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
+// }
 
 
 type testTransport struct {
 type testTransport struct {
-	id discover.NodeID
+	rpub *ecdsa.PublicKey
 	*rlpx
 	*rlpx
 
 
 	closeErr error
 	closeErr error
 }
 }
 
 
-func newTestTransport(id discover.NodeID, fd net.Conn) transport {
+func newTestTransport(rpub *ecdsa.PublicKey, fd net.Conn) transport {
 	wrapped := newRLPX(fd).(*rlpx)
 	wrapped := newRLPX(fd).(*rlpx)
 	wrapped.rw = newRLPXFrameRW(fd, secrets{
 	wrapped.rw = newRLPXFrameRW(fd, secrets{
 		MAC:        zero16,
 		MAC:        zero16,
@@ -50,15 +51,16 @@ func newTestTransport(id discover.NodeID, fd net.Conn) transport {
 		IngressMAC: sha3.NewKeccak256(),
 		IngressMAC: sha3.NewKeccak256(),
 		EgressMAC:  sha3.NewKeccak256(),
 		EgressMAC:  sha3.NewKeccak256(),
 	})
 	})
-	return &testTransport{id: id, rlpx: wrapped}
+	return &testTransport{rpub: rpub, rlpx: wrapped}
 }
 }
 
 
-func (c *testTransport) doEncHandshake(prv *ecdsa.PrivateKey, dialDest *discover.Node) (discover.NodeID, error) {
-	return c.id, nil
+func (c *testTransport) doEncHandshake(prv *ecdsa.PrivateKey, dialDest *ecdsa.PublicKey) (*ecdsa.PublicKey, error) {
+	return c.rpub, nil
 }
 }
 
 
 func (c *testTransport) doProtoHandshake(our *protoHandshake) (*protoHandshake, error) {
 func (c *testTransport) doProtoHandshake(our *protoHandshake) (*protoHandshake, error) {
-	return &protoHandshake{ID: c.id, Name: "test"}, nil
+	pubkey := crypto.FromECDSAPub(c.rpub)[1:]
+	return &protoHandshake{ID: pubkey, Name: "test"}, nil
 }
 }
 
 
 func (c *testTransport) close(err error) {
 func (c *testTransport) close(err error) {
@@ -66,7 +68,7 @@ func (c *testTransport) close(err error) {
 	c.closeErr = err
 	c.closeErr = err
 }
 }
 
 
-func startTestServer(t *testing.T, id discover.NodeID, pf func(*Peer)) *Server {
+func startTestServer(t *testing.T, remoteKey *ecdsa.PublicKey, pf func(*Peer)) *Server {
 	config := Config{
 	config := Config{
 		Name:       "test",
 		Name:       "test",
 		MaxPeers:   10,
 		MaxPeers:   10,
@@ -76,7 +78,7 @@ func startTestServer(t *testing.T, id discover.NodeID, pf func(*Peer)) *Server {
 	server := &Server{
 	server := &Server{
 		Config:       config,
 		Config:       config,
 		newPeerHook:  pf,
 		newPeerHook:  pf,
-		newTransport: func(fd net.Conn) transport { return newTestTransport(id, fd) },
+		newTransport: func(fd net.Conn) transport { return newTestTransport(remoteKey, fd) },
 	}
 	}
 	if err := server.Start(); err != nil {
 	if err := server.Start(); err != nil {
 		t.Fatalf("Could not start server: %v", err)
 		t.Fatalf("Could not start server: %v", err)
@@ -87,14 +89,11 @@ func startTestServer(t *testing.T, id discover.NodeID, pf func(*Peer)) *Server {
 func TestServerListen(t *testing.T) {
 func TestServerListen(t *testing.T) {
 	// start the test server
 	// start the test server
 	connected := make(chan *Peer)
 	connected := make(chan *Peer)
-	remid := randomID()
+	remid := &newkey().PublicKey
 	srv := startTestServer(t, remid, func(p *Peer) {
 	srv := startTestServer(t, remid, func(p *Peer) {
-		if p.ID() != remid {
+		if p.ID() != enode.PubkeyToIDV4(remid) {
 			t.Error("peer func called with wrong node id")
 			t.Error("peer func called with wrong node id")
 		}
 		}
-		if p == nil {
-			t.Error("peer func called with nil conn")
-		}
 		connected <- p
 		connected <- p
 	})
 	})
 	defer close(connected)
 	defer close(connected)
@@ -141,14 +140,14 @@ func TestServerDial(t *testing.T) {
 
 
 	// start the server
 	// start the server
 	connected := make(chan *Peer)
 	connected := make(chan *Peer)
-	remid := randomID()
+	remid := &newkey().PublicKey
 	srv := startTestServer(t, remid, func(p *Peer) { connected <- p })
 	srv := startTestServer(t, remid, func(p *Peer) { connected <- p })
 	defer close(connected)
 	defer close(connected)
 	defer srv.Stop()
 	defer srv.Stop()
 
 
 	// tell the server to connect
 	// tell the server to connect
 	tcpAddr := listener.Addr().(*net.TCPAddr)
 	tcpAddr := listener.Addr().(*net.TCPAddr)
-	node := &discover.Node{ID: remid, IP: tcpAddr.IP, TCP: uint16(tcpAddr.Port)}
+	node := enode.NewV4(remid, tcpAddr.IP, tcpAddr.Port, 0)
 	srv.AddPeer(node)
 	srv.AddPeer(node)
 
 
 	select {
 	select {
@@ -157,7 +156,7 @@ func TestServerDial(t *testing.T) {
 
 
 		select {
 		select {
 		case peer := <-connected:
 		case peer := <-connected:
-			if peer.ID() != remid {
+			if peer.ID() != enode.PubkeyToIDV4(remid) {
 				t.Errorf("peer has wrong id")
 				t.Errorf("peer has wrong id")
 			}
 			}
 			if peer.Name() != "test" {
 			if peer.Name() != "test" {
@@ -211,7 +210,7 @@ func TestServerTaskScheduling(t *testing.T) {
 		quit, returned = make(chan struct{}), make(chan struct{})
 		quit, returned = make(chan struct{}), make(chan struct{})
 		tc             = 0
 		tc             = 0
 		tg             = taskgen{
 		tg             = taskgen{
-			newFunc: func(running int, peers map[discover.NodeID]*Peer) []task {
+			newFunc: func(running int, peers map[enode.ID]*Peer) []task {
 				tc++
 				tc++
 				return []task{&testTask{index: tc - 1}}
 				return []task{&testTask{index: tc - 1}}
 			},
 			},
@@ -284,7 +283,7 @@ func TestServerManyTasks(t *testing.T) {
 	defer srv.Stop()
 	defer srv.Stop()
 	srv.loopWG.Add(1)
 	srv.loopWG.Add(1)
 	go srv.run(taskgen{
 	go srv.run(taskgen{
-		newFunc: func(running int, peers map[discover.NodeID]*Peer) []task {
+		newFunc: func(running int, peers map[enode.ID]*Peer) []task {
 			start, end = end, end+maxActiveDialTasks+10
 			start, end = end, end+maxActiveDialTasks+10
 			if end > len(alltasks) {
 			if end > len(alltasks) {
 				end = len(alltasks)
 				end = len(alltasks)
@@ -319,19 +318,19 @@ func TestServerManyTasks(t *testing.T) {
 }
 }
 
 
 type taskgen struct {
 type taskgen struct {
-	newFunc  func(running int, peers map[discover.NodeID]*Peer) []task
+	newFunc  func(running int, peers map[enode.ID]*Peer) []task
 	doneFunc func(task)
 	doneFunc func(task)
 }
 }
 
 
-func (tg taskgen) newTasks(running int, peers map[discover.NodeID]*Peer, now time.Time) []task {
+func (tg taskgen) newTasks(running int, peers map[enode.ID]*Peer, now time.Time) []task {
 	return tg.newFunc(running, peers)
 	return tg.newFunc(running, peers)
 }
 }
 func (tg taskgen) taskDone(t task, now time.Time) {
 func (tg taskgen) taskDone(t task, now time.Time) {
 	tg.doneFunc(t)
 	tg.doneFunc(t)
 }
 }
-func (tg taskgen) addStatic(*discover.Node) {
+func (tg taskgen) addStatic(*enode.Node) {
 }
 }
-func (tg taskgen) removeStatic(*discover.Node) {
+func (tg taskgen) removeStatic(*enode.Node) {
 }
 }
 
 
 type testTask struct {
 type testTask struct {
@@ -347,13 +346,14 @@ func (t *testTask) Do(srv *Server) {
 // just after the encryption handshake when the server is
 // just after the encryption handshake when the server is
 // at capacity. Trusted connections should still be accepted.
 // at capacity. Trusted connections should still be accepted.
 func TestServerAtCap(t *testing.T) {
 func TestServerAtCap(t *testing.T) {
-	trustedID := randomID()
+	trustedNode := newkey()
+	trustedID := enode.PubkeyToIDV4(&trustedNode.PublicKey)
 	srv := &Server{
 	srv := &Server{
 		Config: Config{
 		Config: Config{
 			PrivateKey:   newkey(),
 			PrivateKey:   newkey(),
 			MaxPeers:     10,
 			MaxPeers:     10,
 			NoDial:       true,
 			NoDial:       true,
-			TrustedNodes: []*discover.Node{{ID: trustedID}},
+			TrustedNodes: []*enode.Node{newNode(trustedID, nil)},
 		},
 		},
 	}
 	}
 	if err := srv.Start(); err != nil {
 	if err := srv.Start(); err != nil {
@@ -361,10 +361,11 @@ func TestServerAtCap(t *testing.T) {
 	}
 	}
 	defer srv.Stop()
 	defer srv.Stop()
 
 
-	newconn := func(id discover.NodeID) *conn {
+	newconn := func(id enode.ID) *conn {
 		fd, _ := net.Pipe()
 		fd, _ := net.Pipe()
-		tx := newTestTransport(id, fd)
-		return &conn{fd: fd, transport: tx, flags: inboundConn, id: id, cont: make(chan error)}
+		tx := newTestTransport(&trustedNode.PublicKey, fd)
+		node := enode.SignNull(new(enr.Record), id)
+		return &conn{fd: fd, transport: tx, flags: inboundConn, node: node, cont: make(chan error)}
 	}
 	}
 
 
 	// Inject a few connections to fill up the peer set.
 	// Inject a few connections to fill up the peer set.
@@ -390,14 +391,14 @@ func TestServerAtCap(t *testing.T) {
 	}
 	}
 
 
 	// Remove from trusted set and try again
 	// Remove from trusted set and try again
-	srv.RemoveTrustedPeer(&discover.Node{ID: trustedID})
+	srv.RemoveTrustedPeer(newNode(trustedID, nil))
 	c = newconn(trustedID)
 	c = newconn(trustedID)
 	if err := srv.checkpoint(c, srv.posthandshake); err != DiscTooManyPeers {
 	if err := srv.checkpoint(c, srv.posthandshake); err != DiscTooManyPeers {
 		t.Error("wrong error for insert:", err)
 		t.Error("wrong error for insert:", err)
 	}
 	}
 
 
 	// Add anotherID to trusted set and try again
 	// Add anotherID to trusted set and try again
-	srv.AddTrustedPeer(&discover.Node{ID: anotherID})
+	srv.AddTrustedPeer(newNode(anotherID, nil))
 	c = newconn(anotherID)
 	c = newconn(anotherID)
 	if err := srv.checkpoint(c, srv.posthandshake); err != nil {
 	if err := srv.checkpoint(c, srv.posthandshake); err != nil {
 		t.Error("unexpected error for trusted conn @posthandshake:", err)
 		t.Error("unexpected error for trusted conn @posthandshake:", err)
@@ -409,20 +410,17 @@ func TestServerAtCap(t *testing.T) {
 
 
 func TestServerPeerLimits(t *testing.T) {
 func TestServerPeerLimits(t *testing.T) {
 	srvkey := newkey()
 	srvkey := newkey()
+	clientkey := newkey()
+	clientnode := enode.NewV4(&clientkey.PublicKey, nil, 0, 0)
 
 
-	clientid := randomID()
-	clientnode := &discover.Node{ID: clientid}
-
-	var tp *setupTransport = &setupTransport{
-		id: clientid,
-		phs: &protoHandshake{
-			ID: clientid,
+	var tp = &setupTransport{
+		pubkey: &clientkey.PublicKey,
+		phs: protoHandshake{
+			ID: crypto.FromECDSAPub(&clientkey.PublicKey)[1:],
 			// Force "DiscUselessPeer" due to unmatching caps
 			// Force "DiscUselessPeer" due to unmatching caps
 			// Caps: []Cap{discard.cap()},
 			// Caps: []Cap{discard.cap()},
 		},
 		},
 	}
 	}
-	var flags connFlag = dynDialedConn
-	var dialDest *discover.Node = &discover.Node{ID: clientid}
 
 
 	srv := &Server{
 	srv := &Server{
 		Config: Config{
 		Config: Config{
@@ -440,6 +438,8 @@ func TestServerPeerLimits(t *testing.T) {
 	defer srv.Stop()
 	defer srv.Stop()
 
 
 	// Check that server is full (MaxPeers=0)
 	// Check that server is full (MaxPeers=0)
+	flags := dynDialedConn
+	dialDest := clientnode
 	conn, _ := net.Pipe()
 	conn, _ := net.Pipe()
 	srv.SetupConn(conn, flags, dialDest)
 	srv.SetupConn(conn, flags, dialDest)
 	if tp.closeErr != DiscTooManyPeers {
 	if tp.closeErr != DiscTooManyPeers {
@@ -473,59 +473,61 @@ func TestServerPeerLimits(t *testing.T) {
 }
 }
 
 
 func TestServerSetupConn(t *testing.T) {
 func TestServerSetupConn(t *testing.T) {
-	id := randomID()
-	srvkey := newkey()
-	srvid := discover.PubkeyID(&srvkey.PublicKey)
+	var (
+		clientkey, srvkey = newkey(), newkey()
+		clientpub         = &clientkey.PublicKey
+		srvpub            = &srvkey.PublicKey
+	)
 	tests := []struct {
 	tests := []struct {
 		dontstart bool
 		dontstart bool
 		tt        *setupTransport
 		tt        *setupTransport
 		flags     connFlag
 		flags     connFlag
-		dialDest  *discover.Node
+		dialDest  *enode.Node
 
 
 		wantCloseErr error
 		wantCloseErr error
 		wantCalls    string
 		wantCalls    string
 	}{
 	}{
 		{
 		{
 			dontstart:    true,
 			dontstart:    true,
-			tt:           &setupTransport{id: id},
+			tt:           &setupTransport{pubkey: clientpub},
 			wantCalls:    "close,",
 			wantCalls:    "close,",
 			wantCloseErr: errServerStopped,
 			wantCloseErr: errServerStopped,
 		},
 		},
 		{
 		{
-			tt:           &setupTransport{id: id, encHandshakeErr: errors.New("read error")},
+			tt:           &setupTransport{pubkey: clientpub, encHandshakeErr: errors.New("read error")},
 			flags:        inboundConn,
 			flags:        inboundConn,
 			wantCalls:    "doEncHandshake,close,",
 			wantCalls:    "doEncHandshake,close,",
 			wantCloseErr: errors.New("read error"),
 			wantCloseErr: errors.New("read error"),
 		},
 		},
 		{
 		{
-			tt:           &setupTransport{id: id},
-			dialDest:     &discover.Node{ID: randomID()},
+			tt:           &setupTransport{pubkey: clientpub},
+			dialDest:     enode.NewV4(&newkey().PublicKey, nil, 0, 0),
 			flags:        dynDialedConn,
 			flags:        dynDialedConn,
 			wantCalls:    "doEncHandshake,close,",
 			wantCalls:    "doEncHandshake,close,",
 			wantCloseErr: DiscUnexpectedIdentity,
 			wantCloseErr: DiscUnexpectedIdentity,
 		},
 		},
 		{
 		{
-			tt:           &setupTransport{id: id, phs: &protoHandshake{ID: randomID()}},
-			dialDest:     &discover.Node{ID: id},
+			tt:           &setupTransport{pubkey: clientpub, phs: protoHandshake{ID: randomID().Bytes()}},
+			dialDest:     enode.NewV4(clientpub, nil, 0, 0),
 			flags:        dynDialedConn,
 			flags:        dynDialedConn,
 			wantCalls:    "doEncHandshake,doProtoHandshake,close,",
 			wantCalls:    "doEncHandshake,doProtoHandshake,close,",
 			wantCloseErr: DiscUnexpectedIdentity,
 			wantCloseErr: DiscUnexpectedIdentity,
 		},
 		},
 		{
 		{
-			tt:           &setupTransport{id: id, protoHandshakeErr: errors.New("foo")},
-			dialDest:     &discover.Node{ID: id},
+			tt:           &setupTransport{pubkey: clientpub, protoHandshakeErr: errors.New("foo")},
+			dialDest:     enode.NewV4(clientpub, nil, 0, 0),
 			flags:        dynDialedConn,
 			flags:        dynDialedConn,
 			wantCalls:    "doEncHandshake,doProtoHandshake,close,",
 			wantCalls:    "doEncHandshake,doProtoHandshake,close,",
 			wantCloseErr: errors.New("foo"),
 			wantCloseErr: errors.New("foo"),
 		},
 		},
 		{
 		{
-			tt:           &setupTransport{id: srvid, phs: &protoHandshake{ID: srvid}},
+			tt:           &setupTransport{pubkey: srvpub, phs: protoHandshake{ID: crypto.FromECDSAPub(srvpub)[1:]}},
 			flags:        inboundConn,
 			flags:        inboundConn,
 			wantCalls:    "doEncHandshake,close,",
 			wantCalls:    "doEncHandshake,close,",
 			wantCloseErr: DiscSelf,
 			wantCloseErr: DiscSelf,
 		},
 		},
 		{
 		{
-			tt:           &setupTransport{id: id, phs: &protoHandshake{ID: id}},
+			tt:           &setupTransport{pubkey: clientpub, phs: protoHandshake{ID: crypto.FromECDSAPub(clientpub)[1:]}},
 			flags:        inboundConn,
 			flags:        inboundConn,
 			wantCalls:    "doEncHandshake,doProtoHandshake,close,",
 			wantCalls:    "doEncHandshake,doProtoHandshake,close,",
 			wantCloseErr: DiscUselessPeer,
 			wantCloseErr: DiscUselessPeer,
@@ -560,26 +562,26 @@ func TestServerSetupConn(t *testing.T) {
 }
 }
 
 
 type setupTransport struct {
 type setupTransport struct {
-	id              discover.NodeID
-	encHandshakeErr error
-
-	phs               *protoHandshake
+	pubkey            *ecdsa.PublicKey
+	encHandshakeErr   error
+	phs               protoHandshake
 	protoHandshakeErr error
 	protoHandshakeErr error
 
 
 	calls    string
 	calls    string
 	closeErr error
 	closeErr error
 }
 }
 
 
-func (c *setupTransport) doEncHandshake(prv *ecdsa.PrivateKey, dialDest *discover.Node) (discover.NodeID, error) {
+func (c *setupTransport) doEncHandshake(prv *ecdsa.PrivateKey, dialDest *ecdsa.PublicKey) (*ecdsa.PublicKey, error) {
 	c.calls += "doEncHandshake,"
 	c.calls += "doEncHandshake,"
-	return c.id, c.encHandshakeErr
+	return c.pubkey, c.encHandshakeErr
 }
 }
+
 func (c *setupTransport) doProtoHandshake(our *protoHandshake) (*protoHandshake, error) {
 func (c *setupTransport) doProtoHandshake(our *protoHandshake) (*protoHandshake, error) {
 	c.calls += "doProtoHandshake,"
 	c.calls += "doProtoHandshake,"
 	if c.protoHandshakeErr != nil {
 	if c.protoHandshakeErr != nil {
 		return nil, c.protoHandshakeErr
 		return nil, c.protoHandshakeErr
 	}
 	}
-	return c.phs, nil
+	return &c.phs, nil
 }
 }
 func (c *setupTransport) close(err error) {
 func (c *setupTransport) close(err error) {
 	c.calls += "close,"
 	c.calls += "close,"
@@ -602,7 +604,7 @@ func newkey() *ecdsa.PrivateKey {
 	return key
 	return key
 }
 }
 
 
-func randomID() (id discover.NodeID) {
+func randomID() (id enode.ID) {
 	for i := range id {
 	for i := range id {
 		id[i] = byte(rand.Intn(255))
 		id[i] = byte(rand.Intn(255))
 	}
 	}

+ 2 - 2
p2p/simulations/adapters/docker.go

@@ -29,7 +29,7 @@ import (
 
 
 	"github.com/docker/docker/pkg/reexec"
 	"github.com/docker/docker/pkg/reexec"
 	"github.com/ethereum/go-ethereum/node"
 	"github.com/ethereum/go-ethereum/node"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 )
 
 
 var (
 var (
@@ -64,7 +64,7 @@ func NewDockerAdapter() (*DockerAdapter, error) {
 
 
 	return &DockerAdapter{
 	return &DockerAdapter{
 		ExecAdapter{
 		ExecAdapter{
-			nodes: make(map[discover.NodeID]*ExecNode),
+			nodes: make(map[enode.ID]*ExecNode),
 		},
 		},
 	}, nil
 	}, nil
 }
 }

+ 5 - 5
p2p/simulations/adapters/exec.go

@@ -38,7 +38,7 @@ import (
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/rpc"
 	"github.com/ethereum/go-ethereum/rpc"
 	"golang.org/x/net/websocket"
 	"golang.org/x/net/websocket"
 )
 )
@@ -54,7 +54,7 @@ type ExecAdapter struct {
 	// simulation node are created.
 	// simulation node are created.
 	BaseDir string
 	BaseDir string
 
 
-	nodes map[discover.NodeID]*ExecNode
+	nodes map[enode.ID]*ExecNode
 }
 }
 
 
 // NewExecAdapter returns an ExecAdapter which stores node data in
 // NewExecAdapter returns an ExecAdapter which stores node data in
@@ -62,7 +62,7 @@ type ExecAdapter struct {
 func NewExecAdapter(baseDir string) *ExecAdapter {
 func NewExecAdapter(baseDir string) *ExecAdapter {
 	return &ExecAdapter{
 	return &ExecAdapter{
 		BaseDir: baseDir,
 		BaseDir: baseDir,
-		nodes:   make(map[discover.NodeID]*ExecNode),
+		nodes:   make(map[enode.ID]*ExecNode),
 	}
 	}
 }
 }
 
 
@@ -122,7 +122,7 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) {
 // ExecNode starts a simulation node by exec'ing the current binary and
 // ExecNode starts a simulation node by exec'ing the current binary and
 // running the configured services
 // running the configured services
 type ExecNode struct {
 type ExecNode struct {
-	ID     discover.NodeID
+	ID     enode.ID
 	Dir    string
 	Dir    string
 	Config *execNodeConfig
 	Config *execNodeConfig
 	Cmd    *exec.Cmd
 	Cmd    *exec.Cmd
@@ -492,7 +492,7 @@ type wsRPCDialer struct {
 
 
 // DialRPC implements the RPCDialer interface by creating a WebSocket RPC
 // DialRPC implements the RPCDialer interface by creating a WebSocket RPC
 // client of the given node
 // client of the given node
-func (w *wsRPCDialer) DialRPC(id discover.NodeID) (*rpc.Client, error) {
+func (w *wsRPCDialer) DialRPC(id enode.ID) (*rpc.Client, error) {
 	addr, ok := w.addrs[id.String()]
 	addr, ok := w.addrs[id.String()]
 	if !ok {
 	if !ok {
 		return nil, fmt.Errorf("unknown node: %s", id)
 		return nil, fmt.Errorf("unknown node: %s", id)

+ 14 - 14
p2p/simulations/adapters/inproc.go

@@ -27,7 +27,7 @@ import (
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"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"
 )
 )
@@ -37,7 +37,7 @@ import (
 type SimAdapter struct {
 type SimAdapter struct {
 	pipe     func() (net.Conn, net.Conn, error)
 	pipe     func() (net.Conn, net.Conn, error)
 	mtx      sync.RWMutex
 	mtx      sync.RWMutex
-	nodes    map[discover.NodeID]*SimNode
+	nodes    map[enode.ID]*SimNode
 	services map[string]ServiceFunc
 	services map[string]ServiceFunc
 }
 }
 
 
@@ -48,7 +48,7 @@ type SimAdapter struct {
 func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter {
 func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter {
 	return &SimAdapter{
 	return &SimAdapter{
 		pipe:     pipes.NetPipe,
 		pipe:     pipes.NetPipe,
-		nodes:    make(map[discover.NodeID]*SimNode),
+		nodes:    make(map[enode.ID]*SimNode),
 		services: services,
 		services: services,
 	}
 	}
 }
 }
@@ -56,7 +56,7 @@ func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter {
 func NewTCPAdapter(services map[string]ServiceFunc) *SimAdapter {
 func NewTCPAdapter(services map[string]ServiceFunc) *SimAdapter {
 	return &SimAdapter{
 	return &SimAdapter{
 		pipe:     pipes.TCPPipe,
 		pipe:     pipes.TCPPipe,
-		nodes:    make(map[discover.NodeID]*SimNode),
+		nodes:    make(map[enode.ID]*SimNode),
 		services: services,
 		services: services,
 	}
 	}
 }
 }
@@ -115,14 +115,14 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
 
 
 // Dial implements the p2p.NodeDialer interface by connecting to the node using
 // Dial implements the p2p.NodeDialer interface by connecting to the node using
 // an in-memory net.Pipe
 // an in-memory net.Pipe
-func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
-	node, ok := s.GetNode(dest.ID)
+func (s *SimAdapter) Dial(dest *enode.Node) (conn net.Conn, err error) {
+	node, ok := s.GetNode(dest.ID())
 	if !ok {
 	if !ok {
-		return nil, fmt.Errorf("unknown node: %s", dest.ID)
+		return nil, fmt.Errorf("unknown node: %s", dest.ID())
 	}
 	}
 	srv := node.Server()
 	srv := node.Server()
 	if srv == nil {
 	if srv == nil {
-		return nil, fmt.Errorf("node not running: %s", dest.ID)
+		return nil, fmt.Errorf("node not running: %s", dest.ID())
 	}
 	}
 	// SimAdapter.pipe is net.Pipe (NewSimAdapter)
 	// SimAdapter.pipe is net.Pipe (NewSimAdapter)
 	pipe1, pipe2, err := s.pipe()
 	pipe1, pipe2, err := s.pipe()
@@ -138,7 +138,7 @@ func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
 
 
 // DialRPC implements the RPCDialer interface by creating an in-memory RPC
 // DialRPC implements the RPCDialer interface by creating an in-memory RPC
 // client of the given node
 // client of the given node
-func (s *SimAdapter) DialRPC(id discover.NodeID) (*rpc.Client, error) {
+func (s *SimAdapter) DialRPC(id enode.ID) (*rpc.Client, error) {
 	node, ok := s.GetNode(id)
 	node, ok := s.GetNode(id)
 	if !ok {
 	if !ok {
 		return nil, fmt.Errorf("unknown node: %s", id)
 		return nil, fmt.Errorf("unknown node: %s", id)
@@ -151,7 +151,7 @@ func (s *SimAdapter) DialRPC(id discover.NodeID) (*rpc.Client, error) {
 }
 }
 
 
 // GetNode returns the node with the given ID if it exists
 // GetNode returns the node with the given ID if it exists
-func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) {
+func (s *SimAdapter) GetNode(id enode.ID) (*SimNode, bool) {
 	s.mtx.RLock()
 	s.mtx.RLock()
 	defer s.mtx.RUnlock()
 	defer s.mtx.RUnlock()
 	node, ok := s.nodes[id]
 	node, ok := s.nodes[id]
@@ -163,7 +163,7 @@ func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) {
 // pipe
 // pipe
 type SimNode struct {
 type SimNode struct {
 	lock         sync.RWMutex
 	lock         sync.RWMutex
-	ID           discover.NodeID
+	ID           enode.ID
 	config       *NodeConfig
 	config       *NodeConfig
 	adapter      *SimAdapter
 	adapter      *SimAdapter
 	node         *node.Node
 	node         *node.Node
@@ -177,9 +177,9 @@ func (sn *SimNode) Addr() []byte {
 	return []byte(sn.Node().String())
 	return []byte(sn.Node().String())
 }
 }
 
 
-// Node returns a discover.Node representing the SimNode
-func (sn *SimNode) Node() *discover.Node {
-	return discover.NewNode(sn.ID, net.IP{127, 0, 0, 1}, 30303, 30303)
+// Node returns a node descriptor representing the SimNode
+func (sn *SimNode) Node() *enode.Node {
+	return sn.config.Node()
 }
 }
 
 
 // Client returns an rpc.Client which can be used to communicate with the
 // Client returns an rpc.Client which can be used to communicate with the

+ 11 - 8
p2p/simulations/adapters/types.go

@@ -29,7 +29,7 @@ import (
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto"
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/rpc"
 	"github.com/ethereum/go-ethereum/rpc"
 )
 )
 
 
@@ -78,7 +78,7 @@ type NodeAdapter interface {
 type NodeConfig struct {
 type NodeConfig struct {
 	// ID is the node's ID which is used to identify the node in the
 	// ID is the node's ID which is used to identify the node in the
 	// simulation network
 	// simulation network
-	ID discover.NodeID
+	ID enode.ID
 
 
 	// PrivateKey is the node's private key which is used by the devp2p
 	// PrivateKey is the node's private key which is used by the devp2p
 	// stack to encrypt communications
 	// stack to encrypt communications
@@ -97,7 +97,7 @@ type NodeConfig struct {
 	Services []string
 	Services []string
 
 
 	// function to sanction or prevent suggesting a peer
 	// function to sanction or prevent suggesting a peer
-	Reachable func(id discover.NodeID) bool
+	Reachable func(id enode.ID) bool
 
 
 	Port uint16
 	Port uint16
 }
 }
@@ -138,11 +138,9 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error {
 	}
 	}
 
 
 	if confJSON.ID != "" {
 	if confJSON.ID != "" {
-		nodeID, err := discover.HexID(confJSON.ID)
-		if err != nil {
+		if err := n.ID.UnmarshalText([]byte(confJSON.ID)); err != nil {
 			return err
 			return err
 		}
 		}
-		n.ID = nodeID
 	}
 	}
 
 
 	if confJSON.PrivateKey != "" {
 	if confJSON.PrivateKey != "" {
@@ -165,6 +163,11 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error {
 	return nil
 	return nil
 }
 }
 
 
+// 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))
+}
+
 // 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 {
@@ -173,7 +176,7 @@ func RandomNodeConfig() *NodeConfig {
 		panic("unable to generate key")
 		panic("unable to generate key")
 	}
 	}
 
 
-	id := discover.PubkeyID(&key.PublicKey)
+	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")
@@ -218,7 +221,7 @@ type ServiceContext struct {
 // other nodes in the network (for example a simulated Swarm node which needs
 // other nodes in the network (for example a simulated Swarm node which needs
 // to connect to a Geth node to resolve ENS names)
 // to connect to a Geth node to resolve ENS names)
 type RPCDialer interface {
 type RPCDialer interface {
-	DialRPC(id discover.NodeID) (*rpc.Client, error)
+	DialRPC(id enode.ID) (*rpc.Client, error)
 }
 }
 
 
 // Services is a collection of services which can be run in a simulation
 // Services is a collection of services which can be run in a simulation

+ 3 - 3
p2p/simulations/examples/ping-pong.go

@@ -28,7 +28,7 @@ import (
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"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/discover"
+	"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/rpc"
 	"github.com/ethereum/go-ethereum/rpc"
@@ -96,12 +96,12 @@ func main() {
 // sends a ping to all its connected peers every 10s and receives a pong in
 // sends a ping to all its connected peers every 10s and receives a pong in
 // return
 // return
 type pingPongService struct {
 type pingPongService struct {
-	id       discover.NodeID
+	id       enode.ID
 	log      log.Logger
 	log      log.Logger
 	received int64
 	received int64
 }
 }
 
 
-func newPingPongService(id discover.NodeID) *pingPongService {
+func newPingPongService(id enode.ID) *pingPongService {
 	return &pingPongService{
 	return &pingPongService{
 		id:  id,
 		id:  id,
 		log: log.New("node.id", id),
 		log: log.New("node.id", id),

+ 5 - 3
p2p/simulations/http.go

@@ -31,7 +31,7 @@ import (
 
 
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/rpc"
 	"github.com/ethereum/go-ethereum/rpc"
 	"github.com/julienschmidt/httprouter"
 	"github.com/julienschmidt/httprouter"
@@ -709,8 +709,9 @@ func (s *Server) wrapHandler(handler http.HandlerFunc) httprouter.Handle {
 		ctx := context.Background()
 		ctx := context.Background()
 
 
 		if id := params.ByName("nodeid"); id != "" {
 		if id := params.ByName("nodeid"); id != "" {
+			var nodeID enode.ID
 			var node *Node
 			var node *Node
-			if nodeID, err := discover.HexID(id); err == nil {
+			if nodeID.UnmarshalText([]byte(id)) == nil {
 				node = s.network.GetNode(nodeID)
 				node = s.network.GetNode(nodeID)
 			} else {
 			} else {
 				node = s.network.GetNodeByName(id)
 				node = s.network.GetNodeByName(id)
@@ -723,8 +724,9 @@ func (s *Server) wrapHandler(handler http.HandlerFunc) httprouter.Handle {
 		}
 		}
 
 
 		if id := params.ByName("peerid"); id != "" {
 		if id := params.ByName("peerid"); id != "" {
+			var peerID enode.ID
 			var peer *Node
 			var peer *Node
-			if peerID, err := discover.HexID(id); err == nil {
+			if peerID.UnmarshalText([]byte(id)) == nil {
 				peer = s.network.GetNode(peerID)
 				peer = s.network.GetNode(peerID)
 			} else {
 			} else {
 				peer = s.network.GetNodeByName(id)
 				peer = s.network.GetNodeByName(id)

+ 8 - 8
p2p/simulations/http_test.go

@@ -30,7 +30,7 @@ import (
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/event"
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/rpc"
 	"github.com/ethereum/go-ethereum/rpc"
 )
 )
@@ -38,12 +38,12 @@ import (
 // testService implements the node.Service interface and provides protocols
 // testService implements the node.Service interface and provides protocols
 // and APIs which are useful for testing nodes in a simulation network
 // and APIs which are useful for testing nodes in a simulation network
 type testService struct {
 type testService struct {
-	id discover.NodeID
+	id enode.ID
 
 
 	// peerCount is incremented once a peer handshake has been performed
 	// peerCount is incremented once a peer handshake has been performed
 	peerCount int64
 	peerCount int64
 
 
-	peers    map[discover.NodeID]*testPeer
+	peers    map[enode.ID]*testPeer
 	peersMtx sync.Mutex
 	peersMtx sync.Mutex
 
 
 	// state stores []byte which is used to test creating and loading
 	// state stores []byte which is used to test creating and loading
@@ -54,7 +54,7 @@ type testService struct {
 func newTestService(ctx *adapters.ServiceContext) (node.Service, error) {
 func newTestService(ctx *adapters.ServiceContext) (node.Service, error) {
 	svc := &testService{
 	svc := &testService{
 		id:    ctx.Config.ID,
 		id:    ctx.Config.ID,
-		peers: make(map[discover.NodeID]*testPeer),
+		peers: make(map[enode.ID]*testPeer),
 	}
 	}
 	svc.state.Store(ctx.Snapshot)
 	svc.state.Store(ctx.Snapshot)
 	return svc, nil
 	return svc, nil
@@ -65,7 +65,7 @@ type testPeer struct {
 	dumReady  chan struct{}
 	dumReady  chan struct{}
 }
 }
 
 
-func (t *testService) peer(id discover.NodeID) *testPeer {
+func (t *testService) peer(id enode.ID) *testPeer {
 	t.peersMtx.Lock()
 	t.peersMtx.Lock()
 	defer t.peersMtx.Unlock()
 	defer t.peersMtx.Unlock()
 	if peer, ok := t.peers[id]; ok {
 	if peer, ok := t.peers[id]; ok {
@@ -410,7 +410,7 @@ func (t *expectEvents) nodeEvent(id string, up bool) *Event {
 		Type: EventTypeNode,
 		Type: EventTypeNode,
 		Node: &Node{
 		Node: &Node{
 			Config: &adapters.NodeConfig{
 			Config: &adapters.NodeConfig{
-				ID: discover.MustHexID(id),
+				ID: enode.HexID(id),
 			},
 			},
 			Up: up,
 			Up: up,
 		},
 		},
@@ -421,8 +421,8 @@ func (t *expectEvents) connEvent(one, other string, up bool) *Event {
 	return &Event{
 	return &Event{
 		Type: EventTypeConn,
 		Type: EventTypeConn,
 		Conn: &Conn{
 		Conn: &Conn{
-			One:   discover.MustHexID(one),
-			Other: discover.MustHexID(other),
+			One:   enode.HexID(one),
+			Other: enode.HexID(other),
 			Up:    up,
 			Up:    up,
 		},
 		},
 	}
 	}

+ 4 - 4
p2p/simulations/mocker.go

@@ -25,7 +25,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 )
 )
 
 
@@ -154,7 +154,7 @@ func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
 				wg.Done()
 				wg.Done()
 				continue
 				continue
 			}
 			}
-			go func(id discover.NodeID) {
+			go func(id enode.ID) {
 				time.Sleep(randWait)
 				time.Sleep(randWait)
 				err := net.Start(id)
 				err := net.Start(id)
 				if err != nil {
 				if err != nil {
@@ -169,8 +169,8 @@ func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
 }
 }
 
 
 //connect nodeCount number of nodes in a ring
 //connect nodeCount number of nodes in a ring
-func connectNodesInRing(net *Network, nodeCount int) ([]discover.NodeID, error) {
-	ids := make([]discover.NodeID, nodeCount)
+func connectNodesInRing(net *Network, nodeCount int) ([]enode.ID, error) {
+	ids := make([]enode.ID, nodeCount)
 	for i := 0; i < nodeCount; i++ {
 	for i := 0; i < nodeCount; i++ {
 		conf := adapters.RandomNodeConfig()
 		conf := adapters.RandomNodeConfig()
 		node, err := net.NewNodeWithConfig(conf)
 		node, err := net.NewNodeWithConfig(conf)

+ 2 - 2
p2p/simulations/mocker_test.go

@@ -27,7 +27,7 @@ import (
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 )
 
 
 func TestMocker(t *testing.T) {
 func TestMocker(t *testing.T) {
@@ -82,7 +82,7 @@ func TestMocker(t *testing.T) {
 	defer sub.Unsubscribe()
 	defer sub.Unsubscribe()
 	//wait until all nodes are started and connected
 	//wait until all nodes are started and connected
 	//store every node up event in a map (value is irrelevant, mimic Set datatype)
 	//store every node up event in a map (value is irrelevant, mimic Set datatype)
-	nodemap := make(map[discover.NodeID]bool)
+	nodemap := make(map[enode.ID]bool)
 	wg.Add(1)
 	wg.Add(1)
 	nodesComplete := false
 	nodesComplete := false
 	connCount := 0
 	connCount := 0

+ 32 - 32
p2p/simulations/network.go

@@ -27,7 +27,7 @@ import (
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/event"
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 )
 )
 
 
@@ -51,7 +51,7 @@ type Network struct {
 	NetworkConfig
 	NetworkConfig
 
 
 	Nodes   []*Node `json:"nodes"`
 	Nodes   []*Node `json:"nodes"`
-	nodeMap map[discover.NodeID]int
+	nodeMap map[enode.ID]int
 
 
 	Conns   []*Conn `json:"conns"`
 	Conns   []*Conn `json:"conns"`
 	connMap map[string]int
 	connMap map[string]int
@@ -67,7 +67,7 @@ func NewNetwork(nodeAdapter adapters.NodeAdapter, conf *NetworkConfig) *Network
 	return &Network{
 	return &Network{
 		NetworkConfig: *conf,
 		NetworkConfig: *conf,
 		nodeAdapter:   nodeAdapter,
 		nodeAdapter:   nodeAdapter,
-		nodeMap:       make(map[discover.NodeID]int),
+		nodeMap:       make(map[enode.ID]int),
 		connMap:       make(map[string]int),
 		connMap:       make(map[string]int),
 		quitc:         make(chan struct{}),
 		quitc:         make(chan struct{}),
 	}
 	}
@@ -85,7 +85,7 @@ func (net *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error)
 	defer net.lock.Unlock()
 	defer net.lock.Unlock()
 
 
 	if conf.Reachable == nil {
 	if conf.Reachable == nil {
-		conf.Reachable = func(otherID discover.NodeID) bool {
+		conf.Reachable = func(otherID enode.ID) bool {
 			_, err := net.InitConn(conf.ID, otherID)
 			_, err := net.InitConn(conf.ID, otherID)
 			if err != nil && bytes.Compare(conf.ID.Bytes(), otherID.Bytes()) < 0 {
 			if err != nil && bytes.Compare(conf.ID.Bytes(), otherID.Bytes()) < 0 {
 				return false
 				return false
@@ -158,13 +158,13 @@ func (net *Network) StopAll() error {
 }
 }
 
 
 // Start starts the node with the given ID
 // Start starts the node with the given ID
-func (net *Network) Start(id discover.NodeID) error {
+func (net *Network) Start(id enode.ID) error {
 	return net.startWithSnapshots(id, nil)
 	return net.startWithSnapshots(id, nil)
 }
 }
 
 
 // startWithSnapshots starts the node with the given ID using the give
 // startWithSnapshots starts the node with the given ID using the give
 // snapshots
 // snapshots
-func (net *Network) startWithSnapshots(id discover.NodeID, snapshots map[string][]byte) error {
+func (net *Network) startWithSnapshots(id enode.ID, snapshots map[string][]byte) error {
 	net.lock.Lock()
 	net.lock.Lock()
 	defer net.lock.Unlock()
 	defer net.lock.Unlock()
 	node := net.getNode(id)
 	node := net.getNode(id)
@@ -200,7 +200,7 @@ func (net *Network) startWithSnapshots(id discover.NodeID, snapshots map[string]
 
 
 // watchPeerEvents reads peer events from the given channel and emits
 // watchPeerEvents reads peer events from the given channel and emits
 // corresponding network events
 // corresponding network events
-func (net *Network) watchPeerEvents(id discover.NodeID, events chan *p2p.PeerEvent, sub event.Subscription) {
+func (net *Network) watchPeerEvents(id enode.ID, events chan *p2p.PeerEvent, sub event.Subscription) {
 	defer func() {
 	defer func() {
 		sub.Unsubscribe()
 		sub.Unsubscribe()
 
 
@@ -248,7 +248,7 @@ func (net *Network) watchPeerEvents(id discover.NodeID, events chan *p2p.PeerEve
 }
 }
 
 
 // Stop stops the node with the given ID
 // Stop stops the node with the given ID
-func (net *Network) Stop(id discover.NodeID) error {
+func (net *Network) Stop(id enode.ID) error {
 	net.lock.Lock()
 	net.lock.Lock()
 	defer net.lock.Unlock()
 	defer net.lock.Unlock()
 	node := net.getNode(id)
 	node := net.getNode(id)
@@ -270,7 +270,7 @@ func (net *Network) Stop(id discover.NodeID) error {
 
 
 // Connect connects two nodes together by calling the "admin_addPeer" RPC
 // Connect connects two nodes together by calling the "admin_addPeer" RPC
 // method on the "one" node so that it connects to the "other" node
 // method on the "one" node so that it connects to the "other" node
-func (net *Network) Connect(oneID, otherID discover.NodeID) error {
+func (net *Network) Connect(oneID, otherID enode.ID) error {
 	log.Debug(fmt.Sprintf("connecting %s to %s", oneID, otherID))
 	log.Debug(fmt.Sprintf("connecting %s to %s", oneID, otherID))
 	conn, err := net.InitConn(oneID, otherID)
 	conn, err := net.InitConn(oneID, otherID)
 	if err != nil {
 	if err != nil {
@@ -286,7 +286,7 @@ func (net *Network) Connect(oneID, otherID discover.NodeID) error {
 
 
 // Disconnect disconnects two nodes by calling the "admin_removePeer" RPC
 // Disconnect disconnects two nodes by calling the "admin_removePeer" RPC
 // method on the "one" node so that it disconnects from the "other" node
 // method on the "one" node so that it disconnects from the "other" node
-func (net *Network) Disconnect(oneID, otherID discover.NodeID) error {
+func (net *Network) Disconnect(oneID, otherID enode.ID) error {
 	conn := net.GetConn(oneID, otherID)
 	conn := net.GetConn(oneID, otherID)
 	if conn == nil {
 	if conn == nil {
 		return fmt.Errorf("connection between %v and %v does not exist", oneID, otherID)
 		return fmt.Errorf("connection between %v and %v does not exist", oneID, otherID)
@@ -303,7 +303,7 @@ func (net *Network) Disconnect(oneID, otherID discover.NodeID) error {
 }
 }
 
 
 // DidConnect tracks the fact that the "one" node connected to the "other" node
 // DidConnect tracks the fact that the "one" node connected to the "other" node
-func (net *Network) DidConnect(one, other discover.NodeID) error {
+func (net *Network) DidConnect(one, other enode.ID) error {
 	net.lock.Lock()
 	net.lock.Lock()
 	defer net.lock.Unlock()
 	defer net.lock.Unlock()
 	conn, err := net.getOrCreateConn(one, other)
 	conn, err := net.getOrCreateConn(one, other)
@@ -320,7 +320,7 @@ func (net *Network) DidConnect(one, other discover.NodeID) error {
 
 
 // DidDisconnect tracks the fact that the "one" node disconnected from the
 // DidDisconnect tracks the fact that the "one" node disconnected from the
 // "other" node
 // "other" node
-func (net *Network) DidDisconnect(one, other discover.NodeID) error {
+func (net *Network) DidDisconnect(one, other enode.ID) error {
 	net.lock.Lock()
 	net.lock.Lock()
 	defer net.lock.Unlock()
 	defer net.lock.Unlock()
 	conn := net.getConn(one, other)
 	conn := net.getConn(one, other)
@@ -337,7 +337,7 @@ func (net *Network) DidDisconnect(one, other discover.NodeID) error {
 }
 }
 
 
 // DidSend tracks the fact that "sender" sent a message to "receiver"
 // DidSend tracks the fact that "sender" sent a message to "receiver"
-func (net *Network) DidSend(sender, receiver discover.NodeID, proto string, code uint64) error {
+func (net *Network) DidSend(sender, receiver enode.ID, proto string, code uint64) error {
 	msg := &Msg{
 	msg := &Msg{
 		One:      sender,
 		One:      sender,
 		Other:    receiver,
 		Other:    receiver,
@@ -350,7 +350,7 @@ func (net *Network) DidSend(sender, receiver discover.NodeID, proto string, code
 }
 }
 
 
 // DidReceive tracks the fact that "receiver" received a message from "sender"
 // DidReceive tracks the fact that "receiver" received a message from "sender"
-func (net *Network) DidReceive(sender, receiver discover.NodeID, proto string, code uint64) error {
+func (net *Network) DidReceive(sender, receiver enode.ID, proto string, code uint64) error {
 	msg := &Msg{
 	msg := &Msg{
 		One:      sender,
 		One:      sender,
 		Other:    receiver,
 		Other:    receiver,
@@ -364,7 +364,7 @@ func (net *Network) DidReceive(sender, receiver discover.NodeID, proto string, c
 
 
 // GetNode gets the node with the given ID, returning nil if the node does not
 // GetNode gets the node with the given ID, returning nil if the node does not
 // exist
 // exist
-func (net *Network) GetNode(id discover.NodeID) *Node {
+func (net *Network) GetNode(id enode.ID) *Node {
 	net.lock.Lock()
 	net.lock.Lock()
 	defer net.lock.Unlock()
 	defer net.lock.Unlock()
 	return net.getNode(id)
 	return net.getNode(id)
@@ -387,7 +387,7 @@ func (net *Network) GetNodes() (nodes []*Node) {
 	return nodes
 	return nodes
 }
 }
 
 
-func (net *Network) getNode(id discover.NodeID) *Node {
+func (net *Network) getNode(id enode.ID) *Node {
 	i, found := net.nodeMap[id]
 	i, found := net.nodeMap[id]
 	if !found {
 	if !found {
 		return nil
 		return nil
@@ -406,7 +406,7 @@ func (net *Network) getNodeByName(name string) *Node {
 
 
 // GetConn returns the connection which exists between "one" and "other"
 // GetConn returns the connection which exists between "one" and "other"
 // regardless of which node initiated the connection
 // regardless of which node initiated the connection
-func (net *Network) GetConn(oneID, otherID discover.NodeID) *Conn {
+func (net *Network) GetConn(oneID, otherID enode.ID) *Conn {
 	net.lock.Lock()
 	net.lock.Lock()
 	defer net.lock.Unlock()
 	defer net.lock.Unlock()
 	return net.getConn(oneID, otherID)
 	return net.getConn(oneID, otherID)
@@ -414,13 +414,13 @@ func (net *Network) GetConn(oneID, otherID discover.NodeID) *Conn {
 
 
 // GetOrCreateConn is like GetConn but creates the connection if it doesn't
 // GetOrCreateConn is like GetConn but creates the connection if it doesn't
 // already exist
 // already exist
-func (net *Network) GetOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error) {
+func (net *Network) GetOrCreateConn(oneID, otherID enode.ID) (*Conn, error) {
 	net.lock.Lock()
 	net.lock.Lock()
 	defer net.lock.Unlock()
 	defer net.lock.Unlock()
 	return net.getOrCreateConn(oneID, otherID)
 	return net.getOrCreateConn(oneID, otherID)
 }
 }
 
 
-func (net *Network) getOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error) {
+func (net *Network) getOrCreateConn(oneID, otherID enode.ID) (*Conn, error) {
 	if conn := net.getConn(oneID, otherID); conn != nil {
 	if conn := net.getConn(oneID, otherID); conn != nil {
 		return conn, nil
 		return conn, nil
 	}
 	}
@@ -445,7 +445,7 @@ func (net *Network) getOrCreateConn(oneID, otherID discover.NodeID) (*Conn, erro
 	return conn, nil
 	return conn, nil
 }
 }
 
 
-func (net *Network) getConn(oneID, otherID discover.NodeID) *Conn {
+func (net *Network) getConn(oneID, otherID enode.ID) *Conn {
 	label := ConnLabel(oneID, otherID)
 	label := ConnLabel(oneID, otherID)
 	i, found := net.connMap[label]
 	i, found := net.connMap[label]
 	if !found {
 	if !found {
@@ -462,7 +462,7 @@ func (net *Network) getConn(oneID, otherID discover.NodeID) *Conn {
 // it also checks whether there has been recent attempt to connect the peers
 // it also checks whether there has been recent attempt to connect the peers
 // this is cheating as the simulation is used as an oracle and know about
 // this is cheating as the simulation is used as an oracle and know about
 // remote peers attempt to connect to a node which will then not initiate the connection
 // remote peers attempt to connect to a node which will then not initiate the connection
-func (net *Network) InitConn(oneID, otherID discover.NodeID) (*Conn, error) {
+func (net *Network) InitConn(oneID, otherID enode.ID) (*Conn, error) {
 	net.lock.Lock()
 	net.lock.Lock()
 	defer net.lock.Unlock()
 	defer net.lock.Unlock()
 	if oneID == otherID {
 	if oneID == otherID {
@@ -508,7 +508,7 @@ func (net *Network) Reset() {
 
 
 	//re-initialize the maps
 	//re-initialize the maps
 	net.connMap = make(map[string]int)
 	net.connMap = make(map[string]int)
-	net.nodeMap = make(map[discover.NodeID]int)
+	net.nodeMap = make(map[enode.ID]int)
 
 
 	net.Nodes = nil
 	net.Nodes = nil
 	net.Conns = nil
 	net.Conns = nil
@@ -527,7 +527,7 @@ type Node struct {
 }
 }
 
 
 // ID returns the ID of the node
 // ID returns the ID of the node
-func (n *Node) ID() discover.NodeID {
+func (n *Node) ID() enode.ID {
 	return n.Config.ID
 	return n.Config.ID
 }
 }
 
 
@@ -564,10 +564,10 @@ func (n *Node) MarshalJSON() ([]byte, error) {
 // Conn represents a connection between two nodes in the network
 // Conn represents a connection between two nodes in the network
 type Conn struct {
 type Conn struct {
 	// One is the node which initiated the connection
 	// One is the node which initiated the connection
-	One discover.NodeID `json:"one"`
+	One enode.ID `json:"one"`
 
 
 	// Other is the node which the connection was made to
 	// Other is the node which the connection was made to
-	Other discover.NodeID `json:"other"`
+	Other enode.ID `json:"other"`
 
 
 	// Up tracks whether or not the connection is active
 	// Up tracks whether or not the connection is active
 	Up bool `json:"up"`
 	Up bool `json:"up"`
@@ -596,11 +596,11 @@ func (c *Conn) String() string {
 
 
 // Msg represents a p2p message sent between two nodes in the network
 // Msg represents a p2p message sent between two nodes in the network
 type Msg struct {
 type Msg struct {
-	One      discover.NodeID `json:"one"`
-	Other    discover.NodeID `json:"other"`
-	Protocol string          `json:"protocol"`
-	Code     uint64          `json:"code"`
-	Received bool            `json:"received"`
+	One      enode.ID `json:"one"`
+	Other    enode.ID `json:"other"`
+	Protocol string   `json:"protocol"`
+	Code     uint64   `json:"code"`
+	Received bool     `json:"received"`
 }
 }
 
 
 // String returns a log-friendly string
 // String returns a log-friendly string
@@ -611,8 +611,8 @@ func (m *Msg) String() string {
 // ConnLabel generates a deterministic string which represents a connection
 // ConnLabel generates a deterministic string which represents a connection
 // between two nodes, used to compare if two connections are between the same
 // between two nodes, used to compare if two connections are between the same
 // nodes
 // nodes
-func ConnLabel(source, target discover.NodeID) string {
-	var first, second discover.NodeID
+func ConnLabel(source, target enode.ID) string {
+	var first, second enode.ID
 	if bytes.Compare(source.Bytes(), target.Bytes()) > 0 {
 	if bytes.Compare(source.Bytes(), target.Bytes()) > 0 {
 		first = target
 		first = target
 		second = source
 		second = source

+ 5 - 5
p2p/simulations/network_test.go

@@ -22,7 +22,7 @@ import (
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 )
 )
 
 
@@ -39,7 +39,7 @@ func TestNetworkSimulation(t *testing.T) {
 	})
 	})
 	defer network.Shutdown()
 	defer network.Shutdown()
 	nodeCount := 20
 	nodeCount := 20
-	ids := make([]discover.NodeID, nodeCount)
+	ids := make([]enode.ID, nodeCount)
 	for i := 0; i < nodeCount; i++ {
 	for i := 0; i < nodeCount; i++ {
 		conf := adapters.RandomNodeConfig()
 		conf := adapters.RandomNodeConfig()
 		node, err := network.NewNodeWithConfig(conf)
 		node, err := network.NewNodeWithConfig(conf)
@@ -64,7 +64,7 @@ func TestNetworkSimulation(t *testing.T) {
 		}
 		}
 		return nil
 		return nil
 	}
 	}
-	check := func(ctx context.Context, id discover.NodeID) (bool, error) {
+	check := func(ctx context.Context, id enode.ID) (bool, error) {
 		// check we haven't run out of time
 		// check we haven't run out of time
 		select {
 		select {
 		case <-ctx.Done():
 		case <-ctx.Done():
@@ -102,7 +102,7 @@ func TestNetworkSimulation(t *testing.T) {
 	defer cancel()
 	defer cancel()
 
 
 	// trigger a check every 100ms
 	// trigger a check every 100ms
-	trigger := make(chan discover.NodeID)
+	trigger := make(chan enode.ID)
 	go triggerChecks(ctx, ids, trigger, 100*time.Millisecond)
 	go triggerChecks(ctx, ids, trigger, 100*time.Millisecond)
 
 
 	result := NewSimulation(network).Run(ctx, &Step{
 	result := NewSimulation(network).Run(ctx, &Step{
@@ -140,7 +140,7 @@ func TestNetworkSimulation(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func triggerChecks(ctx context.Context, ids []discover.NodeID, trigger chan discover.NodeID, interval time.Duration) {
+func triggerChecks(ctx context.Context, ids []enode.ID, trigger chan enode.ID, interval time.Duration) {
 	tick := time.NewTicker(interval)
 	tick := time.NewTicker(interval)
 	defer tick.Stop()
 	defer tick.Stop()
 	for {
 	for {

+ 7 - 7
p2p/simulations/simulation.go

@@ -20,7 +20,7 @@ import (
 	"context"
 	"context"
 	"time"
 	"time"
 
 
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 )
 
 
 // Simulation provides a framework for running actions in a simulated network
 // Simulation provides a framework for running actions in a simulated network
@@ -55,7 +55,7 @@ func (s *Simulation) Run(ctx context.Context, step *Step) (result *StepResult) {
 	}
 	}
 
 
 	// wait for all node expectations to either pass, error or timeout
 	// wait for all node expectations to either pass, error or timeout
-	nodes := make(map[discover.NodeID]struct{}, len(step.Expect.Nodes))
+	nodes := make(map[enode.ID]struct{}, len(step.Expect.Nodes))
 	for _, id := range step.Expect.Nodes {
 	for _, id := range step.Expect.Nodes {
 		nodes[id] = struct{}{}
 		nodes[id] = struct{}{}
 	}
 	}
@@ -119,7 +119,7 @@ type Step struct {
 
 
 	// Trigger is a channel which receives node ids and triggers an
 	// Trigger is a channel which receives node ids and triggers an
 	// expectation check for that node
 	// expectation check for that node
-	Trigger chan discover.NodeID
+	Trigger chan enode.ID
 
 
 	// Expect is the expectation to wait for when performing this step
 	// Expect is the expectation to wait for when performing this step
 	Expect *Expectation
 	Expect *Expectation
@@ -127,15 +127,15 @@ type Step struct {
 
 
 type Expectation struct {
 type Expectation struct {
 	// Nodes is a list of nodes to check
 	// Nodes is a list of nodes to check
-	Nodes []discover.NodeID
+	Nodes []enode.ID
 
 
 	// Check checks whether a given node meets the expectation
 	// Check checks whether a given node meets the expectation
-	Check func(context.Context, discover.NodeID) (bool, error)
+	Check func(context.Context, enode.ID) (bool, error)
 }
 }
 
 
 func newStepResult() *StepResult {
 func newStepResult() *StepResult {
 	return &StepResult{
 	return &StepResult{
-		Passes: make(map[discover.NodeID]time.Time),
+		Passes: make(map[enode.ID]time.Time),
 	}
 	}
 }
 }
 
 
@@ -150,7 +150,7 @@ type StepResult struct {
 	FinishedAt time.Time
 	FinishedAt time.Time
 
 
 	// Passes are the timestamps of the successful node expectations
 	// Passes are the timestamps of the successful node expectations
-	Passes map[discover.NodeID]time.Time
+	Passes map[enode.ID]time.Time
 
 
 	// NetworkEvents are the network events which occurred during the step
 	// NetworkEvents are the network events which occurred during the step
 	NetworkEvents []*Event
 	NetworkEvents []*Event

+ 6 - 6
p2p/testing/peerpool.go

@@ -21,22 +21,22 @@ import (
 	"sync"
 	"sync"
 
 
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 )
 
 
 type TestPeer interface {
 type TestPeer interface {
-	ID() discover.NodeID
+	ID() enode.ID
 	Drop(error)
 	Drop(error)
 }
 }
 
 
 // TestPeerPool is an example peerPool to demonstrate registration of peer connections
 // TestPeerPool is an example peerPool to demonstrate registration of peer connections
 type TestPeerPool struct {
 type TestPeerPool struct {
 	lock  sync.Mutex
 	lock  sync.Mutex
-	peers map[discover.NodeID]TestPeer
+	peers map[enode.ID]TestPeer
 }
 }
 
 
 func NewTestPeerPool() *TestPeerPool {
 func NewTestPeerPool() *TestPeerPool {
-	return &TestPeerPool{peers: make(map[discover.NodeID]TestPeer)}
+	return &TestPeerPool{peers: make(map[enode.ID]TestPeer)}
 }
 }
 
 
 func (p *TestPeerPool) Add(peer TestPeer) {
 func (p *TestPeerPool) Add(peer TestPeer) {
@@ -53,14 +53,14 @@ func (p *TestPeerPool) Remove(peer TestPeer) {
 	delete(p.peers, peer.ID())
 	delete(p.peers, peer.ID())
 }
 }
 
 
-func (p *TestPeerPool) Has(id discover.NodeID) bool {
+func (p *TestPeerPool) Has(id enode.ID) bool {
 	p.lock.Lock()
 	p.lock.Lock()
 	defer p.lock.Unlock()
 	defer p.lock.Unlock()
 	_, ok := p.peers[id]
 	_, ok := p.peers[id]
 	return ok
 	return ok
 }
 }
 
 
-func (p *TestPeerPool) Get(id discover.NodeID) TestPeer {
+func (p *TestPeerPool) Get(id enode.ID) TestPeer {
 	p.lock.Lock()
 	p.lock.Lock()
 	defer p.lock.Unlock()
 	defer p.lock.Unlock()
 	return p.peers[id]
 	return p.peers[id]

+ 17 - 17
p2p/testing/protocolsession.go

@@ -24,7 +24,7 @@ import (
 
 
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 )
 )
 
 
@@ -35,7 +35,7 @@ var errTimedOut = errors.New("timed out")
 // receive (expect) messages
 // receive (expect) messages
 type ProtocolSession struct {
 type ProtocolSession struct {
 	Server  *p2p.Server
 	Server  *p2p.Server
-	IDs     []discover.NodeID
+	Nodes   []*enode.Node
 	adapter *adapters.SimAdapter
 	adapter *adapters.SimAdapter
 	events  chan *p2p.PeerEvent
 	events  chan *p2p.PeerEvent
 }
 }
@@ -56,32 +56,32 @@ type Exchange struct {
 // Trigger is part of the exchange, incoming message for the pivot node
 // Trigger is part of the exchange, incoming message for the pivot node
 // sent by a peer
 // sent by a peer
 type Trigger struct {
 type Trigger struct {
-	Msg     interface{}     // type of message to be sent
-	Code    uint64          // code of message is given
-	Peer    discover.NodeID // the peer to send the message to
-	Timeout time.Duration   // timeout duration for the sending
+	Msg     interface{}   // type of message to be sent
+	Code    uint64        // code of message is given
+	Peer    enode.ID      // the peer to send the message to
+	Timeout time.Duration // timeout duration for the sending
 }
 }
 
 
 // Expect is part of an exchange, outgoing message from the pivot node
 // Expect is part of an exchange, outgoing message from the pivot node
 // received by a peer
 // received by a peer
 type Expect struct {
 type Expect struct {
-	Msg     interface{}     // type of message to expect
-	Code    uint64          // code of message is now given
-	Peer    discover.NodeID // the peer that expects the message
-	Timeout time.Duration   // timeout duration for receiving
+	Msg     interface{}   // type of message to expect
+	Code    uint64        // code of message is now given
+	Peer    enode.ID      // the peer that expects the message
+	Timeout time.Duration // timeout duration for receiving
 }
 }
 
 
 // Disconnect represents a disconnect event, used and checked by TestDisconnected
 // Disconnect represents a disconnect event, used and checked by TestDisconnected
 type Disconnect struct {
 type Disconnect struct {
-	Peer  discover.NodeID // discconnected peer
-	Error error           // disconnect reason
+	Peer  enode.ID // discconnected peer
+	Error error    // disconnect reason
 }
 }
 
 
 // trigger sends messages from peers
 // trigger sends messages from peers
 func (s *ProtocolSession) trigger(trig Trigger) error {
 func (s *ProtocolSession) trigger(trig Trigger) error {
 	simNode, ok := s.adapter.GetNode(trig.Peer)
 	simNode, ok := s.adapter.GetNode(trig.Peer)
 	if !ok {
 	if !ok {
-		return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(s.IDs))
+		return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(s.Nodes))
 	}
 	}
 	mockNode, ok := simNode.Services()[0].(*mockNode)
 	mockNode, ok := simNode.Services()[0].(*mockNode)
 	if !ok {
 	if !ok {
@@ -111,7 +111,7 @@ func (s *ProtocolSession) trigger(trig Trigger) error {
 // expect checks an expectation of a message sent out by the pivot node
 // expect checks an expectation of a message sent out by the pivot node
 func (s *ProtocolSession) expect(exps []Expect) error {
 func (s *ProtocolSession) expect(exps []Expect) error {
 	// construct a map of expectations for each node
 	// construct a map of expectations for each node
-	peerExpects := make(map[discover.NodeID][]Expect)
+	peerExpects := make(map[enode.ID][]Expect)
 	for _, exp := range exps {
 	for _, exp := range exps {
 		if exp.Msg == nil {
 		if exp.Msg == nil {
 			return errors.New("no message to expect")
 			return errors.New("no message to expect")
@@ -120,11 +120,11 @@ func (s *ProtocolSession) expect(exps []Expect) error {
 	}
 	}
 
 
 	// construct a map of mockNodes for each node
 	// construct a map of mockNodes for each node
-	mockNodes := make(map[discover.NodeID]*mockNode)
+	mockNodes := make(map[enode.ID]*mockNode)
 	for nodeID := range peerExpects {
 	for nodeID := range peerExpects {
 		simNode, ok := s.adapter.GetNode(nodeID)
 		simNode, ok := s.adapter.GetNode(nodeID)
 		if !ok {
 		if !ok {
-			return fmt.Errorf("trigger: peer %v does not exist (1- %v)", nodeID, len(s.IDs))
+			return fmt.Errorf("trigger: peer %v does not exist (1- %v)", nodeID, len(s.Nodes))
 		}
 		}
 		mockNode, ok := simNode.Services()[0].(*mockNode)
 		mockNode, ok := simNode.Services()[0].(*mockNode)
 		if !ok {
 		if !ok {
@@ -253,7 +253,7 @@ func (s *ProtocolSession) testExchange(e Exchange) error {
 // TestDisconnected tests the disconnections given as arguments
 // TestDisconnected tests the disconnections given as arguments
 // the disconnect structs describe what disconnect error is expected on which peer
 // the disconnect structs describe what disconnect error is expected on which peer
 func (s *ProtocolSession) TestDisconnected(disconnects ...*Disconnect) error {
 func (s *ProtocolSession) TestDisconnected(disconnects ...*Disconnect) error {
-	expects := make(map[discover.NodeID]error)
+	expects := make(map[enode.ID]error)
 	for _, disconnect := range disconnects {
 	for _, disconnect := range disconnects {
 		expects[disconnect.Peer] = disconnect.Error
 		expects[disconnect.Peer] = disconnect.Error
 	}
 	}

+ 6 - 6
p2p/testing/protocoltester.go

@@ -35,7 +35,7 @@ import (
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"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/discover"
+	"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/rlp"
 	"github.com/ethereum/go-ethereum/rlp"
@@ -52,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(t *testing.T, id discover.NodeID, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
+func NewProtocolTester(t *testing.T, id enode.ID, n 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
@@ -76,17 +76,17 @@ func NewProtocolTester(t *testing.T, id discover.NodeID, n int, run func(*p2p.Pe
 
 
 	node := net.GetNode(id).Node.(*adapters.SimNode)
 	node := net.GetNode(id).Node.(*adapters.SimNode)
 	peers := make([]*adapters.NodeConfig, n)
 	peers := make([]*adapters.NodeConfig, n)
-	peerIDs := make([]discover.NodeID, n)
+	nodes := make([]*enode.Node, n)
 	for i := 0; i < n; i++ {
 	for i := 0; i < n; i++ {
 		peers[i] = adapters.RandomNodeConfig()
 		peers[i] = adapters.RandomNodeConfig()
 		peers[i].Services = []string{"mock"}
 		peers[i].Services = []string{"mock"}
-		peerIDs[i] = peers[i].ID
+		nodes[i] = peers[i].Node()
 	}
 	}
 	events := make(chan *p2p.PeerEvent, 1000)
 	events := make(chan *p2p.PeerEvent, 1000)
 	node.SubscribeEvents(events)
 	node.SubscribeEvents(events)
 	ps := &ProtocolSession{
 	ps := &ProtocolSession{
 		Server:  node.Server(),
 		Server:  node.Server(),
-		IDs:     peerIDs,
+		Nodes:   nodes,
 		adapter: adapter,
 		adapter: adapter,
 		events:  events,
 		events:  events,
 	}
 	}
@@ -108,7 +108,7 @@ func (t *ProtocolTester) Stop() error {
 
 
 // Connect brings up the remote peer node and connects it using the
 // Connect brings up the remote peer node and connects it using the
 // 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 discover.NodeID, 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))
 		log.Trace(fmt.Sprintf("start node %v", peer.ID))
 		if _, err := t.network.NewNodeWithConfig(peer); err != nil {
 		if _, err := t.network.NewNodeWithConfig(peer); err != nil {

+ 2 - 2
swarm/api/config.go

@@ -27,7 +27,7 @@ import (
 	"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"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/pss"
 	"github.com/ethereum/go-ethereum/swarm/pss"
@@ -117,7 +117,7 @@ func (c *Config) Init(prvKey *ecdsa.PrivateKey) {
 
 
 	c.PublicKey = pubkeyhex
 	c.PublicKey = pubkeyhex
 	c.BzzKey = keyhex
 	c.BzzKey = keyhex
-	c.NodeID = discover.PubkeyID(&prvKey.PublicKey).String()
+	c.NodeID = enode.PubkeyToIDV4(&prvKey.PublicKey).String()
 
 
 	if c.SwapEnabled {
 	if c.SwapEnabled {
 		c.Swap.Init(c.Contract, prvKey)
 		c.Swap.Init(c.Contract, prvKey)

+ 2 - 2
swarm/network/discovery.go

@@ -87,7 +87,7 @@ func NotifyPeer(p *BzzAddr, k *Kademlia) {
 // unless already notified during the connection session
 // unless already notified during the connection session
 func (d *Peer) NotifyPeer(a *BzzAddr, po uint8) {
 func (d *Peer) NotifyPeer(a *BzzAddr, po uint8) {
 	// immediately return
 	// immediately return
-	if (po < d.getDepth() && pot.ProxCmp(d.localAddr, d, a) != 1) || d.seen(a) {
+	if (po < d.getDepth() && pot.ProxCmp(d.kad.BaseAddr(), d, a) != 1) || d.seen(a) {
 		return
 		return
 	}
 	}
 	resp := &peersMsg{
 	resp := &peersMsg{
@@ -161,7 +161,7 @@ func (d *Peer) handleSubPeersMsg(msg *subPeersMsg) error {
 		d.setDepth(msg.Depth)
 		d.setDepth(msg.Depth)
 		var peers []*BzzAddr
 		var peers []*BzzAddr
 		d.kad.EachConn(d.Over(), 255, func(p *Peer, po int, isproxbin bool) bool {
 		d.kad.EachConn(d.Over(), 255, func(p *Peer, po int, isproxbin bool) bool {
-			if pob, _ := pof(d, d.localAddr, 0); pob > po {
+			if pob, _ := pof(d, d.kad.BaseAddr(), 0); pob > po {
 				return false
 				return false
 			}
 			}
 			if !d.seen(p.BzzAddr) {
 			if !d.seen(p.BzzAddr) {

+ 3 - 3
swarm/network/discovery_test.go

@@ -31,8 +31,8 @@ func TestDiscovery(t *testing.T) {
 	params := NewHiveParams()
 	params := NewHiveParams()
 	s, pp := newHiveTester(t, params, 1, nil)
 	s, pp := newHiveTester(t, params, 1, nil)
 
 
-	id := s.IDs[0]
-	raddr := NewAddrFromNodeID(id)
+	node := s.Nodes[0]
+	raddr := NewAddr(node)
 	pp.Register(raddr)
 	pp.Register(raddr)
 
 
 	// start the hive and wait for the connection
 	// start the hive and wait for the connection
@@ -46,7 +46,7 @@ func TestDiscovery(t *testing.T) {
 			{
 			{
 				Code: 1,
 				Code: 1,
 				Msg:  &subPeersMsg{Depth: 0},
 				Msg:  &subPeersMsg{Depth: 0},
-				Peer: id,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})

+ 19 - 19
swarm/network/fetcher.go

@@ -22,7 +22,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/swarm/storage"
 	"github.com/ethereum/go-ethereum/swarm/storage"
 )
 )
 
 
@@ -32,7 +32,7 @@ var searchTimeout = 1 * time.Second
 // Also used in stream delivery.
 // Also used in stream delivery.
 var RequestTimeout = 10 * time.Second
 var RequestTimeout = 10 * time.Second
 
 
-type RequestFunc func(context.Context, *Request) (*discover.NodeID, chan struct{}, error)
+type RequestFunc func(context.Context, *Request) (*enode.ID, chan struct{}, error)
 
 
 // Fetcher is created when a chunk is not found locally. It starts a request handler loop once and
 // Fetcher is created when a chunk is not found locally. It starts a request handler loop once and
 // keeps it alive until all active requests are completed. This can happen:
 // keeps it alive until all active requests are completed. This can happen:
@@ -41,18 +41,18 @@ type RequestFunc func(context.Context, *Request) (*discover.NodeID, chan struct{
 // Fetcher self destroys itself after it is completed.
 // Fetcher self destroys itself after it is completed.
 // TODO: cancel all forward requests after termination
 // TODO: cancel all forward requests after termination
 type Fetcher struct {
 type Fetcher struct {
-	protoRequestFunc RequestFunc           // request function fetcher calls to issue retrieve request for a chunk
-	addr             storage.Address       // the address of the chunk to be fetched
-	offerC           chan *discover.NodeID // channel of sources (peer node id strings)
+	protoRequestFunc RequestFunc     // request function fetcher calls to issue retrieve request for a chunk
+	addr             storage.Address // the address of the chunk to be fetched
+	offerC           chan *enode.ID  // channel of sources (peer node id strings)
 	requestC         chan struct{}
 	requestC         chan struct{}
 	skipCheck        bool
 	skipCheck        bool
 }
 }
 
 
 type Request struct {
 type Request struct {
-	Addr        storage.Address  // chunk address
-	Source      *discover.NodeID // nodeID of peer to request from (can be nil)
-	SkipCheck   bool             // whether to offer the chunk first or deliver directly
-	peersToSkip *sync.Map        // peers not to request chunk from (only makes sense if source is nil)
+	Addr        storage.Address // chunk address
+	Source      *enode.ID       // nodeID of peer to request from (can be nil)
+	SkipCheck   bool            // whether to offer the chunk first or deliver directly
+	peersToSkip *sync.Map       // peers not to request chunk from (only makes sense if source is nil)
 }
 }
 
 
 // NewRequest returns a new instance of Request based on chunk address skip check and
 // NewRequest returns a new instance of Request based on chunk address skip check and
@@ -112,14 +112,14 @@ func NewFetcher(addr storage.Address, rf RequestFunc, skipCheck bool) *Fetcher {
 	return &Fetcher{
 	return &Fetcher{
 		addr:             addr,
 		addr:             addr,
 		protoRequestFunc: rf,
 		protoRequestFunc: rf,
-		offerC:           make(chan *discover.NodeID),
+		offerC:           make(chan *enode.ID),
 		requestC:         make(chan struct{}),
 		requestC:         make(chan struct{}),
 		skipCheck:        skipCheck,
 		skipCheck:        skipCheck,
 	}
 	}
 }
 }
 
 
 // Offer is called when an upstream peer offers the chunk via syncing as part of `OfferedHashesMsg` and the node does not have the chunk locally.
 // Offer is called when an upstream peer offers the chunk via syncing as part of `OfferedHashesMsg` and the node does not have the chunk locally.
-func (f *Fetcher) Offer(ctx context.Context, source *discover.NodeID) {
+func (f *Fetcher) Offer(ctx context.Context, source *enode.ID) {
 	// First we need to have this select to make sure that we return if context is done
 	// First we need to have this select to make sure that we return if context is done
 	select {
 	select {
 	case <-ctx.Done():
 	case <-ctx.Done():
@@ -156,13 +156,13 @@ func (f *Fetcher) Request(ctx context.Context) {
 // it keeps the Fetcher alive within the lifecycle of the passed context
 // it keeps the Fetcher alive within the lifecycle of the passed context
 func (f *Fetcher) run(ctx context.Context, peers *sync.Map) {
 func (f *Fetcher) run(ctx context.Context, peers *sync.Map) {
 	var (
 	var (
-		doRequest bool               // determines if retrieval is initiated in the current iteration
-		wait      *time.Timer        // timer for search timeout
-		waitC     <-chan time.Time   // timer channel
-		sources   []*discover.NodeID // known sources, ie. peers that offered the chunk
-		requested bool               // true if the chunk was actually requested
+		doRequest bool             // determines if retrieval is initiated in the current iteration
+		wait      *time.Timer      // timer for search timeout
+		waitC     <-chan time.Time // timer channel
+		sources   []*enode.ID      // known sources, ie. peers that offered the chunk
+		requested bool             // true if the chunk was actually requested
 	)
 	)
-	gone := make(chan *discover.NodeID) // channel to signal that a peer we requested from disconnected
+	gone := make(chan *enode.ID) // channel to signal that a peer we requested from disconnected
 
 
 	// loop that keeps the fetching process alive
 	// loop that keeps the fetching process alive
 	// after every request a timer is set. If this goes off we request again from another peer
 	// after every request a timer is set. If this goes off we request again from another peer
@@ -251,9 +251,9 @@ func (f *Fetcher) run(ctx context.Context, peers *sync.Map) {
 // * the peer's address is added to the set of peers to skip
 // * the peer's address is added to the set of peers to skip
 // * the peer's address is removed from prospective sources, and
 // * the peer's address is removed from prospective sources, and
 // * a go routine is started that reports on the gone channel if the peer is disconnected (or terminated their streamer)
 // * a go routine is started that reports on the gone channel if the peer is disconnected (or terminated their streamer)
-func (f *Fetcher) doRequest(ctx context.Context, gone chan *discover.NodeID, peersToSkip *sync.Map, sources []*discover.NodeID) ([]*discover.NodeID, error) {
+func (f *Fetcher) doRequest(ctx context.Context, gone chan *enode.ID, peersToSkip *sync.Map, sources []*enode.ID) ([]*enode.ID, error) {
 	var i int
 	var i int
-	var sourceID *discover.NodeID
+	var sourceID *enode.ID
 	var quit chan struct{}
 	var quit chan struct{}
 
 
 	req := &Request{
 	req := &Request{

+ 9 - 9
swarm/network/fetcher_test.go

@@ -22,11 +22,11 @@ import (
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 )
 
 
-var requestedPeerID = discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
-var sourcePeerID = discover.MustHexID("2dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
+var requestedPeerID = enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8")
+var sourcePeerID = enode.HexID("99d8594b52298567d2ca3f4c441a5ba0140ee9245e26460d01102a52773c73b9")
 
 
 // mockRequester pushes every request to the requestC channel when its doRequest function is called
 // mockRequester pushes every request to the requestC channel when its doRequest function is called
 type mockRequester struct {
 type mockRequester struct {
@@ -45,7 +45,7 @@ func newMockRequester(waitTimes ...time.Duration) *mockRequester {
 	}
 	}
 }
 }
 
 
-func (m *mockRequester) doRequest(ctx context.Context, request *Request) (*discover.NodeID, chan struct{}, error) {
+func (m *mockRequester) doRequest(ctx context.Context, request *Request) (*enode.ID, chan struct{}, error) {
 	waitTime := time.Duration(0)
 	waitTime := time.Duration(0)
 	if m.ctr < len(m.waitTimes) {
 	if m.ctr < len(m.waitTimes) {
 		waitTime = m.waitTimes[m.ctr]
 		waitTime = m.waitTimes[m.ctr]
@@ -389,9 +389,9 @@ func TestFetcherRequestQuitRetriesRequest(t *testing.T) {
 // and not skip unknown one.
 // and not skip unknown one.
 func TestRequestSkipPeer(t *testing.T) {
 func TestRequestSkipPeer(t *testing.T) {
 	addr := make([]byte, 32)
 	addr := make([]byte, 32)
-	peers := []discover.NodeID{
-		discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
-		discover.MustHexID("2dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
+	peers := []enode.ID{
+		enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8"),
+		enode.HexID("99d8594b52298567d2ca3f4c441a5ba0140ee9245e26460d01102a52773c73b9"),
 	}
 	}
 
 
 	peersToSkip := new(sync.Map)
 	peersToSkip := new(sync.Map)
@@ -411,7 +411,7 @@ func TestRequestSkipPeer(t *testing.T) {
 // after RequestTimeout has passed.
 // after RequestTimeout has passed.
 func TestRequestSkipPeerExpired(t *testing.T) {
 func TestRequestSkipPeerExpired(t *testing.T) {
 	addr := make([]byte, 32)
 	addr := make([]byte, 32)
-	peer := discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
+	peer := enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8")
 
 
 	// set RequestTimeout to a low value and reset it after the test
 	// set RequestTimeout to a low value and reset it after the test
 	defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout)
 	defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout)
@@ -437,7 +437,7 @@ func TestRequestSkipPeerExpired(t *testing.T) {
 // by value to peersToSkip map is not time.Duration.
 // by value to peersToSkip map is not time.Duration.
 func TestRequestSkipPeerPermanent(t *testing.T) {
 func TestRequestSkipPeerPermanent(t *testing.T) {
 	addr := make([]byte, 32)
 	addr := make([]byte, 32)
-	peer := discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
+	peer := enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8")
 
 
 	// set RequestTimeout to a low value and reset it after the test
 	// set RequestTimeout to a low value and reset it after the test
 	defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout)
 	defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout)

+ 32 - 8
swarm/network/hive.go

@@ -23,7 +23,7 @@ import (
 
 
 	"github.com/ethereum/go-ethereum/common/hexutil"
 	"github.com/ethereum/go-ethereum/common/hexutil"
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/state"
 	"github.com/ethereum/go-ethereum/swarm/state"
 )
 )
@@ -56,12 +56,13 @@ func NewHiveParams() *HiveParams {
 
 
 // Hive manages network connections of the swarm node
 // Hive manages network connections of the swarm node
 type Hive struct {
 type Hive struct {
-	*HiveParams                      // settings
-	*Kademlia                        // the overlay connectiviy driver
-	Store       state.Store          // storage interface to save peers across sessions
-	addPeer     func(*discover.Node) // server callback to connect to a peer
+	*HiveParams                   // settings
+	*Kademlia                     // the overlay connectiviy driver
+	Store       state.Store       // storage interface to save peers across sessions
+	addPeer     func(*enode.Node) // server callback to connect to a peer
 	// bookkeeping
 	// bookkeeping
 	lock   sync.Mutex
 	lock   sync.Mutex
+	peers  map[enode.ID]*BzzPeer
 	ticker *time.Ticker
 	ticker *time.Ticker
 }
 }
 
 
@@ -74,6 +75,7 @@ func NewHive(params *HiveParams, kad *Kademlia, store state.Store) *Hive {
 		HiveParams: params,
 		HiveParams: params,
 		Kademlia:   kad,
 		Kademlia:   kad,
 		Store:      store,
 		Store:      store,
+		peers:      make(map[enode.ID]*BzzPeer),
 	}
 	}
 }
 }
 
 
@@ -137,7 +139,7 @@ func (h *Hive) connect() {
 		}
 		}
 
 
 		log.Trace(fmt.Sprintf("%08x hive connect() suggested %08x", h.BaseAddr()[:4], addr.Address()[:4]))
 		log.Trace(fmt.Sprintf("%08x hive connect() suggested %08x", h.BaseAddr()[:4], addr.Address()[:4]))
-		under, err := discover.ParseNode(string(addr.Under()))
+		under, err := enode.ParseV4(string(addr.Under()))
 		if err != nil {
 		if err != nil {
 			log.Warn(fmt.Sprintf("%08x unable to connect to bee %08x: invalid node URL: %v", h.BaseAddr()[:4], addr.Address()[:4], err))
 			log.Warn(fmt.Sprintf("%08x unable to connect to bee %08x: invalid node URL: %v", h.BaseAddr()[:4], addr.Address()[:4], err))
 			continue
 			continue
@@ -149,6 +151,9 @@ func (h *Hive) connect() {
 
 
 // Run protocol run function
 // Run protocol run function
 func (h *Hive) Run(p *BzzPeer) error {
 func (h *Hive) Run(p *BzzPeer) error {
+	h.trackPeer(p)
+	defer h.untrackPeer(p)
+
 	dp := NewPeer(p, h.Kademlia)
 	dp := NewPeer(p, h.Kademlia)
 	depth, changed := h.On(dp)
 	depth, changed := h.On(dp)
 	// if we want discovery, advertise change of depth
 	// if we want discovery, advertise change of depth
@@ -166,6 +171,18 @@ func (h *Hive) Run(p *BzzPeer) error {
 	return dp.Run(dp.HandleMsg)
 	return dp.Run(dp.HandleMsg)
 }
 }
 
 
+func (h *Hive) trackPeer(p *BzzPeer) {
+	h.lock.Lock()
+	h.peers[p.ID()] = p
+	h.lock.Unlock()
+}
+
+func (h *Hive) untrackPeer(p *BzzPeer) {
+	h.lock.Lock()
+	delete(h.peers, p.ID())
+	h.lock.Unlock()
+}
+
 // NodeInfo function is used by the p2p.server RPC interface to display
 // NodeInfo function is used by the p2p.server RPC interface to display
 // protocol specific node information
 // protocol specific node information
 func (h *Hive) NodeInfo() interface{} {
 func (h *Hive) NodeInfo() interface{} {
@@ -174,8 +191,15 @@ func (h *Hive) NodeInfo() interface{} {
 
 
 // PeerInfo function is used by the p2p.server RPC interface to display
 // PeerInfo function is used by the p2p.server RPC interface to display
 // protocol specific information any connected peer referred to by their NodeID
 // protocol specific information any connected peer referred to by their NodeID
-func (h *Hive) PeerInfo(id discover.NodeID) interface{} {
-	addr := NewAddrFromNodeID(id)
+func (h *Hive) PeerInfo(id enode.ID) interface{} {
+	h.lock.Lock()
+	p := h.peers[id]
+	h.lock.Unlock()
+
+	if p == nil {
+		return nil
+	}
+	addr := NewAddr(p.Node())
 	return struct {
 	return struct {
 		OAddr hexutil.Bytes
 		OAddr hexutil.Bytes
 		UAddr hexutil.Bytes
 		UAddr hexutil.Bytes

+ 10 - 7
swarm/network/hive_test.go

@@ -39,8 +39,8 @@ func TestRegisterAndConnect(t *testing.T) {
 	params := NewHiveParams()
 	params := NewHiveParams()
 	s, pp := newHiveTester(t, params, 1, nil)
 	s, pp := newHiveTester(t, params, 1, nil)
 
 
-	id := s.IDs[0]
-	raddr := NewAddrFromNodeID(id)
+	node := s.Nodes[0]
+	raddr := NewAddr(node)
 	pp.Register(raddr)
 	pp.Register(raddr)
 
 
 	// start the hive and wait for the connection
 	// start the hive and wait for the connection
@@ -51,7 +51,7 @@ func TestRegisterAndConnect(t *testing.T) {
 	defer pp.Stop()
 	defer pp.Stop()
 	// retrieve and broadcast
 	// retrieve and broadcast
 	err = s.TestDisconnected(&p2ptest.Disconnect{
 	err = s.TestDisconnected(&p2ptest.Disconnect{
-		Peer:  s.IDs[0],
+		Peer:  s.Nodes[0].ID(),
 		Error: nil,
 		Error: nil,
 	})
 	})
 
 
@@ -75,8 +75,8 @@ func TestHiveStatePersistance(t *testing.T) {
 	s, pp := newHiveTester(t, params, 5, store)
 	s, pp := newHiveTester(t, params, 5, store)
 
 
 	peers := make(map[string]bool)
 	peers := make(map[string]bool)
-	for _, id := range s.IDs {
-		raddr := NewAddrFromNodeID(id)
+	for _, node := range s.Nodes {
+		raddr := NewAddr(node)
 		pp.Register(raddr)
 		pp.Register(raddr)
 		peers[raddr.String()] = true
 		peers[raddr.String()] = true
 	}
 	}
@@ -102,7 +102,10 @@ func TestHiveStatePersistance(t *testing.T) {
 		i++
 		i++
 		return true
 		return true
 	})
 	})
-	if len(peers) != 0 || i != 5 {
-		t.Fatalf("invalid peers loaded")
+	if i != 5 {
+		t.Errorf("invalid number of entries: got %v, want %v", i, 5)
+	}
+	if len(peers) != 0 {
+		t.Fatalf("%d peers left over: %v", len(peers), peers)
 	}
 	}
 }
 }

+ 11 - 12
swarm/network/networkid_test.go

@@ -29,7 +29,7 @@ import (
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"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/discover"
+	"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/rpc"
 	"github.com/ethereum/go-ethereum/rpc"
@@ -38,8 +38,8 @@ import (
 var (
 var (
 	currentNetworkID int
 	currentNetworkID int
 	cnt              int
 	cnt              int
-	nodeMap          map[int][]discover.NodeID
-	kademlias        map[discover.NodeID]*Kademlia
+	nodeMap          map[int][]enode.ID
+	kademlias        map[enode.ID]*Kademlia
 )
 )
 
 
 const (
 const (
@@ -70,7 +70,7 @@ func TestNetworkID(t *testing.T) {
 	//arbitrarily set the number of nodes. It could be any number
 	//arbitrarily set the number of nodes. It could be any number
 	numNodes := 24
 	numNodes := 24
 	//the nodeMap maps all nodes (slice value) with the same network ID (key)
 	//the nodeMap maps all nodes (slice value) with the same network ID (key)
-	nodeMap = make(map[int][]discover.NodeID)
+	nodeMap = make(map[int][]enode.ID)
 	//set up the network and connect nodes
 	//set up the network and connect nodes
 	net, err := setupNetwork(numNodes)
 	net, err := setupNetwork(numNodes)
 	if err != nil {
 	if err != nil {
@@ -95,7 +95,7 @@ func TestNetworkID(t *testing.T) {
 			kademlias[node].EachAddr(nil, 0, func(addr *BzzAddr, _ int, _ bool) bool {
 			kademlias[node].EachAddr(nil, 0, func(addr *BzzAddr, _ int, _ bool) bool {
 				found := false
 				found := false
 				for _, nd := range netIDGroup {
 				for _, nd := range netIDGroup {
-					p := ToOverlayAddr(nd.Bytes())
+					p := nd.Bytes()
 					if bytes.Equal(p, addr.Address()) {
 					if bytes.Equal(p, addr.Address()) {
 						found = true
 						found = true
 					}
 					}
@@ -183,12 +183,11 @@ func setupNetwork(numnodes int) (net *simulations.Network, err error) {
 }
 }
 
 
 func newServices() adapters.Services {
 func newServices() adapters.Services {
-	kademlias = make(map[discover.NodeID]*Kademlia)
-	kademlia := func(id discover.NodeID) *Kademlia {
+	kademlias = make(map[enode.ID]*Kademlia)
+	kademlia := func(id enode.ID) *Kademlia {
 		if k, ok := kademlias[id]; ok {
 		if k, ok := kademlias[id]; ok {
 			return k
 			return k
 		}
 		}
-		addr := NewAddrFromNodeID(id)
 		params := NewKadParams()
 		params := NewKadParams()
 		params.MinProxBinSize = 2
 		params.MinProxBinSize = 2
 		params.MaxBinSize = 3
 		params.MaxBinSize = 3
@@ -196,19 +195,19 @@ func newServices() adapters.Services {
 		params.MaxRetries = 1000
 		params.MaxRetries = 1000
 		params.RetryExponent = 2
 		params.RetryExponent = 2
 		params.RetryInterval = 1000000
 		params.RetryInterval = 1000000
-		kademlias[id] = NewKademlia(addr.Over(), params)
+		kademlias[id] = NewKademlia(id[:], params)
 		return kademlias[id]
 		return kademlias[id]
 	}
 	}
 	return adapters.Services{
 	return adapters.Services{
 		"bzz": func(ctx *adapters.ServiceContext) (node.Service, error) {
 		"bzz": func(ctx *adapters.ServiceContext) (node.Service, error) {
-			addr := NewAddrFromNodeID(ctx.Config.ID)
+			addr := NewAddr(ctx.Config.Node())
 			hp := NewHiveParams()
 			hp := NewHiveParams()
 			hp.Discovery = false
 			hp.Discovery = false
 			cnt++
 			cnt++
 			//assign the network ID
 			//assign the network ID
 			currentNetworkID = cnt % NumberOfNets
 			currentNetworkID = cnt % NumberOfNets
 			if ok := nodeMap[currentNetworkID]; ok == nil {
 			if ok := nodeMap[currentNetworkID]; ok == nil {
-				nodeMap[currentNetworkID] = make([]discover.NodeID, 0)
+				nodeMap[currentNetworkID] = make([]enode.ID, 0)
 			}
 			}
 			//add this node to the group sharing the same network ID
 			//add this node to the group sharing the same network ID
 			nodeMap[currentNetworkID] = append(nodeMap[currentNetworkID], ctx.Config.ID)
 			nodeMap[currentNetworkID] = append(nodeMap[currentNetworkID], ctx.Config.ID)
@@ -224,7 +223,7 @@ func newServices() adapters.Services {
 	}
 	}
 }
 }
 
 
-func watchSubscriptionEvents(ctx context.Context, id discover.NodeID, client *rpc.Client, errc chan error, quitC chan struct{}) {
+func watchSubscriptionEvents(ctx context.Context, id enode.ID, client *rpc.Client, errc chan error, quitC chan struct{}) {
 	events := make(chan *p2p.PeerEvent)
 	events := make(chan *p2p.PeerEvent)
 	sub, err := client.Subscribe(context.Background(), "admin", events, "peerEvents")
 	sub, err := client.Subscribe(context.Background(), "admin", events, "peerEvents")
 	if err != nil {
 	if err != nil {

+ 32 - 53
swarm/network/protocol.go

@@ -26,7 +26,7 @@ import (
 
 
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/protocols"
 	"github.com/ethereum/go-ethereum/p2p/protocols"
 	"github.com/ethereum/go-ethereum/rpc"
 	"github.com/ethereum/go-ethereum/rpc"
 	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/log"
@@ -78,7 +78,7 @@ type Bzz struct {
 	LightNode    bool
 	LightNode    bool
 	localAddr    *BzzAddr
 	localAddr    *BzzAddr
 	mtx          sync.Mutex
 	mtx          sync.Mutex
-	handshakes   map[discover.NodeID]*HandshakeMsg
+	handshakes   map[enode.ID]*HandshakeMsg
 	streamerSpec *protocols.Spec
 	streamerSpec *protocols.Spec
 	streamerRun  func(*BzzPeer) error
 	streamerRun  func(*BzzPeer) error
 }
 }
@@ -94,7 +94,7 @@ func NewBzz(config *BzzConfig, kad *Kademlia, store state.Store, streamerSpec *p
 		NetworkID:    config.NetworkID,
 		NetworkID:    config.NetworkID,
 		LightNode:    config.LightNode,
 		LightNode:    config.LightNode,
 		localAddr:    &BzzAddr{config.OverlayAddr, config.UnderlayAddr},
 		localAddr:    &BzzAddr{config.OverlayAddr, config.UnderlayAddr},
-		handshakes:   make(map[discover.NodeID]*HandshakeMsg),
+		handshakes:   make(map[enode.ID]*HandshakeMsg),
 		streamerRun:  streamerRun,
 		streamerRun:  streamerRun,
 		streamerSpec: streamerSpec,
 		streamerSpec: streamerSpec,
 	}
 	}
@@ -183,7 +183,6 @@ func (b *Bzz) RunProtocol(spec *protocols.Spec, run func(*BzzPeer) error) func(*
 		// the handshake has succeeded so construct the BzzPeer and run the protocol
 		// the handshake has succeeded so construct the BzzPeer and run the protocol
 		peer := &BzzPeer{
 		peer := &BzzPeer{
 			Peer:       protocols.NewPeer(p, rw, spec),
 			Peer:       protocols.NewPeer(p, rw, spec),
-			localAddr:  b.localAddr,
 			BzzAddr:    handshake.peerAddr,
 			BzzAddr:    handshake.peerAddr,
 			lastActive: time.Now(),
 			lastActive: time.Now(),
 			LightNode:  handshake.LightNode,
 			LightNode:  handshake.LightNode,
@@ -218,14 +217,14 @@ func (b *Bzz) performHandshake(p *protocols.Peer, handshake *HandshakeMsg) error
 func (b *Bzz) runBzz(p *p2p.Peer, rw p2p.MsgReadWriter) error {
 func (b *Bzz) runBzz(p *p2p.Peer, rw p2p.MsgReadWriter) error {
 	handshake, _ := b.GetHandshake(p.ID())
 	handshake, _ := b.GetHandshake(p.ID())
 	if !<-handshake.init {
 	if !<-handshake.init {
-		return fmt.Errorf("%08x: bzz already started on peer %08x", b.localAddr.Over()[:4], ToOverlayAddr(p.ID().Bytes())[:4])
+		return fmt.Errorf("%08x: bzz already started on peer %08x", b.localAddr.Over()[:4], p.ID().Bytes()[:4])
 	}
 	}
 	close(handshake.init)
 	close(handshake.init)
 	defer b.removeHandshake(p.ID())
 	defer b.removeHandshake(p.ID())
 	peer := protocols.NewPeer(p, rw, BzzSpec)
 	peer := protocols.NewPeer(p, rw, BzzSpec)
 	err := b.performHandshake(peer, handshake)
 	err := b.performHandshake(peer, handshake)
 	if err != nil {
 	if err != nil {
-		log.Warn(fmt.Sprintf("%08x: handshake failed with remote peer %08x: %v", b.localAddr.Over()[:4], ToOverlayAddr(p.ID().Bytes())[:4], err))
+		log.Warn(fmt.Sprintf("%08x: handshake failed with remote peer %08x: %v", b.localAddr.Over()[:4], p.ID().Bytes()[:4], err))
 
 
 		return err
 		return err
 	}
 	}
@@ -242,18 +241,13 @@ func (b *Bzz) runBzz(p *p2p.Peer, rw p2p.MsgReadWriter) error {
 // implements the Peer interface and all interfaces Peer implements: Addr, OverlayPeer
 // implements the Peer interface and all interfaces Peer implements: Addr, OverlayPeer
 type BzzPeer struct {
 type BzzPeer struct {
 	*protocols.Peer           // represents the connection for online peers
 	*protocols.Peer           // represents the connection for online peers
-	localAddr       *BzzAddr  // local Peers address
 	*BzzAddr                  // remote address -> implements Addr interface = protocols.Peer
 	*BzzAddr                  // remote address -> implements Addr interface = protocols.Peer
 	lastActive      time.Time // time is updated whenever mutexes are releasing
 	lastActive      time.Time // time is updated whenever mutexes are releasing
 	LightNode       bool
 	LightNode       bool
 }
 }
 
 
-func NewBzzPeer(p *protocols.Peer, addr *BzzAddr) *BzzPeer {
-	return &BzzPeer{
-		Peer:      p,
-		localAddr: addr,
-		BzzAddr:   NewAddrFromNodeID(p.ID()),
-	}
+func NewBzzPeer(p *protocols.Peer) *BzzPeer {
+	return &BzzPeer{Peer: p, BzzAddr: NewAddr(p.Node())}
 }
 }
 
 
 // LastActive returns the time the peer was last active
 // LastActive returns the time the peer was last active
@@ -261,6 +255,14 @@ func (p *BzzPeer) LastActive() time.Time {
 	return p.lastActive
 	return p.lastActive
 }
 }
 
 
+// ID returns the peer's underlay node identifier.
+func (p *BzzPeer) ID() enode.ID {
+	// This is here to resolve a method tie: both protocols.Peer and BzzAddr are embedded
+	// into the struct and provide ID(). The protocols.Peer version is faster, ensure it
+	// gets used.
+	return p.Peer.ID()
+}
+
 /*
 /*
  Handshake
  Handshake
 
 
@@ -301,14 +303,14 @@ func (b *Bzz) checkHandshake(hs interface{}) error {
 
 
 // removeHandshake removes handshake for peer with peerID
 // removeHandshake removes handshake for peer with peerID
 // from the bzz handshake store
 // from the bzz handshake store
-func (b *Bzz) removeHandshake(peerID discover.NodeID) {
+func (b *Bzz) removeHandshake(peerID enode.ID) {
 	b.mtx.Lock()
 	b.mtx.Lock()
 	defer b.mtx.Unlock()
 	defer b.mtx.Unlock()
 	delete(b.handshakes, peerID)
 	delete(b.handshakes, peerID)
 }
 }
 
 
 // GetHandshake returns the bzz handhake that the remote peer with peerID sent
 // GetHandshake returns the bzz handhake that the remote peer with peerID sent
-func (b *Bzz) GetHandshake(peerID discover.NodeID) (*HandshakeMsg, bool) {
+func (b *Bzz) GetHandshake(peerID enode.ID) (*HandshakeMsg, bool) {
 	b.mtx.Lock()
 	b.mtx.Lock()
 	defer b.mtx.Unlock()
 	defer b.mtx.Unlock()
 	handshake, found := b.handshakes[peerID]
 	handshake, found := b.handshakes[peerID]
@@ -336,24 +338,28 @@ type BzzAddr struct {
 	UAddr []byte
 	UAddr []byte
 }
 }
 
 
-// Address implements OverlayPeer interface to be used in Overlay
+// Address implements OverlayPeer interface to be used in Overlay.
 func (a *BzzAddr) Address() []byte {
 func (a *BzzAddr) Address() []byte {
 	return a.OAddr
 	return a.OAddr
 }
 }
 
 
-// Over returns the overlay address
+// Over returns the overlay address.
 func (a *BzzAddr) Over() []byte {
 func (a *BzzAddr) Over() []byte {
 	return a.OAddr
 	return a.OAddr
 }
 }
 
 
-// Under returns the underlay address
+// Under returns the underlay address.
 func (a *BzzAddr) Under() []byte {
 func (a *BzzAddr) Under() []byte {
 	return a.UAddr
 	return a.UAddr
 }
 }
 
 
-// ID returns the nodeID from the underlay enode address
-func (a *BzzAddr) ID() discover.NodeID {
-	return discover.MustParseNode(string(a.UAddr)).ID
+// 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
 // Update updates the underlay address of a peer record
@@ -372,38 +378,11 @@ func RandomAddr() *BzzAddr {
 	if err != nil {
 	if err != nil {
 		panic("unable to generate key")
 		panic("unable to generate key")
 	}
 	}
-	pubkey := crypto.FromECDSAPub(&key.PublicKey)
-	var id discover.NodeID
-	copy(id[:], pubkey[1:])
-	return NewAddrFromNodeID(id)
-}
-
-// NewNodeIDFromAddr transforms the underlay address to an adapters.NodeID
-func NewNodeIDFromAddr(addr *BzzAddr) discover.NodeID {
-	log.Info(fmt.Sprintf("uaddr=%s", string(addr.UAddr)))
-	node := discover.MustParseNode(string(addr.UAddr))
-	return node.ID
-}
-
-// NewAddrFromNodeID constucts a BzzAddr from a discover.NodeID
-// the overlay address is derived as the hash of the nodeID
-func NewAddrFromNodeID(id discover.NodeID) *BzzAddr {
-	return &BzzAddr{
-		OAddr: ToOverlayAddr(id.Bytes()),
-		UAddr: []byte(discover.NewNode(id, net.IP{127, 0, 0, 1}, 30303, 30303).String()),
-	}
-}
-
-// NewAddrFromNodeIDAndPort constucts a BzzAddr from a discover.NodeID and port uint16
-// the overlay address is derived as the hash of the nodeID
-func NewAddrFromNodeIDAndPort(id discover.NodeID, host net.IP, port uint16) *BzzAddr {
-	return &BzzAddr{
-		OAddr: ToOverlayAddr(id.Bytes()),
-		UAddr: []byte(discover.NewNode(id, host, port, port).String()),
-	}
+	node := enode.NewV4(&key.PublicKey, net.IP{127, 0, 0, 1}, 30303, 30303)
+	return NewAddr(node)
 }
 }
 
 
-// ToOverlayAddr creates an overlayaddress from a byte slice
-func ToOverlayAddr(id []byte) []byte {
-	return crypto.Keccak256(id)
+// NewAddr constucts a BzzAddr from a node record.
+func NewAddr(node *enode.Node) *BzzAddr {
+	return &BzzAddr{OAddr: node.ID().Bytes(), UAddr: []byte(node.String())}
 }
 }

+ 23 - 27
swarm/network/protocol_test.go

@@ -25,7 +25,7 @@ import (
 
 
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"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"
 )
 )
@@ -71,7 +71,7 @@ func (t *testStore) Save(key string, v []byte) error {
 	return nil
 	return nil
 }
 }
 
 
-func HandshakeMsgExchange(lhs, rhs *HandshakeMsg, id discover.NodeID) []p2ptest.Exchange {
+func HandshakeMsgExchange(lhs, rhs *HandshakeMsg, id enode.ID) []p2ptest.Exchange {
 
 
 	return []p2ptest.Exchange{
 	return []p2ptest.Exchange{
 		{
 		{
@@ -108,17 +108,13 @@ func newBzzBaseTester(t *testing.T, n int, addr *BzzAddr, spec *protocols.Spec,
 	}
 	}
 
 
 	protocol := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
 	protocol := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
-		return srv(&BzzPeer{
-			Peer:      protocols.NewPeer(p, rw, spec),
-			localAddr: addr,
-			BzzAddr:   NewAddrFromNodeID(p.ID()),
-		})
+		return srv(&BzzPeer{Peer: protocols.NewPeer(p, rw, spec), BzzAddr: NewAddr(p.Node())})
 	}
 	}
 
 
-	s := p2ptest.NewProtocolTester(t, NewNodeIDFromAddr(addr), n, protocol)
+	s := p2ptest.NewProtocolTester(t, addr.ID(), n, protocol)
 
 
-	for _, id := range s.IDs {
-		cs[id.String()] = make(chan bool)
+	for _, node := range s.Nodes {
+		cs[node.ID().String()] = make(chan bool)
 	}
 	}
 
 
 	return &bzzTester{
 	return &bzzTester{
@@ -150,7 +146,7 @@ func newBzz(addr *BzzAddr, lightNode bool) *Bzz {
 
 
 func newBzzHandshakeTester(t *testing.T, n int, addr *BzzAddr, lightNode bool) *bzzTester {
 func newBzzHandshakeTester(t *testing.T, n int, addr *BzzAddr, lightNode bool) *bzzTester {
 	bzz := newBzz(addr, lightNode)
 	bzz := newBzz(addr, lightNode)
-	pt := p2ptest.NewProtocolTester(t, NewNodeIDFromAddr(addr), n, bzz.runBzz)
+	pt := p2ptest.NewProtocolTester(t, addr.ID(), n, bzz.runBzz)
 
 
 	return &bzzTester{
 	return &bzzTester{
 		addr:           addr,
 		addr:           addr,
@@ -161,14 +157,14 @@ func newBzzHandshakeTester(t *testing.T, n int, addr *BzzAddr, lightNode bool) *
 
 
 // should test handshakes in one exchange? parallelisation
 // should test handshakes in one exchange? parallelisation
 func (s *bzzTester) testHandshake(lhs, rhs *HandshakeMsg, disconnects ...*p2ptest.Disconnect) error {
 func (s *bzzTester) testHandshake(lhs, rhs *HandshakeMsg, disconnects ...*p2ptest.Disconnect) error {
-	var peers []discover.NodeID
-	id := NewNodeIDFromAddr(rhs.Addr)
+	var peers []enode.ID
+	id := rhs.Addr.ID()
 	if len(disconnects) > 0 {
 	if len(disconnects) > 0 {
 		for _, d := range disconnects {
 		for _, d := range disconnects {
 			peers = append(peers, d.Peer)
 			peers = append(peers, d.Peer)
 		}
 		}
 	} else {
 	} else {
-		peers = []discover.NodeID{id}
+		peers = []enode.ID{id}
 	}
 	}
 
 
 	if err := s.TestExchanges(HandshakeMsgExchange(lhs, rhs, id)...); err != nil {
 	if err := s.TestExchanges(HandshakeMsgExchange(lhs, rhs, id)...); err != nil {
@@ -181,7 +177,7 @@ func (s *bzzTester) testHandshake(lhs, rhs *HandshakeMsg, disconnects ...*p2ptes
 
 
 	// If we don't expect disconnect, ensure peers remain connected
 	// If we don't expect disconnect, ensure peers remain connected
 	err := s.TestDisconnected(&p2ptest.Disconnect{
 	err := s.TestDisconnected(&p2ptest.Disconnect{
-		Peer:  s.IDs[0],
+		Peer:  s.Nodes[0].ID(),
 		Error: nil,
 		Error: nil,
 	})
 	})
 
 
@@ -209,12 +205,12 @@ func TestBzzHandshakeNetworkIDMismatch(t *testing.T) {
 	lightNode := false
 	lightNode := false
 	addr := RandomAddr()
 	addr := RandomAddr()
 	s := newBzzHandshakeTester(t, 1, addr, lightNode)
 	s := newBzzHandshakeTester(t, 1, addr, lightNode)
-	id := s.IDs[0]
+	node := s.Nodes[0]
 
 
 	err := s.testHandshake(
 	err := s.testHandshake(
 		correctBzzHandshake(addr, lightNode),
 		correctBzzHandshake(addr, lightNode),
-		&HandshakeMsg{Version: TestProtocolVersion, NetworkID: 321, Addr: NewAddrFromNodeID(id)},
-		&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): network id mismatch 321 (!= 3)")},
+		&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)")},
 	)
 	)
 
 
 	if err != nil {
 	if err != nil {
@@ -226,12 +222,12 @@ func TestBzzHandshakeVersionMismatch(t *testing.T) {
 	lightNode := false
 	lightNode := false
 	addr := RandomAddr()
 	addr := RandomAddr()
 	s := newBzzHandshakeTester(t, 1, addr, lightNode)
 	s := newBzzHandshakeTester(t, 1, addr, lightNode)
-	id := s.IDs[0]
+	node := s.Nodes[0]
 
 
 	err := s.testHandshake(
 	err := s.testHandshake(
 		correctBzzHandshake(addr, lightNode),
 		correctBzzHandshake(addr, lightNode),
-		&HandshakeMsg{Version: 0, NetworkID: TestProtocolNetworkID, Addr: NewAddrFromNodeID(id)},
-		&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): version mismatch 0 (!= %d)", TestProtocolVersion)},
+		&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)},
 	)
 	)
 
 
 	if err != nil {
 	if err != nil {
@@ -243,11 +239,11 @@ func TestBzzHandshakeSuccess(t *testing.T) {
 	lightNode := false
 	lightNode := false
 	addr := RandomAddr()
 	addr := RandomAddr()
 	s := newBzzHandshakeTester(t, 1, addr, lightNode)
 	s := newBzzHandshakeTester(t, 1, addr, lightNode)
-	id := s.IDs[0]
+	node := s.Nodes[0]
 
 
 	err := s.testHandshake(
 	err := s.testHandshake(
 		correctBzzHandshake(addr, lightNode),
 		correctBzzHandshake(addr, lightNode),
-		&HandshakeMsg{Version: TestProtocolVersion, NetworkID: TestProtocolNetworkID, Addr: NewAddrFromNodeID(id)},
+		&HandshakeMsg{Version: TestProtocolVersion, NetworkID: TestProtocolNetworkID, Addr: NewAddr(node)},
 	)
 	)
 
 
 	if err != nil {
 	if err != nil {
@@ -268,8 +264,8 @@ func TestBzzHandshakeLightNode(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 			randomAddr := RandomAddr()
 			randomAddr := RandomAddr()
 			pt := newBzzHandshakeTester(t, 1, randomAddr, false)
 			pt := newBzzHandshakeTester(t, 1, randomAddr, false)
-			id := pt.IDs[0]
-			addr := NewAddrFromNodeID(id)
+			node := pt.Nodes[0]
+			addr := NewAddr(node)
 
 
 			err := pt.testHandshake(
 			err := pt.testHandshake(
 				correctBzzHandshake(randomAddr, false),
 				correctBzzHandshake(randomAddr, false),
@@ -280,8 +276,8 @@ func TestBzzHandshakeLightNode(t *testing.T) {
 				t.Fatal(err)
 				t.Fatal(err)
 			}
 			}
 
 
-			if pt.bzz.handshakes[id].LightNode != test.lightNode {
-				t.Fatalf("peer LightNode flag is %v, should be %v", pt.bzz.handshakes[id].LightNode, test.lightNode)
+			if pt.bzz.handshakes[node.ID()].LightNode != test.lightNode {
+				t.Fatalf("peer LightNode flag is %v, should be %v", pt.bzz.handshakes[node.ID()].LightNode, test.lightNode)
 			}
 			}
 		})
 		})
 	}
 	}

+ 7 - 9
swarm/network/simulation/bucket.go

@@ -16,15 +16,13 @@
 
 
 package simulation
 package simulation
 
 
-import (
-	"github.com/ethereum/go-ethereum/p2p/discover"
-)
+import "github.com/ethereum/go-ethereum/p2p/enode"
 
 
 // BucketKey is the type that should be used for keys in simulation buckets.
 // BucketKey is the type that should be used for keys in simulation buckets.
 type BucketKey string
 type BucketKey string
 
 
 // NodeItem returns an item set in ServiceFunc function for a particualar node.
 // NodeItem returns an item set in ServiceFunc function for a particualar node.
-func (s *Simulation) NodeItem(id discover.NodeID, key interface{}) (value interface{}, ok bool) {
+func (s *Simulation) NodeItem(id enode.ID, key interface{}) (value interface{}, ok bool) {
 	s.mu.Lock()
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	defer s.mu.Unlock()
 
 
@@ -36,7 +34,7 @@ func (s *Simulation) NodeItem(id discover.NodeID, key interface{}) (value interf
 
 
 // SetNodeItem sets a new item associated with the node with provided NodeID.
 // SetNodeItem sets a new item associated with the node with provided NodeID.
 // Buckets should be used to avoid managing separate simulation global state.
 // Buckets should be used to avoid managing separate simulation global state.
-func (s *Simulation) SetNodeItem(id discover.NodeID, key interface{}, value interface{}) {
+func (s *Simulation) SetNodeItem(id enode.ID, key interface{}, value interface{}) {
 	s.mu.Lock()
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	defer s.mu.Unlock()
 
 
@@ -45,12 +43,12 @@ func (s *Simulation) SetNodeItem(id discover.NodeID, key interface{}, value inte
 
 
 // NodesItems returns a map of items from all nodes that are all set under the
 // NodesItems returns a map of items from all nodes that are all set under the
 // same BucketKey.
 // same BucketKey.
-func (s *Simulation) NodesItems(key interface{}) (values map[discover.NodeID]interface{}) {
+func (s *Simulation) NodesItems(key interface{}) (values map[enode.ID]interface{}) {
 	s.mu.RLock()
 	s.mu.RLock()
 	defer s.mu.RUnlock()
 	defer s.mu.RUnlock()
 
 
 	ids := s.NodeIDs()
 	ids := s.NodeIDs()
-	values = make(map[discover.NodeID]interface{}, len(ids))
+	values = make(map[enode.ID]interface{}, len(ids))
 	for _, id := range ids {
 	for _, id := range ids {
 		if _, ok := s.buckets[id]; !ok {
 		if _, ok := s.buckets[id]; !ok {
 			continue
 			continue
@@ -63,12 +61,12 @@ func (s *Simulation) NodesItems(key interface{}) (values map[discover.NodeID]int
 }
 }
 
 
 // UpNodesItems returns a map of items with the same BucketKey from all nodes that are up.
 // UpNodesItems returns a map of items with the same BucketKey from all nodes that are up.
-func (s *Simulation) UpNodesItems(key interface{}) (values map[discover.NodeID]interface{}) {
+func (s *Simulation) UpNodesItems(key interface{}) (values map[enode.ID]interface{}) {
 	s.mu.RLock()
 	s.mu.RLock()
 	defer s.mu.RUnlock()
 	defer s.mu.RUnlock()
 
 
 	ids := s.UpNodeIDs()
 	ids := s.UpNodeIDs()
-	values = make(map[discover.NodeID]interface{})
+	values = make(map[enode.ID]interface{})
 	for _, id := range ids {
 	for _, id := range ids {
 		if _, ok := s.buckets[id]; !ok {
 		if _, ok := s.buckets[id]; !ok {
 			continue
 			continue

+ 10 - 10
swarm/network/simulation/connect.go

@@ -19,14 +19,14 @@ package simulation
 import (
 import (
 	"strings"
 	"strings"
 
 
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 )
 
 
 // ConnectToPivotNode connects the node with provided NodeID
 // ConnectToPivotNode connects the node with provided NodeID
 // to the pivot node, already set by Simulation.SetPivotNode method.
 // to the pivot node, already set by Simulation.SetPivotNode method.
 // It is useful when constructing a star network topology
 // It is useful when constructing a star network topology
 // when simulation adds and removes nodes dynamically.
 // when simulation adds and removes nodes dynamically.
-func (s *Simulation) ConnectToPivotNode(id discover.NodeID) (err error) {
+func (s *Simulation) ConnectToPivotNode(id enode.ID) (err error) {
 	pid := s.PivotNodeID()
 	pid := s.PivotNodeID()
 	if pid == nil {
 	if pid == nil {
 		return ErrNoPivotNode
 		return ErrNoPivotNode
@@ -38,7 +38,7 @@ func (s *Simulation) ConnectToPivotNode(id discover.NodeID) (err error) {
 // to the last node that is up, and avoiding connection to self.
 // to the last node that is up, and avoiding connection to self.
 // It is useful when constructing a chain network topology
 // It is useful when constructing a chain network topology
 // when simulation adds and removes nodes dynamically.
 // when simulation adds and removes nodes dynamically.
-func (s *Simulation) ConnectToLastNode(id discover.NodeID) (err error) {
+func (s *Simulation) ConnectToLastNode(id enode.ID) (err error) {
 	ids := s.UpNodeIDs()
 	ids := s.UpNodeIDs()
 	l := len(ids)
 	l := len(ids)
 	if l < 2 {
 	if l < 2 {
@@ -53,7 +53,7 @@ func (s *Simulation) ConnectToLastNode(id discover.NodeID) (err error) {
 
 
 // ConnectToRandomNode connects the node with provieded NodeID
 // ConnectToRandomNode connects the node with provieded NodeID
 // to a random node that is up.
 // to a random node that is up.
-func (s *Simulation) ConnectToRandomNode(id discover.NodeID) (err error) {
+func (s *Simulation) ConnectToRandomNode(id enode.ID) (err error) {
 	n := s.RandomUpNode(id)
 	n := s.RandomUpNode(id)
 	if n == nil {
 	if n == nil {
 		return ErrNodeNotFound
 		return ErrNodeNotFound
@@ -64,7 +64,7 @@ func (s *Simulation) ConnectToRandomNode(id discover.NodeID) (err error) {
 // ConnectNodesFull connects all nodes one to another.
 // ConnectNodesFull connects all nodes one to another.
 // It provides a complete connectivity in the network
 // It provides a complete connectivity in the network
 // which should be rarely needed.
 // which should be rarely needed.
-func (s *Simulation) ConnectNodesFull(ids []discover.NodeID) (err error) {
+func (s *Simulation) ConnectNodesFull(ids []enode.ID) (err error) {
 	if ids == nil {
 	if ids == nil {
 		ids = s.UpNodeIDs()
 		ids = s.UpNodeIDs()
 	}
 	}
@@ -82,7 +82,7 @@ func (s *Simulation) ConnectNodesFull(ids []discover.NodeID) (err error) {
 
 
 // ConnectNodesChain connects all nodes in a chain topology.
 // ConnectNodesChain connects all nodes in a chain topology.
 // If ids argument is nil, all nodes that are up will be connected.
 // If ids argument is nil, all nodes that are up will be connected.
-func (s *Simulation) ConnectNodesChain(ids []discover.NodeID) (err error) {
+func (s *Simulation) ConnectNodesChain(ids []enode.ID) (err error) {
 	if ids == nil {
 	if ids == nil {
 		ids = s.UpNodeIDs()
 		ids = s.UpNodeIDs()
 	}
 	}
@@ -98,7 +98,7 @@ func (s *Simulation) ConnectNodesChain(ids []discover.NodeID) (err error) {
 
 
 // ConnectNodesRing connects all nodes in a ring topology.
 // ConnectNodesRing connects all nodes in a ring topology.
 // If ids argument is nil, all nodes that are up will be connected.
 // If ids argument is nil, all nodes that are up will be connected.
-func (s *Simulation) ConnectNodesRing(ids []discover.NodeID) (err error) {
+func (s *Simulation) ConnectNodesRing(ids []enode.ID) (err error) {
 	if ids == nil {
 	if ids == nil {
 		ids = s.UpNodeIDs()
 		ids = s.UpNodeIDs()
 	}
 	}
@@ -118,7 +118,7 @@ func (s *Simulation) ConnectNodesRing(ids []discover.NodeID) (err error) {
 // ConnectNodesStar connects all nodes in a star topology
 // ConnectNodesStar connects all nodes in a star topology
 // with the center at provided NodeID.
 // with the center at provided NodeID.
 // If ids argument is nil, all nodes that are up will be connected.
 // If ids argument is nil, all nodes that are up will be connected.
-func (s *Simulation) ConnectNodesStar(id discover.NodeID, ids []discover.NodeID) (err error) {
+func (s *Simulation) ConnectNodesStar(id enode.ID, ids []enode.ID) (err error) {
 	if ids == nil {
 	if ids == nil {
 		ids = s.UpNodeIDs()
 		ids = s.UpNodeIDs()
 	}
 	}
@@ -138,7 +138,7 @@ func (s *Simulation) ConnectNodesStar(id discover.NodeID, ids []discover.NodeID)
 // ConnectNodesStarPivot connects all nodes in a star topology
 // ConnectNodesStarPivot connects all nodes in a star topology
 // with the center at already set pivot node.
 // with the center at already set pivot node.
 // If ids argument is nil, all nodes that are up will be connected.
 // If ids argument is nil, all nodes that are up will be connected.
-func (s *Simulation) ConnectNodesStarPivot(ids []discover.NodeID) (err error) {
+func (s *Simulation) ConnectNodesStarPivot(ids []enode.ID) (err error) {
 	id := s.PivotNodeID()
 	id := s.PivotNodeID()
 	if id == nil {
 	if id == nil {
 		return ErrNoPivotNode
 		return ErrNoPivotNode
@@ -147,7 +147,7 @@ func (s *Simulation) ConnectNodesStarPivot(ids []discover.NodeID) (err error) {
 }
 }
 
 
 // connect connects two nodes but ignores already connected error.
 // connect connects two nodes but ignores already connected error.
-func (s *Simulation) connect(oneID, otherID discover.NodeID) error {
+func (s *Simulation) connect(oneID, otherID enode.ID) error {
 	return ignoreAlreadyConnectedErr(s.Net.Connect(oneID, otherID))
 	return ignoreAlreadyConnectedErr(s.Net.Connect(oneID, otherID))
 }
 }
 
 

+ 5 - 5
swarm/network/simulation/connect_test.go

@@ -19,7 +19,7 @@ package simulation
 import (
 import (
 	"testing"
 	"testing"
 
 
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 )
 
 
 func TestConnectToPivotNode(t *testing.T) {
 func TestConnectToPivotNode(t *testing.T) {
@@ -143,7 +143,7 @@ func TestConnectNodesFull(t *testing.T) {
 	testFull(t, sim, ids)
 	testFull(t, sim, ids)
 }
 }
 
 
-func testFull(t *testing.T, sim *Simulation, ids []discover.NodeID) {
+func testFull(t *testing.T, sim *Simulation, ids []enode.ID) {
 	n := len(ids)
 	n := len(ids)
 	var cc int
 	var cc int
 	for i := 0; i < n; i++ {
 	for i := 0; i < n; i++ {
@@ -182,7 +182,7 @@ func TestConnectNodesChain(t *testing.T) {
 	testChain(t, sim, ids)
 	testChain(t, sim, ids)
 }
 }
 
 
-func testChain(t *testing.T, sim *Simulation, ids []discover.NodeID) {
+func testChain(t *testing.T, sim *Simulation, ids []enode.ID) {
 	n := len(ids)
 	n := len(ids)
 	for i := 0; i < n; i++ {
 	for i := 0; i < n; i++ {
 		for j := i + 1; j < n; j++ {
 		for j := i + 1; j < n; j++ {
@@ -221,7 +221,7 @@ func TestConnectNodesRing(t *testing.T) {
 	testRing(t, sim, ids)
 	testRing(t, sim, ids)
 }
 }
 
 
-func testRing(t *testing.T, sim *Simulation, ids []discover.NodeID) {
+func testRing(t *testing.T, sim *Simulation, ids []enode.ID) {
 	n := len(ids)
 	n := len(ids)
 	for i := 0; i < n; i++ {
 	for i := 0; i < n; i++ {
 		for j := i + 1; j < n; j++ {
 		for j := i + 1; j < n; j++ {
@@ -262,7 +262,7 @@ func TestConnectToNodesStar(t *testing.T) {
 	testStar(t, sim, ids, centerIndex)
 	testStar(t, sim, ids, centerIndex)
 }
 }
 
 
-func testStar(t *testing.T, sim *Simulation, ids []discover.NodeID, centerIndex int) {
+func testStar(t *testing.T, sim *Simulation, ids []enode.ID, centerIndex int) {
 	n := len(ids)
 	n := len(ids)
 	for i := 0; i < n; i++ {
 	for i := 0; i < n; i++ {
 		for j := i + 1; j < n; j++ {
 		for j := i + 1; j < n; j++ {

+ 4 - 5
swarm/network/simulation/events.go

@@ -20,15 +20,14 @@ import (
 	"context"
 	"context"
 	"sync"
 	"sync"
 
 
-	"github.com/ethereum/go-ethereum/p2p/discover"
-
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 )
 )
 
 
 // PeerEvent is the type of the channel returned by Simulation.PeerEvents.
 // PeerEvent is the type of the channel returned by Simulation.PeerEvents.
 type PeerEvent struct {
 type PeerEvent struct {
 	// NodeID is the ID of node that the event is caught on.
 	// NodeID is the ID of node that the event is caught on.
-	NodeID discover.NodeID
+	NodeID enode.ID
 	// Event is the event that is caught.
 	// Event is the event that is caught.
 	Event *p2p.PeerEvent
 	Event *p2p.PeerEvent
 	// Error is the error that may have happened during event watching.
 	// Error is the error that may have happened during event watching.
@@ -69,7 +68,7 @@ func (f *PeerEventsFilter) MsgCode(c uint64) *PeerEventsFilter {
 // PeerEvents returns a channel of events that are captured by admin peerEvents
 // PeerEvents returns a channel of events that are captured by admin peerEvents
 // subscription nodes with provided NodeIDs. Additional filters can be set to ignore
 // subscription nodes with provided NodeIDs. Additional filters can be set to ignore
 // events that are not relevant.
 // events that are not relevant.
-func (s *Simulation) PeerEvents(ctx context.Context, ids []discover.NodeID, filters ...*PeerEventsFilter) <-chan PeerEvent {
+func (s *Simulation) PeerEvents(ctx context.Context, ids []enode.ID, filters ...*PeerEventsFilter) <-chan PeerEvent {
 	eventC := make(chan PeerEvent)
 	eventC := make(chan PeerEvent)
 
 
 	// wait group to make sure all subscriptions to admin peerEvents are established
 	// wait group to make sure all subscriptions to admin peerEvents are established
@@ -78,7 +77,7 @@ func (s *Simulation) PeerEvents(ctx context.Context, ids []discover.NodeID, filt
 	for _, id := range ids {
 	for _, id := range ids {
 		s.shutdownWG.Add(1)
 		s.shutdownWG.Add(1)
 		subsWG.Add(1)
 		subsWG.Add(1)
-		go func(id discover.NodeID) {
+		go func(id enode.ID) {
 			defer s.shutdownWG.Done()
 			defer s.shutdownWG.Done()
 
 
 			client, err := s.Net.GetNode(id).Client()
 			client, err := s.Net.GetNode(id).Client()

+ 1 - 1
swarm/network/simulation/example_test.go

@@ -36,7 +36,7 @@ import (
 func ExampleSimulation_WaitTillHealthy() {
 func ExampleSimulation_WaitTillHealthy() {
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 		"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
 		"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
-			addr := network.NewAddrFromNodeID(ctx.Config.ID)
+			addr := network.NewAddr(ctx.Config.Node())
 			hp := network.NewHiveParams()
 			hp := network.NewHiveParams()
 			hp.Discovery = false
 			hp.Discovery = false
 			config := &network.BzzConfig{
 			config := &network.BzzConfig{

+ 5 - 6
swarm/network/simulation/kademlia.go

@@ -21,10 +21,9 @@ import (
 	"encoding/hex"
 	"encoding/hex"
 	"time"
 	"time"
 
 
-	"github.com/ethereum/go-ethereum/p2p/discover"
-
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/network"
 )
 )
 
 
@@ -34,7 +33,7 @@ var BucketKeyKademlia BucketKey = "kademlia"
 
 
 // WaitTillHealthy is blocking until the health of all kademlias is true.
 // WaitTillHealthy is blocking until the health of all kademlias is true.
 // If error is not nil, a map of kademlia that was found not healthy is returned.
 // If error is not nil, a map of kademlia that was found not healthy is returned.
-func (s *Simulation) WaitTillHealthy(ctx context.Context, kadMinProxSize int) (ill map[discover.NodeID]*network.Kademlia, err error) {
+func (s *Simulation) WaitTillHealthy(ctx context.Context, kadMinProxSize int) (ill map[enode.ID]*network.Kademlia, err error) {
 	// Prepare PeerPot map for checking Kademlia health
 	// Prepare PeerPot map for checking Kademlia health
 	var ppmap map[string]*network.PeerPot
 	var ppmap map[string]*network.PeerPot
 	kademlias := s.kademlias()
 	kademlias := s.kademlias()
@@ -48,7 +47,7 @@ func (s *Simulation) WaitTillHealthy(ctx context.Context, kadMinProxSize int) (i
 	ticker := time.NewTicker(200 * time.Millisecond)
 	ticker := time.NewTicker(200 * time.Millisecond)
 	defer ticker.Stop()
 	defer ticker.Stop()
 
 
-	ill = make(map[discover.NodeID]*network.Kademlia)
+	ill = make(map[enode.ID]*network.Kademlia)
 	for {
 	for {
 		select {
 		select {
 		case <-ctx.Done():
 		case <-ctx.Done():
@@ -82,9 +81,9 @@ func (s *Simulation) WaitTillHealthy(ctx context.Context, kadMinProxSize int) (i
 
 
 // kademlias returns all Kademlia instances that are set
 // kademlias returns all Kademlia instances that are set
 // in simulation bucket.
 // in simulation bucket.
-func (s *Simulation) kademlias() (ks map[discover.NodeID]*network.Kademlia) {
+func (s *Simulation) kademlias() (ks map[enode.ID]*network.Kademlia) {
 	items := s.UpNodesItems(BucketKeyKademlia)
 	items := s.UpNodesItems(BucketKeyKademlia)
-	ks = make(map[discover.NodeID]*network.Kademlia, len(items))
+	ks = make(map[enode.ID]*network.Kademlia, len(items))
 	for id, v := range items {
 	for id, v := range items {
 		k, ok := v.(*network.Kademlia)
 		k, ok := v.(*network.Kademlia)
 		if !ok {
 		if !ok {

+ 1 - 1
swarm/network/simulation/kademlia_test.go

@@ -30,7 +30,7 @@ import (
 func TestWaitTillHealthy(t *testing.T) {
 func TestWaitTillHealthy(t *testing.T) {
 	sim := New(map[string]ServiceFunc{
 	sim := New(map[string]ServiceFunc{
 		"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
 		"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
-			addr := network.NewAddrFromNodeID(ctx.Config.ID)
+			addr := network.NewAddr(ctx.Config.Node())
 			hp := network.NewHiveParams()
 			hp := network.NewHiveParams()
 			hp.Discovery = false
 			hp.Discovery = false
 			config := &network.BzzConfig{
 			config := &network.BzzConfig{

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

@@ -25,15 +25,15 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"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"
 )
 )
 
 
 // NodeIDs returns NodeIDs for all nodes in the network.
 // NodeIDs returns NodeIDs for all nodes in the network.
-func (s *Simulation) NodeIDs() (ids []discover.NodeID) {
+func (s *Simulation) NodeIDs() (ids []enode.ID) {
 	nodes := s.Net.GetNodes()
 	nodes := s.Net.GetNodes()
-	ids = make([]discover.NodeID, len(nodes))
+	ids = make([]enode.ID, len(nodes))
 	for i, node := range nodes {
 	for i, node := range nodes {
 		ids[i] = node.ID()
 		ids[i] = node.ID()
 	}
 	}
@@ -41,7 +41,7 @@ func (s *Simulation) NodeIDs() (ids []discover.NodeID) {
 }
 }
 
 
 // UpNodeIDs returns NodeIDs for nodes that are up in the network.
 // UpNodeIDs returns NodeIDs for nodes that are up in the network.
-func (s *Simulation) UpNodeIDs() (ids []discover.NodeID) {
+func (s *Simulation) UpNodeIDs() (ids []enode.ID) {
 	nodes := s.Net.GetNodes()
 	nodes := s.Net.GetNodes()
 	for _, node := range nodes {
 	for _, node := range nodes {
 		if node.Up {
 		if node.Up {
@@ -52,7 +52,7 @@ func (s *Simulation) UpNodeIDs() (ids []discover.NodeID) {
 }
 }
 
 
 // DownNodeIDs returns NodeIDs for nodes that are stopped in the network.
 // DownNodeIDs returns NodeIDs for nodes that are stopped in the network.
-func (s *Simulation) DownNodeIDs() (ids []discover.NodeID) {
+func (s *Simulation) DownNodeIDs() (ids []enode.ID) {
 	nodes := s.Net.GetNodes()
 	nodes := s.Net.GetNodes()
 	for _, node := range nodes {
 	for _, node := range nodes {
 		if !node.Up {
 		if !node.Up {
@@ -88,7 +88,7 @@ func AddNodeWithService(serviceName string) AddNodeOption {
 // applies provided options to the config and adds the node to network.
 // applies provided options to the config and adds the node to network.
 // By default all services will be started on a node. If one or more
 // By default all services will be started on a node. If one or more
 // AddNodeWithService option are provided, only specified services will be started.
 // AddNodeWithService option are provided, only specified services will be started.
-func (s *Simulation) AddNode(opts ...AddNodeOption) (id discover.NodeID, err error) {
+func (s *Simulation) AddNode(opts ...AddNodeOption) (id enode.ID, err error) {
 	conf := adapters.RandomNodeConfig()
 	conf := adapters.RandomNodeConfig()
 	for _, o := range opts {
 	for _, o := range opts {
 		o(conf)
 		o(conf)
@@ -105,8 +105,8 @@ func (s *Simulation) AddNode(opts ...AddNodeOption) (id discover.NodeID, err err
 
 
 // AddNodes creates new nodes with random configurations,
 // AddNodes creates new nodes with random configurations,
 // applies provided options to the config and adds nodes to network.
 // applies provided options to the config and adds nodes to network.
-func (s *Simulation) AddNodes(count int, opts ...AddNodeOption) (ids []discover.NodeID, err error) {
-	ids = make([]discover.NodeID, 0, count)
+func (s *Simulation) AddNodes(count int, opts ...AddNodeOption) (ids []enode.ID, err error) {
+	ids = make([]enode.ID, 0, count)
 	for i := 0; i < count; i++ {
 	for i := 0; i < count; i++ {
 		id, err := s.AddNode(opts...)
 		id, err := s.AddNode(opts...)
 		if err != nil {
 		if err != nil {
@@ -119,7 +119,7 @@ func (s *Simulation) AddNodes(count int, opts ...AddNodeOption) (ids []discover.
 
 
 // AddNodesAndConnectFull is a helpper method that combines
 // AddNodesAndConnectFull is a helpper method that combines
 // AddNodes and ConnectNodesFull. Only new nodes will be connected.
 // AddNodes and ConnectNodesFull. Only new nodes will be connected.
-func (s *Simulation) AddNodesAndConnectFull(count int, opts ...AddNodeOption) (ids []discover.NodeID, err error) {
+func (s *Simulation) AddNodesAndConnectFull(count int, opts ...AddNodeOption) (ids []enode.ID, err error) {
 	if count < 2 {
 	if count < 2 {
 		return nil, errors.New("count of nodes must be at least 2")
 		return nil, errors.New("count of nodes must be at least 2")
 	}
 	}
@@ -137,7 +137,7 @@ func (s *Simulation) AddNodesAndConnectFull(count int, opts ...AddNodeOption) (i
 // AddNodesAndConnectChain is a helpper method that combines
 // AddNodesAndConnectChain is a helpper method that combines
 // AddNodes and ConnectNodesChain. The chain will be continued from the last
 // AddNodes and ConnectNodesChain. The chain will be continued from the last
 // added node, if there is one in simulation using ConnectToLastNode method.
 // added node, if there is one in simulation using ConnectToLastNode method.
-func (s *Simulation) AddNodesAndConnectChain(count int, opts ...AddNodeOption) (ids []discover.NodeID, err error) {
+func (s *Simulation) AddNodesAndConnectChain(count int, opts ...AddNodeOption) (ids []enode.ID, err error) {
 	if count < 2 {
 	if count < 2 {
 		return nil, errors.New("count of nodes must be at least 2")
 		return nil, errors.New("count of nodes must be at least 2")
 	}
 	}
@@ -153,7 +153,7 @@ func (s *Simulation) AddNodesAndConnectChain(count int, opts ...AddNodeOption) (
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	ids = append([]discover.NodeID{id}, ids...)
+	ids = append([]enode.ID{id}, ids...)
 	err = s.ConnectNodesChain(ids)
 	err = s.ConnectNodesChain(ids)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -163,7 +163,7 @@ func (s *Simulation) AddNodesAndConnectChain(count int, opts ...AddNodeOption) (
 
 
 // AddNodesAndConnectRing is a helpper method that combines
 // AddNodesAndConnectRing is a helpper method that combines
 // AddNodes and ConnectNodesRing.
 // AddNodes and ConnectNodesRing.
-func (s *Simulation) AddNodesAndConnectRing(count int, opts ...AddNodeOption) (ids []discover.NodeID, err error) {
+func (s *Simulation) AddNodesAndConnectRing(count int, opts ...AddNodeOption) (ids []enode.ID, err error) {
 	if count < 2 {
 	if count < 2 {
 		return nil, errors.New("count of nodes must be at least 2")
 		return nil, errors.New("count of nodes must be at least 2")
 	}
 	}
@@ -180,7 +180,7 @@ func (s *Simulation) AddNodesAndConnectRing(count int, opts ...AddNodeOption) (i
 
 
 // AddNodesAndConnectStar is a helpper method that combines
 // AddNodesAndConnectStar is a helpper method that combines
 // AddNodes and ConnectNodesStar.
 // AddNodes and ConnectNodesStar.
-func (s *Simulation) AddNodesAndConnectStar(count int, opts ...AddNodeOption) (ids []discover.NodeID, err error) {
+func (s *Simulation) AddNodesAndConnectStar(count int, opts ...AddNodeOption) (ids []enode.ID, err error) {
 	if count < 2 {
 	if count < 2 {
 		return nil, errors.New("count of nodes must be at least 2")
 		return nil, errors.New("count of nodes must be at least 2")
 	}
 	}
@@ -246,7 +246,7 @@ func (s *Simulation) UploadSnapshot(snapshotFile string, opts ...AddNodeOption)
 // differently then other nodes in test. SetPivotNode and
 // differently then other nodes in test. SetPivotNode and
 // PivotNodeID are just a convenient functions to set and
 // PivotNodeID are just a convenient functions to set and
 // retrieve it.
 // retrieve it.
-func (s *Simulation) SetPivotNode(id discover.NodeID) {
+func (s *Simulation) SetPivotNode(id enode.ID) {
 	s.mu.Lock()
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	defer s.mu.Unlock()
 	s.pivotNodeID = &id
 	s.pivotNodeID = &id
@@ -254,19 +254,19 @@ func (s *Simulation) SetPivotNode(id discover.NodeID) {
 
 
 // PivotNodeID returns NodeID of the pivot node set by
 // PivotNodeID returns NodeID of the pivot node set by
 // Simulation.SetPivotNode method.
 // Simulation.SetPivotNode method.
-func (s *Simulation) PivotNodeID() (id *discover.NodeID) {
+func (s *Simulation) PivotNodeID() (id *enode.ID) {
 	s.mu.Lock()
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	defer s.mu.Unlock()
 	return s.pivotNodeID
 	return s.pivotNodeID
 }
 }
 
 
 // StartNode starts a node by NodeID.
 // StartNode starts a node by NodeID.
-func (s *Simulation) StartNode(id discover.NodeID) (err error) {
+func (s *Simulation) StartNode(id enode.ID) (err error) {
 	return s.Net.Start(id)
 	return s.Net.Start(id)
 }
 }
 
 
 // StartRandomNode starts a random node.
 // StartRandomNode starts a random node.
-func (s *Simulation) StartRandomNode() (id discover.NodeID, err error) {
+func (s *Simulation) StartRandomNode() (id enode.ID, err error) {
 	n := s.randomDownNode()
 	n := s.randomDownNode()
 	if n == nil {
 	if n == nil {
 		return id, ErrNodeNotFound
 		return id, ErrNodeNotFound
@@ -275,8 +275,8 @@ func (s *Simulation) StartRandomNode() (id discover.NodeID, err error) {
 }
 }
 
 
 // StartRandomNodes starts random nodes.
 // StartRandomNodes starts random nodes.
-func (s *Simulation) StartRandomNodes(count int) (ids []discover.NodeID, err error) {
-	ids = make([]discover.NodeID, 0, count)
+func (s *Simulation) StartRandomNodes(count int) (ids []enode.ID, err error) {
+	ids = make([]enode.ID, 0, count)
 	downIDs := s.DownNodeIDs()
 	downIDs := s.DownNodeIDs()
 	for i := 0; i < count; i++ {
 	for i := 0; i < count; i++ {
 		n := s.randomNode(downIDs, ids...)
 		n := s.randomNode(downIDs, ids...)
@@ -293,12 +293,12 @@ func (s *Simulation) StartRandomNodes(count int) (ids []discover.NodeID, err err
 }
 }
 
 
 // StopNode stops a node by NodeID.
 // StopNode stops a node by NodeID.
-func (s *Simulation) StopNode(id discover.NodeID) (err error) {
+func (s *Simulation) StopNode(id enode.ID) (err error) {
 	return s.Net.Stop(id)
 	return s.Net.Stop(id)
 }
 }
 
 
 // StopRandomNode stops a random node.
 // StopRandomNode stops a random node.
-func (s *Simulation) StopRandomNode() (id discover.NodeID, err error) {
+func (s *Simulation) StopRandomNode() (id enode.ID, err error) {
 	n := s.RandomUpNode()
 	n := s.RandomUpNode()
 	if n == nil {
 	if n == nil {
 		return id, ErrNodeNotFound
 		return id, ErrNodeNotFound
@@ -307,8 +307,8 @@ func (s *Simulation) StopRandomNode() (id discover.NodeID, err error) {
 }
 }
 
 
 // StopRandomNodes stops random nodes.
 // StopRandomNodes stops random nodes.
-func (s *Simulation) StopRandomNodes(count int) (ids []discover.NodeID, err error) {
-	ids = make([]discover.NodeID, 0, count)
+func (s *Simulation) StopRandomNodes(count int) (ids []enode.ID, err error) {
+	ids = make([]enode.ID, 0, count)
 	upIDs := s.UpNodeIDs()
 	upIDs := s.UpNodeIDs()
 	for i := 0; i < count; i++ {
 	for i := 0; i < count; i++ {
 		n := s.randomNode(upIDs, ids...)
 		n := s.randomNode(upIDs, ids...)
@@ -331,17 +331,17 @@ func init() {
 
 
 // RandomUpNode returns a random SimNode that is up.
 // RandomUpNode returns a random SimNode that is up.
 // Arguments are NodeIDs for nodes that should not be returned.
 // Arguments are NodeIDs for nodes that should not be returned.
-func (s *Simulation) RandomUpNode(exclude ...discover.NodeID) *adapters.SimNode {
+func (s *Simulation) RandomUpNode(exclude ...enode.ID) *adapters.SimNode {
 	return s.randomNode(s.UpNodeIDs(), exclude...)
 	return s.randomNode(s.UpNodeIDs(), exclude...)
 }
 }
 
 
 // randomDownNode returns a random SimNode that is not up.
 // randomDownNode returns a random SimNode that is not up.
-func (s *Simulation) randomDownNode(exclude ...discover.NodeID) *adapters.SimNode {
+func (s *Simulation) randomDownNode(exclude ...enode.ID) *adapters.SimNode {
 	return s.randomNode(s.DownNodeIDs(), exclude...)
 	return s.randomNode(s.DownNodeIDs(), exclude...)
 }
 }
 
 
 // randomNode returns a random SimNode from the slice of NodeIDs.
 // randomNode returns a random SimNode from the slice of NodeIDs.
-func (s *Simulation) randomNode(ids []discover.NodeID, exclude ...discover.NodeID) *adapters.SimNode {
+func (s *Simulation) randomNode(ids []enode.ID, exclude ...enode.ID) *adapters.SimNode {
 	for _, e := range exclude {
 	for _, e := range exclude {
 		var i int
 		var i int
 		for _, id := range ids {
 		for _, id := range ids {

+ 3 - 3
swarm/network/simulation/node_test.go

@@ -25,7 +25,7 @@ import (
 
 
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/node"
 	"github.com/ethereum/go-ethereum/node"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/network"
 )
 )
@@ -75,7 +75,7 @@ func TestUpDownNodeIDs(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func equalNodeIDs(one, other []discover.NodeID) bool {
+func equalNodeIDs(one, other []enode.ID) bool {
 	if len(one) != len(other) {
 	if len(one) != len(other) {
 		return false
 		return false
 	}
 	}
@@ -244,7 +244,7 @@ func TestUploadSnapshot(t *testing.T) {
 	log.Debug("Creating simulation")
 	log.Debug("Creating simulation")
 	s := New(map[string]ServiceFunc{
 	s := New(map[string]ServiceFunc{
 		"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
 		"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
-			addr := network.NewAddrFromNodeID(ctx.Config.ID)
+			addr := network.NewAddr(ctx.Config.Node())
 			hp := network.NewHiveParams()
 			hp := network.NewHiveParams()
 			hp.Discovery = false
 			hp.Discovery = false
 			config := &network.BzzConfig{
 			config := &network.BzzConfig{

+ 4 - 4
swarm/network/simulation/service.go

@@ -18,13 +18,13 @@ package simulation
 
 
 import (
 import (
 	"github.com/ethereum/go-ethereum/node"
 	"github.com/ethereum/go-ethereum/node"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 )
 )
 
 
 // Service returns a single Service by name on a particular node
 // Service returns a single Service by name on a particular node
 // with provided id.
 // with provided id.
-func (s *Simulation) Service(name string, id discover.NodeID) node.Service {
+func (s *Simulation) Service(name string, id enode.ID) node.Service {
 	simNode, ok := s.Net.GetNode(id).Node.(*adapters.SimNode)
 	simNode, ok := s.Net.GetNode(id).Node.(*adapters.SimNode)
 	if !ok {
 	if !ok {
 		return nil
 		return nil
@@ -48,9 +48,9 @@ func (s *Simulation) RandomService(name string) node.Service {
 
 
 // Services returns all services with a provided name
 // Services returns all services with a provided name
 // from nodes that are up.
 // from nodes that are up.
-func (s *Simulation) Services(name string) (services map[discover.NodeID]node.Service) {
+func (s *Simulation) Services(name string) (services map[enode.ID]node.Service) {
 	nodes := s.Net.GetNodes()
 	nodes := s.Net.GetNodes()
-	services = make(map[discover.NodeID]node.Service)
+	services = make(map[enode.ID]node.Service)
 	for _, node := range nodes {
 	for _, node := range nodes {
 		if !node.Up {
 		if !node.Up {
 			continue
 			continue

+ 4 - 4
swarm/network/simulation/simulation.go

@@ -25,7 +25,7 @@ import (
 
 
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/node"
 	"github.com/ethereum/go-ethereum/node"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"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"
 )
 )
@@ -45,8 +45,8 @@ type Simulation struct {
 
 
 	serviceNames []string
 	serviceNames []string
 	cleanupFuncs []func()
 	cleanupFuncs []func()
-	buckets      map[discover.NodeID]*sync.Map
-	pivotNodeID  *discover.NodeID
+	buckets      map[enode.ID]*sync.Map
+	pivotNodeID  *enode.ID
 	shutdownWG   sync.WaitGroup
 	shutdownWG   sync.WaitGroup
 	done         chan struct{}
 	done         chan struct{}
 	mu           sync.RWMutex
 	mu           sync.RWMutex
@@ -70,7 +70,7 @@ type ServiceFunc func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Se
 // simulations.Network initialized with provided services.
 // simulations.Network initialized with provided services.
 func New(services map[string]ServiceFunc) (s *Simulation) {
 func New(services map[string]ServiceFunc) (s *Simulation) {
 	s = &Simulation{
 	s = &Simulation{
-		buckets: make(map[discover.NodeID]*sync.Map),
+		buckets: make(map[enode.ID]*sync.Map),
 		done:    make(chan struct{}),
 		done:    make(chan struct{}),
 	}
 	}
 
 

+ 16 - 20
swarm/network/simulations/discovery/discovery_test.go

@@ -31,11 +31,10 @@ import (
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
-	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"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/discover"
+	"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"
 	"github.com/ethereum/go-ethereum/swarm/network"
@@ -237,8 +236,8 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
 		DefaultService: serviceName,
 		DefaultService: serviceName,
 	})
 	})
 	defer net.Shutdown()
 	defer net.Shutdown()
-	trigger := make(chan discover.NodeID)
-	ids := make([]discover.NodeID, nodes)
+	trigger := make(chan enode.ID)
+	ids := make([]enode.ID, nodes)
 	for i := 0; i < nodes; i++ {
 	for i := 0; i < nodes; i++ {
 		conf := adapters.RandomNodeConfig()
 		conf := adapters.RandomNodeConfig()
 		node, err := net.NewNodeWithConfig(conf)
 		node, err := net.NewNodeWithConfig(conf)
@@ -263,7 +262,7 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
 	wg := sync.WaitGroup{}
 	wg := sync.WaitGroup{}
 	for i := range ids {
 	for i := range ids {
 		// collect the overlay addresses, to
 		// collect the overlay addresses, to
-		addrs = append(addrs, network.ToOverlayAddr(ids[i].Bytes()))
+		addrs = append(addrs, ids[i].Bytes())
 		for j := 0; j < conns; j++ {
 		for j := 0; j < conns; j++ {
 			var k int
 			var k int
 			if j == 0 {
 			if j == 0 {
@@ -282,7 +281,7 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
 	log.Debug(fmt.Sprintf("nodes: %v", len(addrs)))
 	log.Debug(fmt.Sprintf("nodes: %v", len(addrs)))
 	// construct the peer pot, so that kademlia health can be checked
 	// construct the peer pot, so that kademlia health can be checked
 	ppmap := network.NewPeerPotMap(testMinProxBinSize, addrs)
 	ppmap := network.NewPeerPotMap(testMinProxBinSize, addrs)
-	check := func(ctx context.Context, id discover.NodeID) (bool, error) {
+	check := func(ctx context.Context, id enode.ID) (bool, error) {
 		select {
 		select {
 		case <-ctx.Done():
 		case <-ctx.Done():
 			return false, ctx.Err()
 			return false, ctx.Err()
@@ -298,8 +297,7 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
 			return false, fmt.Errorf("error getting node client: %s", err)
 			return false, fmt.Errorf("error getting node client: %s", err)
 		}
 		}
 		healthy := &network.Health{}
 		healthy := &network.Health{}
-		addr := common.Bytes2Hex(network.ToOverlayAddr(id.Bytes()))
-		if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); err != nil {
+		if err := client.Call(&healthy, "hive_healthy", ppmap[id.String()]); err != nil {
 			return false, fmt.Errorf("error getting node health: %s", err)
 			return false, fmt.Errorf("error getting node health: %s", err)
 		}
 		}
 		log.Debug(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v, saturated: %v\n%v", id, healthy.GotNN, healthy.KnowNN, healthy.Full, healthy.Hive))
 		log.Debug(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v, saturated: %v\n%v", id, healthy.GotNN, healthy.KnowNN, healthy.Full, healthy.Hive))
@@ -351,8 +349,8 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
 		DefaultService: serviceName,
 		DefaultService: serviceName,
 	})
 	})
 	defer net.Shutdown()
 	defer net.Shutdown()
-	trigger := make(chan discover.NodeID)
-	ids := make([]discover.NodeID, nodes)
+	trigger := make(chan enode.ID)
+	ids := make([]enode.ID, nodes)
 	var addrs [][]byte
 	var addrs [][]byte
 
 
 	for i := 0; i < nodes; i++ {
 	for i := 0; i < nodes; i++ {
@@ -371,7 +369,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
 			return nil, fmt.Errorf("error triggering checks for node %s: %s", node.ID().TerminalString(), err)
 			return nil, fmt.Errorf("error triggering checks for node %s: %s", node.ID().TerminalString(), err)
 		}
 		}
 		ids[i] = node.ID()
 		ids[i] = node.ID()
-		a := network.ToOverlayAddr(ids[i].Bytes())
+		a := ids[i].Bytes()
 
 
 		addrs = append(addrs, a)
 		addrs = append(addrs, a)
 	}
 	}
@@ -398,12 +396,12 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
 					return fmt.Errorf("error getting node client: %s", err)
 					return fmt.Errorf("error getting node client: %s", err)
 				}
 				}
 				healthy := &network.Health{}
 				healthy := &network.Health{}
-				addr := common.Bytes2Hex(network.ToOverlayAddr(id.Bytes()))
+				addr := id.String()
 				if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); err != nil {
 				if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); err != nil {
 					return fmt.Errorf("error getting node health: %s", err)
 					return fmt.Errorf("error getting node health: %s", err)
 				}
 				}
 
 
-				log.Info(fmt.Sprintf("NODE: %s, IS HEALTHY: %t", id.String(), healthy.GotNN && healthy.KnowNN && healthy.Full))
+				log.Info(fmt.Sprintf("NODE: %s, IS HEALTHY: %t", addr, healthy.GotNN && healthy.KnowNN && healthy.Full))
 				if !healthy.GotNN || !healthy.Full {
 				if !healthy.GotNN || !healthy.Full {
 					isHealthy = false
 					isHealthy = false
 					break
 					break
@@ -462,7 +460,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
 	wg.Wait()
 	wg.Wait()
 	log.Debug(fmt.Sprintf("nodes: %v", len(addrs)))
 	log.Debug(fmt.Sprintf("nodes: %v", len(addrs)))
 	// construct the peer pot, so that kademlia health can be checked
 	// construct the peer pot, so that kademlia health can be checked
-	check := func(ctx context.Context, id discover.NodeID) (bool, error) {
+	check := func(ctx context.Context, id enode.ID) (bool, error) {
 		select {
 		select {
 		case <-ctx.Done():
 		case <-ctx.Done():
 			return false, ctx.Err()
 			return false, ctx.Err()
@@ -478,8 +476,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
 			return false, fmt.Errorf("error getting node client: %s", err)
 			return false, fmt.Errorf("error getting node client: %s", err)
 		}
 		}
 		healthy := &network.Health{}
 		healthy := &network.Health{}
-		addr := common.Bytes2Hex(network.ToOverlayAddr(id.Bytes()))
-		if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); err != nil {
+		if err := client.Call(&healthy, "hive_healthy", ppmap[id.String()]); err != nil {
 			return false, fmt.Errorf("error getting node health: %s", err)
 			return false, fmt.Errorf("error getting node health: %s", err)
 		}
 		}
 		log.Info(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v, saturated: %v", id, healthy.GotNN, healthy.KnowNN, healthy.Full))
 		log.Info(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v, saturated: %v", id, healthy.GotNN, healthy.KnowNN, healthy.Full))
@@ -510,7 +507,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
 // triggerChecks triggers a simulation step check whenever a peer is added or
 // triggerChecks triggers a simulation step check whenever a peer is added or
 // removed from the given node, and also every second to avoid a race between
 // removed from the given node, and also every second to avoid a race between
 // peer events and kademlia becoming healthy
 // peer events and kademlia becoming healthy
-func triggerChecks(trigger chan discover.NodeID, net *simulations.Network, id discover.NodeID) error {
+func triggerChecks(trigger chan enode.ID, net *simulations.Network, id enode.ID) error {
 	node := net.GetNode(id)
 	node := net.GetNode(id)
 	if node == nil {
 	if node == nil {
 		return fmt.Errorf("unknown node: %s", id)
 		return fmt.Errorf("unknown node: %s", id)
@@ -548,9 +545,8 @@ func triggerChecks(trigger chan discover.NodeID, net *simulations.Network, id di
 }
 }
 
 
 func newService(ctx *adapters.ServiceContext) (node.Service, error) {
 func newService(ctx *adapters.ServiceContext) (node.Service, error) {
-	host := adapters.ExternalIP()
-
-	addr := network.NewAddrFromNodeIDAndPort(ctx.Config.ID, host, ctx.Config.Port)
+	node := enode.NewV4(&ctx.Config.PrivateKey.PublicKey, adapters.ExternalIP(), int(ctx.Config.Port), int(ctx.Config.Port))
+	addr := network.NewAddr(node)
 
 
 	kp := network.NewKadParams()
 	kp := network.NewKadParams()
 	kp.MinProxBinSize = testMinProxBinSize
 	kp.MinProxBinSize = testMinProxBinSize

File diff suppressed because it is too large
+ 0 - 0
swarm/network/simulations/discovery/jsonsnapshot.txt


+ 1 - 0
swarm/network/simulations/discovery/snapshot.json

@@ -0,0 +1 @@
+{"nodes":[{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}}],"conns":[{"one":"c04a0c47cb0c522ecf28d8841e93721e73f58790b30e92382816a4b453be2988","other":"d9283e5247a18d6564b3581217e9f4d9c93a4359944894c00bb2b22c690faadc","up":true},{"one":"dd99c11abe2abae112d64d902b96fe0c75243ea67eca759a2769058a30cc0e77","other":"c04a0c47cb0c522ecf28d8841e93721e73f58790b30e92382816a4b453be2988","up":true},{"one":"4f5dad2aa4f26ac5a23d4fbcc807296b474eab77761db6594debd60ef4287aed","other":"dd99c11abe2abae112d64d902b96fe0c75243ea67eca759a2769058a30cc0e77","up":true},{"one":"4f47f4e176d1c9f78d9a7e19723689ffe2a0603004a3d4506a2349e55a56fc17","other":"4f5dad2aa4f26ac5a23d4fbcc807296b474eab77761db6594debd60ef4287aed","up":true},{"one":"20b6a1be2cb8f966151682350e029d4f8da8ee92de10a2a1cb1727d110acebfa","other":"4f47f4e176d1c9f78d9a7e19723689ffe2a0603004a3d4506a2349e55a56fc17","up":true},{"one":"50cb92e77710582fa9cbee7a54cf25c95fd27d8d54b13ba5520a50139c309a22","other":"20b6a1be2cb8f966151682350e029d4f8da8ee92de10a2a1cb1727d110acebfa","up":true},{"one":"319dc901f99940f1339c540bc36fbabb10a96d326b13b9d7f53e7496980e2996","other":"50cb92e77710582fa9cbee7a54cf25c95fd27d8d54b13ba5520a50139c309a22","up":true},{"one":"dc285b6436a8bfd4d2e586d478b18d3fe7b705ce0b4fb27a651adcf6d27984f1","other":"319dc901f99940f1339c540bc36fbabb10a96d326b13b9d7f53e7496980e2996","up":true},{"one":"974dbe511377280f945a53a194b4bb397875b10b1ecb119a92425bbb16db68f1","other":"dc285b6436a8bfd4d2e586d478b18d3fe7b705ce0b4fb27a651adcf6d27984f1","up":true},{"one":"d9283e5247a18d6564b3581217e9f4d9c93a4359944894c00bb2b22c690faadc","other":"974dbe511377280f945a53a194b4bb397875b10b1ecb119a92425bbb16db68f1","up":true}]}

+ 7 - 7
swarm/network/simulations/overlay.go

@@ -29,7 +29,7 @@ import (
 
 
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/node"
 	"github.com/ethereum/go-ethereum/node"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"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"
 	"github.com/ethereum/go-ethereum/swarm/network"
@@ -64,26 +64,26 @@ func init() {
 
 
 type Simulation struct {
 type Simulation struct {
 	mtx    sync.Mutex
 	mtx    sync.Mutex
-	stores map[discover.NodeID]*state.InmemoryStore
+	stores map[enode.ID]*state.InmemoryStore
 }
 }
 
 
 func NewSimulation() *Simulation {
 func NewSimulation() *Simulation {
 	return &Simulation{
 	return &Simulation{
-		stores: make(map[discover.NodeID]*state.InmemoryStore),
+		stores: make(map[enode.ID]*state.InmemoryStore),
 	}
 	}
 }
 }
 
 
 func (s *Simulation) NewService(ctx *adapters.ServiceContext) (node.Service, error) {
 func (s *Simulation) NewService(ctx *adapters.ServiceContext) (node.Service, error) {
-	id := ctx.Config.ID
+	node := ctx.Config.Node()
 	s.mtx.Lock()
 	s.mtx.Lock()
-	store, ok := s.stores[id]
+	store, ok := s.stores[node.ID()]
 	if !ok {
 	if !ok {
 		store = state.NewInmemoryStore()
 		store = state.NewInmemoryStore()
-		s.stores[id] = store
+		s.stores[node.ID()] = store
 	}
 	}
 	s.mtx.Unlock()
 	s.mtx.Unlock()
 
 
-	addr := network.NewAddrFromNodeID(id)
+	addr := network.NewAddr(node)
 
 
 	kp := network.NewKadParams()
 	kp := network.NewKadParams()
 	kp.MinProxBinSize = 2
 	kp.MinProxBinSize = 2

+ 3 - 3
swarm/network/simulations/overlay_test.go

@@ -26,7 +26,7 @@ import (
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"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/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/log"
 )
 )
@@ -86,7 +86,7 @@ func TestOverlaySim(t *testing.T) {
 
 
 	//variables needed to wait for nodes being up
 	//variables needed to wait for nodes being up
 	var upCount int
 	var upCount int
-	trigger := make(chan discover.NodeID)
+	trigger := make(chan enode.ID)
 
 
 	//wait for all nodes to be up
 	//wait for all nodes to be up
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
@@ -169,7 +169,7 @@ LOOP:
 }
 }
 
 
 //watch for events so we know when all nodes are up
 //watch for events so we know when all nodes are up
-func watchSimEvents(net *simulations.Network, ctx context.Context, trigger chan discover.NodeID) {
+func watchSimEvents(net *simulations.Network, ctx context.Context, trigger chan enode.ID) {
 	events := make(chan *simulations.Event)
 	events := make(chan *simulations.Event)
 	sub := net.Events().Subscribe(events)
 	sub := net.Events().Subscribe(events)
 	defer sub.Unsubscribe()
 	defer sub.Unsubscribe()

+ 4 - 4
swarm/network/stream/common_test.go

@@ -32,7 +32,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
 	p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/network/simulation"
 	"github.com/ethereum/go-ethereum/swarm/network/simulation"
@@ -114,12 +114,12 @@ func newStreamerTester(t *testing.T) (*p2ptest.ProtocolTester, *Registry, *stora
 
 
 	delivery := NewDelivery(to, netStore)
 	delivery := NewDelivery(to, netStore)
 	netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
 	netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
-	streamer := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), nil)
+	streamer := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), nil)
 	teardown := func() {
 	teardown := func() {
 		streamer.Close()
 		streamer.Close()
 		removeDataDir()
 		removeDataDir()
 	}
 	}
-	protocolTester := p2ptest.NewProtocolTester(t, network.NewNodeIDFromAddr(addr), 1, streamer.runProtocol)
+	protocolTester := p2ptest.NewProtocolTester(t, addr.ID(), 1, streamer.runProtocol)
 
 
 	err = waitForPeers(streamer, 1*time.Second, 1)
 	err = waitForPeers(streamer, 1*time.Second, 1)
 	if err != nil {
 	if err != nil {
@@ -240,7 +240,7 @@ func generateRandomFile() (string, error) {
 }
 }
 
 
 //create a local store for the given node
 //create a local store for the given node
-func createTestLocalStorageForID(id discover.NodeID, addr *network.BzzAddr) (storage.ChunkStore, string, error) {
+func createTestLocalStorageForID(id enode.ID, addr *network.BzzAddr) (storage.ChunkStore, string, error) {
 	var datadir string
 	var datadir string
 	var err error
 	var err error
 	datadir, err = ioutil.TempDir("", fmt.Sprintf("syncer-test-%s", id.TerminalString()))
 	datadir, err = ioutil.TempDir("", fmt.Sprintf("syncer-test-%s", id.TerminalString()))

+ 3 - 3
swarm/network/stream/delivery.go

@@ -23,7 +23,7 @@ import (
 	"fmt"
 	"fmt"
 
 
 	"github.com/ethereum/go-ethereum/metrics"
 	"github.com/ethereum/go-ethereum/metrics"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/spancontext"
 	"github.com/ethereum/go-ethereum/swarm/spancontext"
@@ -47,7 +47,7 @@ var (
 type Delivery struct {
 type Delivery struct {
 	chunkStore storage.SyncChunkStore
 	chunkStore storage.SyncChunkStore
 	kad        *network.Kademlia
 	kad        *network.Kademlia
-	getPeer    func(discover.NodeID) *Peer
+	getPeer    func(enode.ID) *Peer
 }
 }
 
 
 func NewDelivery(kad *network.Kademlia, chunkStore storage.SyncChunkStore) *Delivery {
 func NewDelivery(kad *network.Kademlia, chunkStore storage.SyncChunkStore) *Delivery {
@@ -213,7 +213,7 @@ func (d *Delivery) handleChunkDeliveryMsg(ctx context.Context, sp *Peer, req *Ch
 }
 }
 
 
 // RequestFromPeers sends a chunk retrieve request to
 // RequestFromPeers sends a chunk retrieve request to
-func (d *Delivery) RequestFromPeers(ctx context.Context, req *network.Request) (*discover.NodeID, chan struct{}, error) {
+func (d *Delivery) RequestFromPeers(ctx context.Context, req *network.Request) (*enode.ID, chan struct{}, error) {
 	requestFromPeersCount.Inc(1)
 	requestFromPeersCount.Inc(1)
 	var sp *Peer
 	var sp *Peer
 	spID := req.Source
 	spID := req.Source

+ 24 - 25
swarm/network/stream/delivery_test.go

@@ -45,7 +45,7 @@ func TestStreamerRetrieveRequest(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	peerID := tester.IDs[0]
+	node := tester.Nodes[0]
 
 
 	ctx := context.Background()
 	ctx := context.Background()
 	req := network.NewRequest(
 	req := network.NewRequest(
@@ -64,7 +64,7 @@ func TestStreamerRetrieveRequest(t *testing.T) {
 					Addr:      hash0[:],
 					Addr:      hash0[:],
 					SkipCheck: true,
 					SkipCheck: true,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -81,11 +81,11 @@ func TestStreamerUpstreamRetrieveRequestMsgExchangeWithoutStore(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	peerID := tester.IDs[0]
+	node := tester.Nodes[0]
 
 
 	chunk := storage.NewChunk(storage.Address(hash0[:]), nil)
 	chunk := storage.NewChunk(storage.Address(hash0[:]), nil)
 
 
-	peer := streamer.getPeer(peerID)
+	peer := streamer.getPeer(node.ID())
 
 
 	peer.handleSubscribeMsg(context.TODO(), &SubscribeMsg{
 	peer.handleSubscribeMsg(context.TODO(), &SubscribeMsg{
 		Stream:   NewStream(swarmChunkServerStreamName, "", false),
 		Stream:   NewStream(swarmChunkServerStreamName, "", false),
@@ -101,7 +101,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchangeWithoutStore(t *testing.T) {
 				Msg: &RetrieveRequestMsg{
 				Msg: &RetrieveRequestMsg{
 					Addr: chunk.Address()[:],
 					Addr: chunk.Address()[:],
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 		Expects: []p2ptest.Expect{
 		Expects: []p2ptest.Expect{
@@ -113,7 +113,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchangeWithoutStore(t *testing.T) {
 					From:          0,
 					From:          0,
 					To:            0,
 					To:            0,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -133,8 +133,8 @@ func TestStreamerUpstreamRetrieveRequestMsgExchange(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	peerID := tester.IDs[0]
-	peer := streamer.getPeer(peerID)
+	node := tester.Nodes[0]
+	peer := streamer.getPeer(node.ID())
 
 
 	stream := NewStream(swarmChunkServerStreamName, "", false)
 	stream := NewStream(swarmChunkServerStreamName, "", false)
 
 
@@ -159,7 +159,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchange(t *testing.T) {
 				Msg: &RetrieveRequestMsg{
 				Msg: &RetrieveRequestMsg{
 					Addr: hash,
 					Addr: hash,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 		Expects: []p2ptest.Expect{
 		Expects: []p2ptest.Expect{
@@ -175,7 +175,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchange(t *testing.T) {
 					To:     32,
 					To:     32,
 					Stream: stream,
 					Stream: stream,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -200,7 +200,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchange(t *testing.T) {
 					Addr:      hash,
 					Addr:      hash,
 					SkipCheck: true,
 					SkipCheck: true,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 		Expects: []p2ptest.Expect{
 		Expects: []p2ptest.Expect{
@@ -210,7 +210,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchange(t *testing.T) {
 					Addr:  hash,
 					Addr:  hash,
 					SData: hash,
 					SData: hash,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -233,10 +233,10 @@ func TestStreamerDownstreamChunkDeliveryMsgExchange(t *testing.T) {
 		}, nil
 		}, nil
 	})
 	})
 
 
-	peerID := tester.IDs[0]
+	node := tester.Nodes[0]
 
 
 	stream := NewStream("foo", "", true)
 	stream := NewStream("foo", "", true)
-	err = streamer.Subscribe(peerID, stream, NewRange(5, 8), Top)
+	err = streamer.Subscribe(node.ID(), stream, NewRange(5, 8), Top)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Expected no error, got %v", err)
 		t.Fatalf("Expected no error, got %v", err)
 	}
 	}
@@ -254,7 +254,7 @@ func TestStreamerDownstreamChunkDeliveryMsgExchange(t *testing.T) {
 					History:  NewRange(5, 8),
 					History:  NewRange(5, 8),
 					Priority: Top,
 					Priority: Top,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	},
 	},
@@ -267,7 +267,7 @@ func TestStreamerDownstreamChunkDeliveryMsgExchange(t *testing.T) {
 						Addr:  chunkKey,
 						Addr:  chunkKey,
 						SData: chunkData,
 						SData: chunkData,
 					},
 					},
-					Peer: peerID,
+					Peer: node.ID(),
 				},
 				},
 			},
 			},
 		})
 		})
@@ -314,10 +314,9 @@ func TestDeliveryFromNodes(t *testing.T) {
 func testDeliveryFromNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck bool) {
 func testDeliveryFromNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck bool) {
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
 		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
-
-			id := ctx.Config.ID
-			addr := network.NewAddrFromNodeID(id)
-			store, datadir, err := createTestLocalStorageForID(id, addr)
+			node := ctx.Config.Node()
+			addr := network.NewAddr(node)
+			store, datadir, err := createTestLocalStorageForID(node.ID(), addr)
 			if err != nil {
 			if err != nil {
 				return nil, nil, err
 				return nil, nil, err
 			}
 			}
@@ -336,7 +335,7 @@ func testDeliveryFromNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
 			delivery := NewDelivery(kad, netStore)
 			delivery := NewDelivery(kad, netStore)
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
 
 
-			r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
+			r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
 				SkipCheck: skipCheck,
 				SkipCheck: skipCheck,
 			})
 			})
 			bucket.Store(bucketKeyRegistry, r)
 			bucket.Store(bucketKeyRegistry, r)
@@ -502,9 +501,9 @@ func BenchmarkDeliveryFromNodesWithCheck(b *testing.B) {
 func benchmarkDeliveryFromNodes(b *testing.B, nodes, conns, chunkCount int, skipCheck bool) {
 func benchmarkDeliveryFromNodes(b *testing.B, nodes, conns, chunkCount int, skipCheck bool) {
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
 		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
-			id := ctx.Config.ID
-			addr := network.NewAddrFromNodeID(id)
-			store, datadir, err := createTestLocalStorageForID(id, addr)
+			node := ctx.Config.Node()
+			addr := network.NewAddr(node)
+			store, datadir, err := createTestLocalStorageForID(node.ID(), addr)
 			if err != nil {
 			if err != nil {
 				return nil, nil, err
 				return nil, nil, err
 			}
 			}
@@ -522,7 +521,7 @@ func benchmarkDeliveryFromNodes(b *testing.B, nodes, conns, chunkCount int, skip
 			delivery := NewDelivery(kad, netStore)
 			delivery := NewDelivery(kad, netStore)
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
 
 
-			r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
+			r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
 				SkipCheck:       skipCheck,
 				SkipCheck:       skipCheck,
 				DoSync:          true,
 				DoSync:          true,
 				SyncUpdateDelay: 0,
 				SyncUpdateDelay: 0,

+ 7 - 8
swarm/network/stream/intervals_test.go

@@ -30,7 +30,7 @@ import (
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/network/simulation"
 	"github.com/ethereum/go-ethereum/swarm/network/simulation"
@@ -62,10 +62,9 @@ func testIntervals(t *testing.T, live bool, history *Range, skipCheck bool) {
 
 
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 		"intervalsStreamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
 		"intervalsStreamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
-
-			id := ctx.Config.ID
-			addr := network.NewAddrFromNodeID(id)
-			store, datadir, err := createTestLocalStorageForID(id, addr)
+			n := ctx.Config.Node()
+			addr := network.NewAddr(n)
+			store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
 			if err != nil {
 			if err != nil {
 				return nil, nil, err
 				return nil, nil, err
 			}
 			}
@@ -83,7 +82,7 @@ func testIntervals(t *testing.T, live bool, history *Range, skipCheck bool) {
 			delivery := NewDelivery(kad, netStore)
 			delivery := NewDelivery(kad, netStore)
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
 
 
-			r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
+			r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
 				SkipCheck: skipCheck,
 				SkipCheck: skipCheck,
 			})
 			})
 			bucket.Store(bucketKeyRegistry, r)
 			bucket.Store(bucketKeyRegistry, r)
@@ -281,7 +280,7 @@ func testIntervals(t *testing.T, live bool, history *Range, skipCheck bool) {
 	}
 	}
 }
 }
 
 
-func getHashes(ctx context.Context, r *Registry, peerID discover.NodeID, s Stream) (chan []byte, error) {
+func getHashes(ctx context.Context, r *Registry, peerID enode.ID, s Stream) (chan []byte, error) {
 	peer := r.getPeer(peerID)
 	peer := r.getPeer(peerID)
 
 
 	client, err := peer.getClient(ctx, s)
 	client, err := peer.getClient(ctx, s)
@@ -294,7 +293,7 @@ func getHashes(ctx context.Context, r *Registry, peerID discover.NodeID, s Strea
 	return c.hashes, nil
 	return c.hashes, nil
 }
 }
 
 
-func enableNotifications(r *Registry, peerID discover.NodeID, s Stream) error {
+func enableNotifications(r *Registry, peerID enode.ID, s Stream) error {
 	peer := r.getPeer(peerID)
 	peer := r.getPeer(peerID)
 
 
 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)

+ 3 - 3
swarm/network/stream/messages.go

@@ -75,7 +75,7 @@ type RequestSubscriptionMsg struct {
 }
 }
 
 
 func (p *Peer) handleRequestSubscription(ctx context.Context, req *RequestSubscriptionMsg) (err error) {
 func (p *Peer) handleRequestSubscription(ctx context.Context, req *RequestSubscriptionMsg) (err error) {
-	log.Debug(fmt.Sprintf("handleRequestSubscription: streamer %s to subscribe to %s with stream %s", p.streamer.addr.ID(), p.ID(), req.Stream))
+	log.Debug(fmt.Sprintf("handleRequestSubscription: streamer %s to subscribe to %s with stream %s", p.streamer.addr, p.ID(), req.Stream))
 	return p.streamer.Subscribe(p.ID(), req.Stream, req.History, req.Priority)
 	return p.streamer.Subscribe(p.ID(), req.Stream, req.History, req.Priority)
 }
 }
 
 
@@ -92,7 +92,7 @@ func (p *Peer) handleSubscribeMsg(ctx context.Context, req *SubscribeMsg) (err e
 		}
 		}
 	}()
 	}()
 
 
-	log.Debug("received subscription", "from", p.streamer.addr.ID(), "peer", p.ID(), "stream", req.Stream, "history", req.History)
+	log.Debug("received subscription", "from", p.streamer.addr, "peer", p.ID(), "stream", req.Stream, "history", req.History)
 
 
 	f, err := p.streamer.GetServerFunc(req.Stream.Name)
 	f, err := p.streamer.GetServerFunc(req.Stream.Name)
 	if err != nil {
 	if err != nil {
@@ -254,7 +254,7 @@ func (p *Peer) handleOfferedHashesMsg(ctx context.Context, req *OfferedHashesMsg
 		c.sessionAt = req.From
 		c.sessionAt = req.From
 	}
 	}
 	from, to := c.nextBatch(req.To + 1)
 	from, to := c.nextBatch(req.To + 1)
-	log.Trace("set next batch", "peer", p.ID(), "stream", req.Stream, "from", req.From, "to", req.To, "addr", p.streamer.addr.ID())
+	log.Trace("set next batch", "peer", p.ID(), "stream", req.Stream, "from", req.From, "to", req.To, "addr", p.streamer.addr)
 	if from == to {
 	if from == to {
 		return nil
 		return nil
 	}
 	}

+ 17 - 19
swarm/network/stream/snapshot_retrieval_test.go

@@ -24,7 +24,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/ethereum/go-ethereum/node"
 	"github.com/ethereum/go-ethereum/node"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/network"
@@ -116,10 +116,9 @@ The snapshot should have 'streamer' in its service list.
 func runFileRetrievalTest(nodeCount int) error {
 func runFileRetrievalTest(nodeCount int) error {
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
 		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
-
-			id := ctx.Config.ID
-			addr := network.NewAddrFromNodeID(id)
-			store, datadir, err := createTestLocalStorageForID(id, addr)
+			node := ctx.Config.Node()
+			addr := network.NewAddr(node)
+			store, datadir, err := createTestLocalStorageForID(node.ID(), addr)
 			if err != nil {
 			if err != nil {
 				return nil, nil, err
 				return nil, nil, err
 			}
 			}
@@ -134,7 +133,7 @@ func runFileRetrievalTest(nodeCount int) error {
 			delivery := NewDelivery(kad, netStore)
 			delivery := NewDelivery(kad, netStore)
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
 
 
-			r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
+			r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
 				DoSync:          true,
 				DoSync:          true,
 				SyncUpdateDelay: 3 * time.Second,
 				SyncUpdateDelay: 3 * time.Second,
 			})
 			})
@@ -158,9 +157,9 @@ func runFileRetrievalTest(nodeCount int) error {
 
 
 	conf := &synctestConfig{}
 	conf := &synctestConfig{}
 	//map of discover ID to indexes of chunks expected at that ID
 	//map of discover ID to indexes of chunks expected at that ID
-	conf.idToChunksMap = make(map[discover.NodeID][]int)
+	conf.idToChunksMap = make(map[enode.ID][]int)
 	//map of overlay address to discover ID
 	//map of overlay address to discover ID
-	conf.addrToIDMap = make(map[string]discover.NodeID)
+	conf.addrToIDMap = make(map[string]enode.ID)
 	//array where the generated chunk hashes will be stored
 	//array where the generated chunk hashes will be stored
 	conf.hashes = make([]storage.Address, 0)
 	conf.hashes = make([]storage.Address, 0)
 
 
@@ -176,11 +175,11 @@ func runFileRetrievalTest(nodeCount int) error {
 		nodeIDs := sim.UpNodeIDs()
 		nodeIDs := sim.UpNodeIDs()
 		for _, n := range nodeIDs {
 		for _, n := range nodeIDs {
 			//get the kademlia overlay address from this ID
 			//get the kademlia overlay address from this ID
-			a := network.ToOverlayAddr(n.Bytes())
+			a := n.Bytes()
 			//append it to the array of all overlay addresses
 			//append it to the array of all overlay addresses
 			conf.addrs = append(conf.addrs, a)
 			conf.addrs = append(conf.addrs, a)
 			//the proximity calculation is on overlay addr,
 			//the proximity calculation is on overlay addr,
-			//the p2p/simulations check func triggers on discover.NodeID,
+			//the p2p/simulations check func triggers on enode.ID,
 			//so we need to know which overlay addr maps to which nodeID
 			//so we need to know which overlay addr maps to which nodeID
 			conf.addrToIDMap[string(a)] = n
 			conf.addrToIDMap[string(a)] = n
 		}
 		}
@@ -266,10 +265,9 @@ The snapshot should have 'streamer' in its service list.
 func runRetrievalTest(chunkCount int, nodeCount int) error {
 func runRetrievalTest(chunkCount int, nodeCount int) error {
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
 		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
-
-			id := ctx.Config.ID
-			addr := network.NewAddrFromNodeID(id)
-			store, datadir, err := createTestLocalStorageForID(id, addr)
+			node := ctx.Config.Node()
+			addr := network.NewAddr(node)
+			store, datadir, err := createTestLocalStorageForID(node.ID(), addr)
 			if err != nil {
 			if err != nil {
 				return nil, nil, err
 				return nil, nil, err
 			}
 			}
@@ -284,7 +282,7 @@ func runRetrievalTest(chunkCount int, nodeCount int) error {
 			delivery := NewDelivery(kad, netStore)
 			delivery := NewDelivery(kad, netStore)
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
 
 
-			r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
+			r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
 				DoSync:          true,
 				DoSync:          true,
 				SyncUpdateDelay: 0,
 				SyncUpdateDelay: 0,
 			})
 			})
@@ -307,9 +305,9 @@ func runRetrievalTest(chunkCount int, nodeCount int) error {
 
 
 	conf := &synctestConfig{}
 	conf := &synctestConfig{}
 	//map of discover ID to indexes of chunks expected at that ID
 	//map of discover ID to indexes of chunks expected at that ID
-	conf.idToChunksMap = make(map[discover.NodeID][]int)
+	conf.idToChunksMap = make(map[enode.ID][]int)
 	//map of overlay address to discover ID
 	//map of overlay address to discover ID
-	conf.addrToIDMap = make(map[string]discover.NodeID)
+	conf.addrToIDMap = make(map[string]enode.ID)
 	//array where the generated chunk hashes will be stored
 	//array where the generated chunk hashes will be stored
 	conf.hashes = make([]storage.Address, 0)
 	conf.hashes = make([]storage.Address, 0)
 
 
@@ -323,11 +321,11 @@ func runRetrievalTest(chunkCount int, nodeCount int) error {
 		nodeIDs := sim.UpNodeIDs()
 		nodeIDs := sim.UpNodeIDs()
 		for _, n := range nodeIDs {
 		for _, n := range nodeIDs {
 			//get the kademlia overlay address from this ID
 			//get the kademlia overlay address from this ID
-			a := network.ToOverlayAddr(n.Bytes())
+			a := n.Bytes()
 			//append it to the array of all overlay addresses
 			//append it to the array of all overlay addresses
 			conf.addrs = append(conf.addrs, a)
 			conf.addrs = append(conf.addrs, a)
 			//the proximity calculation is on overlay addr,
 			//the proximity calculation is on overlay addr,
-			//the p2p/simulations check func triggers on discover.NodeID,
+			//the p2p/simulations check func triggers on enode.ID,
 			//so we need to know which overlay addr maps to which nodeID
 			//so we need to know which overlay addr maps to which nodeID
 			conf.addrToIDMap[string(a)] = n
 			conf.addrToIDMap[string(a)] = n
 		}
 		}

+ 22 - 25
swarm/network/stream/snapshot_sync_test.go

@@ -30,7 +30,7 @@ import (
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/log"
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/network/simulation"
 	"github.com/ethereum/go-ethereum/swarm/network/simulation"
@@ -45,14 +45,14 @@ const MaxTimeout = 600
 type synctestConfig struct {
 type synctestConfig struct {
 	addrs         [][]byte
 	addrs         [][]byte
 	hashes        []storage.Address
 	hashes        []storage.Address
-	idToChunksMap map[discover.NodeID][]int
+	idToChunksMap map[enode.ID][]int
 	//chunksToNodesMap map[string][]int
 	//chunksToNodesMap map[string][]int
-	addrToIDMap map[string]discover.NodeID
+	addrToIDMap map[string]enode.ID
 }
 }
 
 
 // Tests in this file should not request chunks from peers.
 // Tests in this file should not request chunks from peers.
 // This function will panic indicating that there is a problem if request has been made.
 // This function will panic indicating that there is a problem if request has been made.
-func dummyRequestFromPeers(_ context.Context, req *network.Request) (*discover.NodeID, chan struct{}, error) {
+func dummyRequestFromPeers(_ context.Context, req *network.Request) (*enode.ID, chan struct{}, error) {
 	panic(fmt.Sprintf("unexpected request: address %s, source %s", req.Addr.String(), req.Source.String()))
 	panic(fmt.Sprintf("unexpected request: address %s, source %s", req.Addr.String(), req.Source.String()))
 }
 }
 
 
@@ -134,10 +134,9 @@ func TestSyncingViaDirectSubscribe(t *testing.T) {
 func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
 func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
 		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
-
-			id := ctx.Config.ID
-			addr := network.NewAddrFromNodeID(id)
-			store, datadir, err := createTestLocalStorageForID(id, addr)
+			n := ctx.Config.Node()
+			addr := network.NewAddr(n)
+			store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
 			if err != nil {
 			if err != nil {
 				return nil, nil, err
 				return nil, nil, err
 			}
 			}
@@ -151,7 +150,7 @@ func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
 			delivery := NewDelivery(kad, netStore)
 			delivery := NewDelivery(kad, netStore)
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(dummyRequestFromPeers, true).New
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(dummyRequestFromPeers, true).New
 
 
-			r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
+			r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
 				DoSync:          true,
 				DoSync:          true,
 				SyncUpdateDelay: 3 * time.Second,
 				SyncUpdateDelay: 3 * time.Second,
 			})
 			})
@@ -173,9 +172,9 @@ func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
 
 
 	conf := &synctestConfig{}
 	conf := &synctestConfig{}
 	//map of discover ID to indexes of chunks expected at that ID
 	//map of discover ID to indexes of chunks expected at that ID
-	conf.idToChunksMap = make(map[discover.NodeID][]int)
+	conf.idToChunksMap = make(map[enode.ID][]int)
 	//map of overlay address to discover ID
 	//map of overlay address to discover ID
-	conf.addrToIDMap = make(map[string]discover.NodeID)
+	conf.addrToIDMap = make(map[string]enode.ID)
 	//array where the generated chunk hashes will be stored
 	//array where the generated chunk hashes will be stored
 	conf.hashes = make([]storage.Address, 0)
 	conf.hashes = make([]storage.Address, 0)
 
 
@@ -209,11 +208,11 @@ func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
 		nodeIDs := sim.UpNodeIDs()
 		nodeIDs := sim.UpNodeIDs()
 		for _, n := range nodeIDs {
 		for _, n := range nodeIDs {
 			//get the kademlia overlay address from this ID
 			//get the kademlia overlay address from this ID
-			a := network.ToOverlayAddr(n.Bytes())
+			a := n.Bytes()
 			//append it to the array of all overlay addresses
 			//append it to the array of all overlay addresses
 			conf.addrs = append(conf.addrs, a)
 			conf.addrs = append(conf.addrs, a)
 			//the proximity calculation is on overlay addr,
 			//the proximity calculation is on overlay addr,
-			//the p2p/simulations check func triggers on discover.NodeID,
+			//the p2p/simulations check func triggers on enode.ID,
 			//so we need to know which overlay addr maps to which nodeID
 			//so we need to know which overlay addr maps to which nodeID
 			conf.addrToIDMap[string(a)] = n
 			conf.addrToIDMap[string(a)] = n
 		}
 		}
@@ -317,10 +316,9 @@ kademlia network. The snapshot should have 'streamer' in its service list.
 func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int) error {
 func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int) error {
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 	sim := simulation.New(map[string]simulation.ServiceFunc{
 		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
 		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
-
-			id := ctx.Config.ID
-			addr := network.NewAddrFromNodeID(id)
-			store, datadir, err := createTestLocalStorageForID(id, addr)
+			n := ctx.Config.Node()
+			addr := network.NewAddr(n)
+			store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
 			if err != nil {
 			if err != nil {
 				return nil, nil, err
 				return nil, nil, err
 			}
 			}
@@ -334,7 +332,7 @@ func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int)
 			delivery := NewDelivery(kad, netStore)
 			delivery := NewDelivery(kad, netStore)
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(dummyRequestFromPeers, true).New
 			netStore.NewNetFetcherFunc = network.NewFetcherFactory(dummyRequestFromPeers, true).New
 
 
-			r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), nil)
+			r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), nil)
 			bucket.Store(bucketKeyRegistry, r)
 			bucket.Store(bucketKeyRegistry, r)
 
 
 			fileStore := storage.NewFileStore(netStore, storage.NewFileStoreParams())
 			fileStore := storage.NewFileStore(netStore, storage.NewFileStoreParams())
@@ -357,9 +355,9 @@ func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int)
 
 
 	conf := &synctestConfig{}
 	conf := &synctestConfig{}
 	//map of discover ID to indexes of chunks expected at that ID
 	//map of discover ID to indexes of chunks expected at that ID
-	conf.idToChunksMap = make(map[discover.NodeID][]int)
+	conf.idToChunksMap = make(map[enode.ID][]int)
 	//map of overlay address to discover ID
 	//map of overlay address to discover ID
-	conf.addrToIDMap = make(map[string]discover.NodeID)
+	conf.addrToIDMap = make(map[string]enode.ID)
 	//array where the generated chunk hashes will be stored
 	//array where the generated chunk hashes will be stored
 	conf.hashes = make([]storage.Address, 0)
 	conf.hashes = make([]storage.Address, 0)
 
 
@@ -390,11 +388,11 @@ func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int)
 		nodeIDs := sim.UpNodeIDs()
 		nodeIDs := sim.UpNodeIDs()
 		for _, n := range nodeIDs {
 		for _, n := range nodeIDs {
 			//get the kademlia overlay address from this ID
 			//get the kademlia overlay address from this ID
-			a := network.ToOverlayAddr(n.Bytes())
+			a := n.Bytes()
 			//append it to the array of all overlay addresses
 			//append it to the array of all overlay addresses
 			conf.addrs = append(conf.addrs, a)
 			conf.addrs = append(conf.addrs, a)
 			//the proximity calculation is on overlay addr,
 			//the proximity calculation is on overlay addr,
-			//the p2p/simulations check func triggers on discover.NodeID,
+			//the p2p/simulations check func triggers on enode.ID,
 			//so we need to know which overlay addr maps to which nodeID
 			//so we need to know which overlay addr maps to which nodeID
 			conf.addrToIDMap[string(a)] = n
 			conf.addrToIDMap[string(a)] = n
 		}
 		}
@@ -525,9 +523,8 @@ func startSyncing(r *Registry, conf *synctestConfig) (int, error) {
 	kad := r.delivery.kad
 	kad := r.delivery.kad
 	subCnt := 0
 	subCnt := 0
 	//iterate over each bin and solicit needed subscription to bins
 	//iterate over each bin and solicit needed subscription to bins
-	kad.EachBin(r.addr.Over(), pof, 0, func(conn *network.Peer, po int) bool {
+	kad.EachBin(r.addr[:], pof, 0, func(conn *network.Peer, po int) bool {
 		//identify begin and start index of the bin(s) we want to subscribe to
 		//identify begin and start index of the bin(s) we want to subscribe to
-
 		subCnt++
 		subCnt++
 		err = r.RequestSubscription(conf.addrToIDMap[string(conn.Address())], NewStream("SYNC", FormatSyncBinKey(uint8(po)), true), NewRange(0, 0), High)
 		err = r.RequestSubscription(conf.addrToIDMap[string(conn.Address())], NewStream("SYNC", FormatSyncBinKey(uint8(po)), true), NewRange(0, 0), High)
 		if err != nil {
 		if err != nil {
@@ -580,7 +577,7 @@ func mapKeysToNodes(conf *synctestConfig) {
 }
 }
 
 
 //upload a file(chunks) to a single local node store
 //upload a file(chunks) to a single local node store
-func uploadFileToSingleNodeStore(id discover.NodeID, chunkCount int, lstore *storage.LocalStore) ([]storage.Address, error) {
+func uploadFileToSingleNodeStore(id enode.ID, chunkCount int, lstore *storage.LocalStore) ([]storage.Address, error) {
 	log.Debug(fmt.Sprintf("Uploading to node id: %s", id))
 	log.Debug(fmt.Sprintf("Uploading to node id: %s", id))
 	fileStore := storage.NewFileStore(lstore, storage.NewFileStoreParams())
 	fileStore := storage.NewFileStore(lstore, storage.NewFileStoreParams())
 	size := chunkSize
 	size := chunkSize

+ 18 - 18
swarm/network/stream/stream.go

@@ -25,7 +25,7 @@ import (
 
 
 	"github.com/ethereum/go-ethereum/metrics"
 	"github.com/ethereum/go-ethereum/metrics"
 	"github.com/ethereum/go-ethereum/p2p"
 	"github.com/ethereum/go-ethereum/p2p"
-	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/protocols"
 	"github.com/ethereum/go-ethereum/p2p/protocols"
 	"github.com/ethereum/go-ethereum/rpc"
 	"github.com/ethereum/go-ethereum/rpc"
 	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/log"
@@ -48,15 +48,15 @@ const (
 
 
 // Registry registry for outgoing and incoming streamer constructors
 // Registry registry for outgoing and incoming streamer constructors
 type Registry struct {
 type Registry struct {
+	addr           enode.ID
 	api            *API
 	api            *API
-	addr           *network.BzzAddr
 	skipCheck      bool
 	skipCheck      bool
 	clientMu       sync.RWMutex
 	clientMu       sync.RWMutex
 	serverMu       sync.RWMutex
 	serverMu       sync.RWMutex
 	peersMu        sync.RWMutex
 	peersMu        sync.RWMutex
 	serverFuncs    map[string]func(*Peer, string, bool) (Server, error)
 	serverFuncs    map[string]func(*Peer, string, bool) (Server, error)
 	clientFuncs    map[string]func(*Peer, string, bool) (Client, error)
 	clientFuncs    map[string]func(*Peer, string, bool) (Client, error)
-	peers          map[discover.NodeID]*Peer
+	peers          map[enode.ID]*Peer
 	delivery       *Delivery
 	delivery       *Delivery
 	intervalsStore state.Store
 	intervalsStore state.Store
 	doRetrieve     bool
 	doRetrieve     bool
@@ -71,7 +71,7 @@ type RegistryOptions struct {
 }
 }
 
 
 // NewRegistry is Streamer constructor
 // NewRegistry is Streamer constructor
-func NewRegistry(addr *network.BzzAddr, delivery *Delivery, syncChunkStore storage.SyncChunkStore, intervalsStore state.Store, options *RegistryOptions) *Registry {
+func NewRegistry(localID enode.ID, delivery *Delivery, syncChunkStore storage.SyncChunkStore, intervalsStore state.Store, options *RegistryOptions) *Registry {
 	if options == nil {
 	if options == nil {
 		options = &RegistryOptions{}
 		options = &RegistryOptions{}
 	}
 	}
@@ -79,11 +79,11 @@ func NewRegistry(addr *network.BzzAddr, delivery *Delivery, syncChunkStore stora
 		options.SyncUpdateDelay = 15 * time.Second
 		options.SyncUpdateDelay = 15 * time.Second
 	}
 	}
 	streamer := &Registry{
 	streamer := &Registry{
-		addr:           addr,
+		addr:           localID,
 		skipCheck:      options.SkipCheck,
 		skipCheck:      options.SkipCheck,
 		serverFuncs:    make(map[string]func(*Peer, string, bool) (Server, error)),
 		serverFuncs:    make(map[string]func(*Peer, string, bool) (Server, error)),
 		clientFuncs:    make(map[string]func(*Peer, string, bool) (Client, error)),
 		clientFuncs:    make(map[string]func(*Peer, string, bool) (Client, error)),
-		peers:          make(map[discover.NodeID]*Peer),
+		peers:          make(map[enode.ID]*Peer),
 		delivery:       delivery,
 		delivery:       delivery,
 		intervalsStore: intervalsStore,
 		intervalsStore: intervalsStore,
 		doRetrieve:     options.DoRetrieve,
 		doRetrieve:     options.DoRetrieve,
@@ -220,7 +220,7 @@ func (r *Registry) GetServerFunc(stream string) (func(*Peer, string, bool) (Serv
 	return f, nil
 	return f, nil
 }
 }
 
 
-func (r *Registry) RequestSubscription(peerId discover.NodeID, s Stream, h *Range, prio uint8) error {
+func (r *Registry) RequestSubscription(peerId enode.ID, s Stream, h *Range, prio uint8) error {
 	// check if the stream is registered
 	// check if the stream is registered
 	if _, err := r.GetServerFunc(s.Name); err != nil {
 	if _, err := r.GetServerFunc(s.Name); err != nil {
 		return err
 		return err
@@ -248,7 +248,7 @@ func (r *Registry) RequestSubscription(peerId discover.NodeID, s Stream, h *Rang
 }
 }
 
 
 // Subscribe initiates the streamer
 // Subscribe initiates the streamer
-func (r *Registry) Subscribe(peerId discover.NodeID, s Stream, h *Range, priority uint8) error {
+func (r *Registry) Subscribe(peerId enode.ID, s Stream, h *Range, priority uint8) error {
 	// check if the stream is registered
 	// check if the stream is registered
 	if _, err := r.GetClientFunc(s.Name); err != nil {
 	if _, err := r.GetClientFunc(s.Name); err != nil {
 		return err
 		return err
@@ -288,7 +288,7 @@ func (r *Registry) Subscribe(peerId discover.NodeID, s Stream, h *Range, priorit
 	return peer.SendPriority(context.TODO(), msg, priority)
 	return peer.SendPriority(context.TODO(), msg, priority)
 }
 }
 
 
-func (r *Registry) Unsubscribe(peerId discover.NodeID, s Stream) error {
+func (r *Registry) Unsubscribe(peerId enode.ID, s Stream) error {
 	peer := r.getPeer(peerId)
 	peer := r.getPeer(peerId)
 	if peer == nil {
 	if peer == nil {
 		return fmt.Errorf("peer not found %v", peerId)
 		return fmt.Errorf("peer not found %v", peerId)
@@ -307,7 +307,7 @@ func (r *Registry) Unsubscribe(peerId discover.NodeID, s Stream) error {
 
 
 // Quit sends the QuitMsg to the peer to remove the
 // Quit sends the QuitMsg to the peer to remove the
 // stream peer client and terminate the streaming.
 // stream peer client and terminate the streaming.
-func (r *Registry) Quit(peerId discover.NodeID, s Stream) error {
+func (r *Registry) Quit(peerId enode.ID, s Stream) error {
 	peer := r.getPeer(peerId)
 	peer := r.getPeer(peerId)
 	if peer == nil {
 	if peer == nil {
 		log.Debug("stream quit: peer not found", "peer", peerId, "stream", s)
 		log.Debug("stream quit: peer not found", "peer", peerId, "stream", s)
@@ -327,7 +327,7 @@ func (r *Registry) NodeInfo() interface{} {
 	return nil
 	return nil
 }
 }
 
 
-func (r *Registry) PeerInfo(id discover.NodeID) interface{} {
+func (r *Registry) PeerInfo(id enode.ID) interface{} {
 	return nil
 	return nil
 }
 }
 
 
@@ -335,7 +335,7 @@ func (r *Registry) Close() error {
 	return r.intervalsStore.Close()
 	return r.intervalsStore.Close()
 }
 }
 
 
-func (r *Registry) getPeer(peerId discover.NodeID) *Peer {
+func (r *Registry) getPeer(peerId enode.ID) *Peer {
 	r.peersMu.RLock()
 	r.peersMu.RLock()
 	defer r.peersMu.RUnlock()
 	defer r.peersMu.RUnlock()
 
 
@@ -390,7 +390,7 @@ func (r *Registry) updateSyncing() {
 	// map of all SYNC streams for all peers
 	// map of all SYNC streams for all peers
 	// used at the and of the function to remove servers
 	// used at the and of the function to remove servers
 	// that are not needed anymore
 	// that are not needed anymore
-	subs := make(map[discover.NodeID]map[Stream]struct{})
+	subs := make(map[enode.ID]map[Stream]struct{})
 	r.peersMu.RLock()
 	r.peersMu.RLock()
 	for id, peer := range r.peers {
 	for id, peer := range r.peers {
 		peer.serverMu.RLock()
 		peer.serverMu.RLock()
@@ -407,8 +407,8 @@ func (r *Registry) updateSyncing() {
 	r.peersMu.RUnlock()
 	r.peersMu.RUnlock()
 
 
 	// request subscriptions for all nodes and bins
 	// request subscriptions for all nodes and bins
-	kad.EachBin(r.addr.Over(), pot.DefaultPof(256), 0, func(p *network.Peer, bin int) bool {
-		log.Debug(fmt.Sprintf("Requesting subscription by: registry %s from peer %s for bin: %d", r.addr.ID(), p.ID(), bin))
+	kad.EachBin(r.addr[:], pot.DefaultPof(256), 0, func(p *network.Peer, bin int) bool {
+		log.Debug(fmt.Sprintf("Requesting subscription by: registry %s from peer %s for bin: %d", r.addr, p.ID(), bin))
 
 
 		// bin is always less then 256 and it is safe to convert it to type uint8
 		// bin is always less then 256 and it is safe to convert it to type uint8
 		stream := NewStream("SYNC", FormatSyncBinKey(uint8(bin)), true)
 		stream := NewStream("SYNC", FormatSyncBinKey(uint8(bin)), true)
@@ -446,7 +446,7 @@ func (r *Registry) updateSyncing() {
 
 
 func (r *Registry) runProtocol(p *p2p.Peer, rw p2p.MsgReadWriter) error {
 func (r *Registry) runProtocol(p *p2p.Peer, rw p2p.MsgReadWriter) error {
 	peer := protocols.NewPeer(p, rw, Spec)
 	peer := protocols.NewPeer(p, rw, Spec)
-	bp := network.NewBzzPeer(peer, r.addr)
+	bp := network.NewBzzPeer(peer)
 	np := network.NewPeer(bp, r.delivery.kad)
 	np := network.NewPeer(bp, r.delivery.kad)
 	r.delivery.kad.On(np)
 	r.delivery.kad.On(np)
 	defer r.delivery.kad.Off(np)
 	defer r.delivery.kad.Off(np)
@@ -724,10 +724,10 @@ func NewAPI(r *Registry) *API {
 	}
 	}
 }
 }
 
 
-func (api *API) SubscribeStream(peerId discover.NodeID, s Stream, history *Range, priority uint8) error {
+func (api *API) SubscribeStream(peerId enode.ID, s Stream, history *Range, priority uint8) error {
 	return api.streamer.Subscribe(peerId, s, history, priority)
 	return api.streamer.Subscribe(peerId, s, history, priority)
 }
 }
 
 
-func (api *API) UnsubscribeStream(peerId discover.NodeID, s Stream) error {
+func (api *API) UnsubscribeStream(peerId enode.ID, s Stream) error {
 	return api.streamer.Unsubscribe(peerId, s)
 	return api.streamer.Unsubscribe(peerId, s)
 }
 }

+ 39 - 39
swarm/network/stream/streamer_test.go

@@ -34,7 +34,7 @@ func TestStreamerSubscribe(t *testing.T) {
 	}
 	}
 
 
 	stream := NewStream("foo", "", true)
 	stream := NewStream("foo", "", true)
-	err = streamer.Subscribe(tester.IDs[0], stream, NewRange(0, 0), Top)
+	err = streamer.Subscribe(tester.Nodes[0].ID(), stream, NewRange(0, 0), Top)
 	if err == nil || err.Error() != "stream foo not registered" {
 	if err == nil || err.Error() != "stream foo not registered" {
 		t.Fatalf("Expected error %v, got %v", "stream foo not registered", err)
 		t.Fatalf("Expected error %v, got %v", "stream foo not registered", err)
 	}
 	}
@@ -48,7 +48,7 @@ func TestStreamerRequestSubscription(t *testing.T) {
 	}
 	}
 
 
 	stream := NewStream("foo", "", false)
 	stream := NewStream("foo", "", false)
-	err = streamer.RequestSubscription(tester.IDs[0], stream, &Range{}, Top)
+	err = streamer.RequestSubscription(tester.Nodes[0].ID(), stream, &Range{}, Top)
 	if err == nil || err.Error() != "stream foo not registered" {
 	if err == nil || err.Error() != "stream foo not registered" {
 		t.Fatalf("Expected error %v, got %v", "stream foo not registered", err)
 		t.Fatalf("Expected error %v, got %v", "stream foo not registered", err)
 	}
 	}
@@ -135,10 +135,10 @@ func TestStreamerDownstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
 		return newTestClient(t), nil
 		return newTestClient(t), nil
 	})
 	})
 
 
-	peerID := tester.IDs[0]
+	node := tester.Nodes[0]
 
 
 	stream := NewStream("foo", "", true)
 	stream := NewStream("foo", "", true)
-	err = streamer.Subscribe(peerID, stream, NewRange(5, 8), Top)
+	err = streamer.Subscribe(node.ID(), stream, NewRange(5, 8), Top)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Expected no error, got %v", err)
 		t.Fatalf("Expected no error, got %v", err)
 	}
 	}
@@ -154,7 +154,7 @@ func TestStreamerDownstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
 						History:  NewRange(5, 8),
 						History:  NewRange(5, 8),
 						Priority: Top,
 						Priority: Top,
 					},
 					},
-					Peer: peerID,
+					Peer: node.ID(),
 				},
 				},
 			},
 			},
 		},
 		},
@@ -173,7 +173,7 @@ func TestStreamerDownstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
 						To:     8,
 						To:     8,
 						Stream: stream,
 						Stream: stream,
 					},
 					},
-					Peer: peerID,
+					Peer: node.ID(),
 				},
 				},
 			},
 			},
 			Expects: []p2ptest.Expect{
 			Expects: []p2ptest.Expect{
@@ -185,7 +185,7 @@ func TestStreamerDownstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
 						From:   9,
 						From:   9,
 						To:     0,
 						To:     0,
 					},
 					},
-					Peer: peerID,
+					Peer: node.ID(),
 				},
 				},
 			},
 			},
 		},
 		},
@@ -194,7 +194,7 @@ func TestStreamerDownstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	err = streamer.Unsubscribe(peerID, stream)
+	err = streamer.Unsubscribe(node.ID(), stream)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Expected no error, got %v", err)
 		t.Fatalf("Expected no error, got %v", err)
 	}
 	}
@@ -207,7 +207,7 @@ func TestStreamerDownstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
 				Msg: &UnsubscribeMsg{
 				Msg: &UnsubscribeMsg{
 					Stream: stream,
 					Stream: stream,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -230,7 +230,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
 		return newTestServer(t), nil
 		return newTestServer(t), nil
 	})
 	})
 
 
-	peerID := tester.IDs[0]
+	node := tester.Nodes[0]
 
 
 	err = tester.TestExchanges(p2ptest.Exchange{
 	err = tester.TestExchanges(p2ptest.Exchange{
 		Label: "Subscribe message",
 		Label: "Subscribe message",
@@ -242,7 +242,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
 					History:  NewRange(5, 8),
 					History:  NewRange(5, 8),
 					Priority: Top,
 					Priority: Top,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 		Expects: []p2ptest.Expect{
 		Expects: []p2ptest.Expect{
@@ -257,7 +257,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
 					From:   6,
 					From:   6,
 					To:     9,
 					To:     9,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -274,7 +274,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
 				Msg: &UnsubscribeMsg{
 				Msg: &UnsubscribeMsg{
 					Stream: stream,
 					Stream: stream,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -297,7 +297,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchangeLive(t *testing.T) {
 		return newTestServer(t), nil
 		return newTestServer(t), nil
 	})
 	})
 
 
-	peerID := tester.IDs[0]
+	node := tester.Nodes[0]
 
 
 	err = tester.TestExchanges(p2ptest.Exchange{
 	err = tester.TestExchanges(p2ptest.Exchange{
 		Label: "Subscribe message",
 		Label: "Subscribe message",
@@ -308,7 +308,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchangeLive(t *testing.T) {
 					Stream:   stream,
 					Stream:   stream,
 					Priority: Top,
 					Priority: Top,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 		Expects: []p2ptest.Expect{
 		Expects: []p2ptest.Expect{
@@ -323,7 +323,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchangeLive(t *testing.T) {
 					From:   1,
 					From:   1,
 					To:     1,
 					To:     1,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -340,7 +340,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchangeLive(t *testing.T) {
 				Msg: &UnsubscribeMsg{
 				Msg: &UnsubscribeMsg{
 					Stream: stream,
 					Stream: stream,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -363,7 +363,7 @@ func TestStreamerUpstreamSubscribeErrorMsgExchange(t *testing.T) {
 
 
 	stream := NewStream("bar", "", true)
 	stream := NewStream("bar", "", true)
 
 
-	peerID := tester.IDs[0]
+	node := tester.Nodes[0]
 
 
 	err = tester.TestExchanges(p2ptest.Exchange{
 	err = tester.TestExchanges(p2ptest.Exchange{
 		Label: "Subscribe message",
 		Label: "Subscribe message",
@@ -375,7 +375,7 @@ func TestStreamerUpstreamSubscribeErrorMsgExchange(t *testing.T) {
 					History:  NewRange(5, 8),
 					History:  NewRange(5, 8),
 					Priority: Top,
 					Priority: Top,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 		Expects: []p2ptest.Expect{
 		Expects: []p2ptest.Expect{
@@ -384,7 +384,7 @@ func TestStreamerUpstreamSubscribeErrorMsgExchange(t *testing.T) {
 				Msg: &SubscribeErrorMsg{
 				Msg: &SubscribeErrorMsg{
 					Error: "stream bar not registered",
 					Error: "stream bar not registered",
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -409,7 +409,7 @@ func TestStreamerUpstreamSubscribeLiveAndHistory(t *testing.T) {
 		}, nil
 		}, nil
 	})
 	})
 
 
-	peerID := tester.IDs[0]
+	node := tester.Nodes[0]
 
 
 	err = tester.TestExchanges(p2ptest.Exchange{
 	err = tester.TestExchanges(p2ptest.Exchange{
 		Label: "Subscribe message",
 		Label: "Subscribe message",
@@ -421,7 +421,7 @@ func TestStreamerUpstreamSubscribeLiveAndHistory(t *testing.T) {
 					History:  NewRange(5, 8),
 					History:  NewRange(5, 8),
 					Priority: Top,
 					Priority: Top,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 		Expects: []p2ptest.Expect{
 		Expects: []p2ptest.Expect{
@@ -436,7 +436,7 @@ func TestStreamerUpstreamSubscribeLiveAndHistory(t *testing.T) {
 					From:   6,
 					From:   6,
 					To:     9,
 					To:     9,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 			{
 			{
 				Code: 1,
 				Code: 1,
@@ -449,7 +449,7 @@ func TestStreamerUpstreamSubscribeLiveAndHistory(t *testing.T) {
 					To:     1,
 					To:     1,
 					Hashes: make([]byte, HashSize),
 					Hashes: make([]byte, HashSize),
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -475,9 +475,9 @@ func TestStreamerDownstreamOfferedHashesMsgExchange(t *testing.T) {
 		return tc, nil
 		return tc, nil
 	})
 	})
 
 
-	peerID := tester.IDs[0]
+	node := tester.Nodes[0]
 
 
-	err = streamer.Subscribe(peerID, stream, NewRange(5, 8), Top)
+	err = streamer.Subscribe(node.ID(), stream, NewRange(5, 8), Top)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Expected no error, got %v", err)
 		t.Fatalf("Expected no error, got %v", err)
 	}
 	}
@@ -492,7 +492,7 @@ func TestStreamerDownstreamOfferedHashesMsgExchange(t *testing.T) {
 					History:  NewRange(5, 8),
 					History:  NewRange(5, 8),
 					Priority: Top,
 					Priority: Top,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	},
 	},
@@ -510,7 +510,7 @@ func TestStreamerDownstreamOfferedHashesMsgExchange(t *testing.T) {
 						To:     8,
 						To:     8,
 						Stream: stream,
 						Stream: stream,
 					},
 					},
-					Peer: peerID,
+					Peer: node.ID(),
 				},
 				},
 			},
 			},
 			Expects: []p2ptest.Expect{
 			Expects: []p2ptest.Expect{
@@ -522,7 +522,7 @@ func TestStreamerDownstreamOfferedHashesMsgExchange(t *testing.T) {
 						From:   9,
 						From:   9,
 						To:     0,
 						To:     0,
 					},
 					},
-					Peer: peerID,
+					Peer: node.ID(),
 				},
 				},
 			},
 			},
 		})
 		})
@@ -569,10 +569,10 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
 		return newTestServer(t), nil
 		return newTestServer(t), nil
 	})
 	})
 
 
-	peerID := tester.IDs[0]
+	node := tester.Nodes[0]
 
 
 	stream := NewStream("foo", "", true)
 	stream := NewStream("foo", "", true)
-	err = streamer.RequestSubscription(peerID, stream, NewRange(5, 8), Top)
+	err = streamer.RequestSubscription(node.ID(), stream, NewRange(5, 8), Top)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Expected no error, got %v", err)
 		t.Fatalf("Expected no error, got %v", err)
 	}
 	}
@@ -588,7 +588,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
 						History:  NewRange(5, 8),
 						History:  NewRange(5, 8),
 						Priority: Top,
 						Priority: Top,
 					},
 					},
-					Peer: peerID,
+					Peer: node.ID(),
 				},
 				},
 			},
 			},
 		},
 		},
@@ -602,7 +602,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
 						History:  NewRange(5, 8),
 						History:  NewRange(5, 8),
 						Priority: Top,
 						Priority: Top,
 					},
 					},
-					Peer: peerID,
+					Peer: node.ID(),
 				},
 				},
 			},
 			},
 			Expects: []p2ptest.Expect{
 			Expects: []p2ptest.Expect{
@@ -617,7 +617,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
 						From:   6,
 						From:   6,
 						To:     9,
 						To:     9,
 					},
 					},
-					Peer: peerID,
+					Peer: node.ID(),
 				},
 				},
 				{
 				{
 					Code: 1,
 					Code: 1,
@@ -630,7 +630,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
 						To:     1,
 						To:     1,
 						Hashes: make([]byte, HashSize),
 						Hashes: make([]byte, HashSize),
 					},
 					},
-					Peer: peerID,
+					Peer: node.ID(),
 				},
 				},
 			},
 			},
 		},
 		},
@@ -639,7 +639,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	err = streamer.Quit(peerID, stream)
+	err = streamer.Quit(node.ID(), stream)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Expected no error, got %v", err)
 		t.Fatalf("Expected no error, got %v", err)
 	}
 	}
@@ -652,7 +652,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
 				Msg: &QuitMsg{
 				Msg: &QuitMsg{
 					Stream: stream,
 					Stream: stream,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})
@@ -663,7 +663,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
 
 
 	historyStream := getHistoryStream(stream)
 	historyStream := getHistoryStream(stream)
 
 
-	err = streamer.Quit(peerID, historyStream)
+	err = streamer.Quit(node.ID(), historyStream)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Expected no error, got %v", err)
 		t.Fatalf("Expected no error, got %v", err)
 	}
 	}
@@ -676,7 +676,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
 				Msg: &QuitMsg{
 				Msg: &QuitMsg{
 					Stream: historyStream,
 					Stream: historyStream,
 				},
 				},
-				Peer: peerID,
+				Peer: node.ID(),
 			},
 			},
 		},
 		},
 	})
 	})

+ 1 - 1
swarm/network/stream/syncer.go

@@ -197,7 +197,7 @@ func NewSwarmSyncerClient(p *Peer, store storage.SyncChunkStore, stream Stream)
 
 
 // // StartSyncing is called on the Peer to start the syncing process
 // // StartSyncing is called on the Peer to start the syncing process
 // // the idea is that it is called only after kademlia is close to healthy
 // // the idea is that it is called only after kademlia is close to healthy
-// func StartSyncing(s *Streamer, peerId discover.NodeID, po uint8, nn bool) {
+// func StartSyncing(s *Streamer, peerId enode.ID, po uint8, nn bool) {
 // 	lastPO := po
 // 	lastPO := po
 // 	if nn {
 // 	if nn {
 // 		lastPO = maxPO
 // 		lastPO = maxPO

+ 8 - 8
swarm/network/stream/syncer_test.go

@@ -31,7 +31,7 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common"
 	"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/discover"
+	"github.com/ethereum/go-ethereum/p2p/enode"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
 	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/log"
 	"github.com/ethereum/go-ethereum/swarm/network"
 	"github.com/ethereum/go-ethereum/swarm/network"
@@ -50,7 +50,7 @@ func TestSyncerSimulation(t *testing.T) {
 	testSyncBetweenNodes(t, 16, 1, dataChunkCount, true, 1)
 	testSyncBetweenNodes(t, 16, 1, dataChunkCount, true, 1)
 }
 }
 
 
-func createMockStore(globalStore *mockdb.GlobalStore, id discover.NodeID, addr *network.BzzAddr) (lstore storage.ChunkStore, datadir string, err error) {
+func createMockStore(globalStore *mockdb.GlobalStore, id enode.ID, addr *network.BzzAddr) (lstore storage.ChunkStore, datadir string, err error) {
 	address := common.BytesToAddress(id.Bytes())
 	address := common.BytesToAddress(id.Bytes())
 	mockStore := globalStore.NewNodeStore(address)
 	mockStore := globalStore.NewNodeStore(address)
 	params := storage.NewDefaultLocalStoreParams()
 	params := storage.NewDefaultLocalStoreParams()
@@ -72,8 +72,8 @@ func testSyncBetweenNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
 			var globalStore *mockdb.GlobalStore
 			var globalStore *mockdb.GlobalStore
 			var gDir, datadir string
 			var gDir, datadir string
 
 
-			id := ctx.Config.ID
-			addr := network.NewAddrFromNodeID(id)
+			node := ctx.Config.Node()
+			addr := network.NewAddr(node)
 			//hack to put addresses in same space
 			//hack to put addresses in same space
 			addr.OAddr[0] = byte(0)
 			addr.OAddr[0] = byte(0)
 
 
@@ -82,9 +82,9 @@ func testSyncBetweenNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
 				if err != nil {
 				if err != nil {
 					return nil, nil, fmt.Errorf("Something went wrong; using mockStore enabled but globalStore is nil")
 					return nil, nil, fmt.Errorf("Something went wrong; using mockStore enabled but globalStore is nil")
 				}
 				}
-				store, datadir, err = createMockStore(globalStore, id, addr)
+				store, datadir, err = createMockStore(globalStore, node.ID(), addr)
 			} else {
 			} else {
-				store, datadir, err = createTestLocalStorageForID(id, addr)
+				store, datadir, err = createTestLocalStorageForID(node.ID(), addr)
 			}
 			}
 			if err != nil {
 			if err != nil {
 				return nil, nil, err
 				return nil, nil, err
@@ -113,7 +113,7 @@ func testSyncBetweenNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
 
 
 			bucket.Store(bucketKeyDelivery, delivery)
 			bucket.Store(bucketKeyDelivery, delivery)
 
 
-			r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
+			r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
 				SkipCheck: skipCheck,
 				SkipCheck: skipCheck,
 			})
 			})
 
 
@@ -139,7 +139,7 @@ func testSyncBetweenNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
 	result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error {
 	result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error {
 		nodeIDs := sim.UpNodeIDs()
 		nodeIDs := sim.UpNodeIDs()
 
 
-		nodeIndex := make(map[discover.NodeID]int)
+		nodeIndex := make(map[enode.ID]int)
 		for i, id := range nodeIDs {
 		for i, id := range nodeIDs {
 			nodeIndex[id] = i
 			nodeIndex[id] = i
 		}
 		}

Some files were not shown because too many files changed in this diff