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.
pull/17774/head^2
Felix Lange 6 years ago committed by GitHub
parent 0ae462fb80
commit 30cd5c1854
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      cmd/bootnode/main.go
  2. 8
      cmd/faucet/faucet.go
  3. 4
      cmd/p2psim/main.go
  4. 6
      cmd/swarm/main.go
  5. 13
      cmd/swarm/run_test.go
  6. 6
      cmd/utils/flags.go
  7. 10
      cmd/wnode/main.go
  8. 4
      eth/handler.go
  9. 4
      eth/helper_test.go
  10. 4
      eth/sync.go
  11. 6
      eth/sync_test.go
  12. 4
      les/commons.go
  13. 5
      les/handler.go
  14. 6
      les/helper_test.go
  15. 4
      les/peer.go
  16. 13
      les/protocol.go
  17. 121
      les/serverpool.go
  18. 10
      node/api.go
  19. 12
      node/config.go
  20. 102
      p2p/dial.go
  21. 443
      p2p/dial_test.go
  22. 422
      p2p/discover/node.go
  23. 224
      p2p/discover/table.go
  24. 560
      p2p/discover/table_test.go
  25. 167
      p2p/discover/table_util_test.go
  26. 122
      p2p/discover/udp.go
  27. 82
      p2p/discover/udp_test.go
  28. 126
      p2p/enode/idscheme.go
  29. 74
      p2p/enode/idscheme_test.go
  30. 248
      p2p/enode/node.go
  31. 62
      p2p/enode/node_test.go
  32. 159
      p2p/enode/nodedb.go
  33. 218
      p2p/enode/nodedb_test.go
  34. 194
      p2p/enode/urlv4.go
  35. 190
      p2p/enode/urlv4_test.go
  36. 168
      p2p/enr/enr.go
  37. 124
      p2p/enr/enr_test.go
  38. 26
      p2p/enr/entries.go
  39. 36
      p2p/enr/idscheme_test.go
  40. 6
      p2p/message.go
  41. 36
      p2p/peer.go
  42. 4
      p2p/peer_test.go
  43. 4
      p2p/protocol.go
  44. 44
      p2p/protocols/protocol_test.go
  45. 55
      p2p/rlpx.go
  46. 43
      p2p/rlpx_test.go
  47. 197
      p2p/server.go
  48. 130
      p2p/server_test.go
  49. 4
      p2p/simulations/adapters/docker.go
  50. 10
      p2p/simulations/adapters/exec.go
  51. 28
      p2p/simulations/adapters/inproc.go
  52. 19
      p2p/simulations/adapters/types.go
  53. 6
      p2p/simulations/examples/ping-pong.go
  54. 8
      p2p/simulations/http.go
  55. 16
      p2p/simulations/http_test.go
  56. 8
      p2p/simulations/mocker.go
  57. 4
      p2p/simulations/mocker_test.go
  58. 64
      p2p/simulations/network.go
  59. 10
      p2p/simulations/network_test.go
  60. 14
      p2p/simulations/simulation.go
  61. 12
      p2p/testing/peerpool.go
  62. 34
      p2p/testing/protocolsession.go
  63. 12
      p2p/testing/protocoltester.go
  64. 4
      swarm/api/config.go
  65. 4
      swarm/network/discovery.go
  66. 6
      swarm/network/discovery_test.go
  67. 38
      swarm/network/fetcher.go
  68. 18
      swarm/network/fetcher_test.go
  69. 40
      swarm/network/hive.go
  70. 17
      swarm/network/hive_test.go
  71. 23
      swarm/network/networkid_test.go
  72. 85
      swarm/network/protocol.go
  73. 50
      swarm/network/protocol_test.go
  74. 16
      swarm/network/simulation/bucket.go
  75. 20
      swarm/network/simulation/connect.go
  76. 10
      swarm/network/simulation/connect_test.go
  77. 9
      swarm/network/simulation/events.go
  78. 2
      swarm/network/simulation/example_test.go
  79. 11
      swarm/network/simulation/kademlia.go
  80. 2
      swarm/network/simulation/kademlia_test.go
  81. 52
      swarm/network/simulation/node.go
  82. 6
      swarm/network/simulation/node_test.go
  83. 8
      swarm/network/simulation/service.go
  84. 8
      swarm/network/simulation/simulation.go
  85. 36
      swarm/network/simulations/discovery/discovery_test.go
  86. 1
      swarm/network/simulations/discovery/jsonsnapshot.txt
  87. 1
      swarm/network/simulations/discovery/snapshot.json
  88. 14
      swarm/network/simulations/overlay.go
  89. 6
      swarm/network/simulations/overlay_test.go
  90. 8
      swarm/network/stream/common_test.go
  91. 6
      swarm/network/stream/delivery.go
  92. 49
      swarm/network/stream/delivery_test.go
  93. 15
      swarm/network/stream/intervals_test.go
  94. 6
      swarm/network/stream/messages.go
  95. 36
      swarm/network/stream/snapshot_retrieval_test.go
  96. 47
      swarm/network/stream/snapshot_sync_test.go
  97. 36
      swarm/network/stream/stream.go
  98. 78
      swarm/network/stream/streamer_test.go
  99. 2
      swarm/network/stream/syncer.go
  100. 16
      swarm/network/stream/syncer_test.go
  101. Some files were not shown because too many files have changed in this diff Show More

@ -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)
} }

@ -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()) old, err := enode.ParseV4(boot.String())
stack.Server().AddPeer(old) 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()

@ -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 != "" {

@ -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)
} }

@ -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
} }

@ -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)
} }

@ -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

@ -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()
} }

@ -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)

@ -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

@ -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 pmFull.handle(pmFull.newPeer(63, p2p.NewPeer(enode.ID{}, "empty", nil), io2))
go pmEmpty.handle(pmEmpty.newPeer(63, p2p.NewPeer(discover.NodeID{}, "full", nil), io1)) 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())

@ -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()
} }

@ -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, peer.Node())
entry = pm.serverPool.connect(peer, addr.IP, uint16(addr.Port))
} }
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
} }

@ -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)

@ -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,

@ -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 id == enode.PubkeyToIDV4(recPubkey) {
if bytes.Equal(pbytes, recPubkey) {
return nil return nil
} }
return errors.New("Wrong signature") return errors.New("wrong signature")
} }
type blockInfo struct { type blockInfo struct {

@ -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 node *enode.Node
port uint16
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, ip: req.node.IP(),
port: req.port, 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{ addr := &poolEntryAddress{ip: node.IP(), port: uint16(node.TCP())}
ip: ip,
port: port,
}
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 { var entry poolEntryEnc
ID discover.NodeID
IP net.IP
Port uint16
Fails uint
CStat, DStat, RStat, TStat poolStats
}
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

@ -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)
} }

@ -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

@ -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) { func (t TCPDialer) Dial(dest *enode.Node) (net.Conn, error) {
addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)} 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 dialing map[enode.ID]connFlag
lookupBuf []*discover.Node // current discovery lookup results lookupBuf []*enode.Node // current discovery lookup results
randomNodes []*discover.Node // filled from Table randomNodes []*enode.Node // filled from Table
static map[discover.NodeID]*dialTask static map[enode.ID]*dialTask
hist *dialHistory hist *dialHistory
start time.Time // time when the dialer was first used start time.Time // time when the dialer was first used
bootnodes []*discover.Node // default dials when there are no peers 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 Resolve(*enode.Node) *enode.Node
Lookup(target discover.NodeID) []*discover.Node LookupRandom() []*enode.Node
ReadRandomNodes([]*discover.Node) int 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), static: make(map[enode.ID]*dialTask),
dialing: make(map[discover.NodeID]connFlag), dialing: make(map[enode.ID]connFlag),
bootnodes: make([]*discover.Node, len(bootnodes)), bootnodes: make([]*enode.Node, len(bootnodes)),
randomNodes: make([]*discover.Node, maxdyn/2), 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) 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) 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 { func (s *dialstate) checkDial(n *enode.Node, peers map[enode.ID]*Peer) error {
_, dialing := s.dialing[n.ID] _, 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)) s.hist.add(t.dest.ID(), now.Add(dialHistoryExpiration))
delete(s.dialing, t.dest.ID) 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 t.results = srv.ntab.LookupRandom()
rand.Read(target[:])
t.results = srv.ntab.Lookup(target)
} }
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

@ -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 { pm := func(ps []*Peer) map[enode.ID]*Peer {
m := make(map[discover.NodeID]*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) Self() *enode.Node { return new(enode.Node) }
func (t fakeTable) Close() {} func (t fakeTable) Close() {}
func (t fakeTable) Lookup(discover.NodeID) []*discover.Node { return nil } func (t fakeTable) LookupRandom() []*enode.Node { return nil }
func (t fakeTable) Resolve(discover.NodeID) *discover.Node { return nil } func (t fakeTable) Resolve(*enode.Node) *enode.Node { return nil }
func (t fakeTable) ReadRandomNodes(buf []*discover.Node) int { return copy(buf, t) } 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: staticDialedConn, node: newNode(uintID(0), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(1)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {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: staticDialedConn, node: newNode(uintID(0), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(1)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
}, },
done: []task{ done: []task{
&discoverTask{results: []*discover.Node{ &discoverTask{results: []*enode.Node{
{ID: uintID(2)}, // this one is already connected and not dialed. newNode(uintID(2), nil), // this one is already connected and not dialed.
{ID: uintID(3)}, newNode(uintID(3), nil),
{ID: uintID(4)}, newNode(uintID(4), nil),
{ID: uintID(5)}, newNode(uintID(5), nil),
{ID: uintID(6)}, // these are not tried because max dyn dials is 5 newNode(uintID(6), nil), // these are not tried because max dyn dials is 5
{ID: uintID(7)}, // ... newNode(uintID(7), nil), // ...
}}, }},
}, },
new: []task{ new: []task{
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, &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: staticDialedConn, node: newNode(uintID(0), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(1)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(3)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(3), nil)}},
{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(3)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, &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: staticDialedConn, node: newNode(uintID(0), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(1)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(3)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(3), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(4)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(5)}}, {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: staticDialedConn, node: newNode(uintID(0), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(1)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(3)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(3), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(4)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(5)}}, {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: staticDialedConn, node: newNode(uintID(0), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(1)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(5)}}, {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: staticDialedConn, node: newNode(uintID(0), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(1)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(5)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(7)}}, {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: staticDialedConn, node: newNode(uintID(0), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(1)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(5)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(7)}}, {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{ bootnodes := []*enode.Node{
{ID: uintID(1)}, newNode(uintID(1), nil),
{ID: uintID(2)}, newNode(uintID(2), nil),
{ID: uintID(3)}, newNode(uintID(3), nil),
} }
table := fakeTable{ table := fakeTable{
{ID: uintID(4)}, newNode(uintID(4), nil),
{ID: uintID(5)}, newNode(uintID(5), nil),
{ID: uintID(6)}, newNode(uintID(6), nil),
{ID: uintID(7)}, newNode(uintID(7), nil),
{ID: uintID(8)}, 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: newNode(uintID(4), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, &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: newNode(uintID(4), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, &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: newNode(uintID(1), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, &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: newNode(uintID(1), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, &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: newNode(uintID(1), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, &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: newNode(uintID(1), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, &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)}, newNode(uintID(1), nil),
{ID: uintID(2)}, newNode(uintID(2), nil),
{ID: uintID(3)}, newNode(uintID(3), nil),
{ID: uintID(4)}, newNode(uintID(4), nil),
{ID: uintID(5)}, newNode(uintID(5), nil),
{ID: uintID(6)}, newNode(uintID(6), nil),
{ID: uintID(7)}, newNode(uintID(7), nil),
{ID: uintID(8)}, 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: newNode(uintID(1), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(2), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, &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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {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: newNode(uintID(1), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(2), nil)},
&discoverTask{results: []*discover.Node{ &discoverTask{results: []*enode.Node{
{ID: uintID(10)}, newNode(uintID(10), nil),
{ID: uintID(11)}, newNode(uintID(11), nil),
{ID: uintID(12)}, newNode(uintID(12), nil),
}}, }},
}, },
new: []task{ new: []task{
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(10), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(11), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}}, &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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(10)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(10), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(11)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(11), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(12)}}, {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: newNode(uintID(3), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(10), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}}, &dialTask{flags: dynDialedConn, dest: newNode(uintID(11), nil)},
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}}, &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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(10)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(10), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(11)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(11), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(12)}}, {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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(10)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(10), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(11)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(11), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(12)}}, {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")}, newNode(uintID(1), net.ParseIP("127.0.0.1")),
{ID: uintID(2), IP: net.ParseIP("127.0.0.2")}, newNode(uintID(2), net.ParseIP("127.0.0.2")),
{ID: uintID(3), IP: net.ParseIP("127.0.0.3")}, newNode(uintID(3), net.ParseIP("127.0.0.3")),
{ID: uintID(4), IP: net.ParseIP("127.0.0.4")}, newNode(uintID(4), net.ParseIP("127.0.0.4")),
{ID: uintID(5), IP: net.ParseIP("127.0.2.5")}, newNode(uintID(5), net.ParseIP("127.0.2.5")),
{ID: uintID(6), IP: net.ParseIP("127.0.2.6")}, newNode(uintID(6), net.ParseIP("127.0.2.6")),
{ID: uintID(7), IP: net.ParseIP("127.0.2.7")}, newNode(uintID(7), net.ParseIP("127.0.2.7")),
{ID: uintID(8), IP: net.ParseIP("127.0.2.8")}, 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{ wantStatic := []*enode.Node{
{ID: uintID(1)}, newNode(uintID(1), nil),
{ID: uintID(2)}, newNode(uintID(2), nil),
{ID: uintID(3)}, newNode(uintID(3), nil),
{ID: uintID(4)}, newNode(uintID(4), nil),
{ID: uintID(5)}, 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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {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)},
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}}, &dialTask{flags: staticDialedConn, dest: newNode(uintID(4), nil)},
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}}, &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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
{rw: &conn{flags: staticDialedConn, id: uintID(3)}}, {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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
{rw: &conn{flags: staticDialedConn, id: uintID(3)}}, {rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
{rw: &conn{flags: staticDialedConn, id: uintID(4)}}, {rw: &conn{flags: staticDialedConn, node: newNode(uintID(4), nil)}},
{rw: &conn{flags: staticDialedConn, id: uintID(5)}}, {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: newNode(uintID(4), nil)},
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}}, &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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
{rw: &conn{flags: staticDialedConn, id: uintID(3)}}, {rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
{rw: &conn{flags: staticDialedConn, id: uintID(4)}}, {rw: &conn{flags: staticDialedConn, node: newNode(uintID(4), nil)}},
{rw: &conn{flags: staticDialedConn, id: uintID(5)}}, {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: dynDialedConn, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: staticDialedConn, id: uintID(3)}}, {rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
{rw: &conn{flags: staticDialedConn, id: uintID(5)}}, {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: newNode(uintID(2), nil)},
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}}, &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{ wantStatic := []*enode.Node{
{ID: uintID(1)}, newNode(uintID(1), nil),
{ID: uintID(2)}, 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: newNode(uintID(1), nil)},
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}}, &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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: staticDialedConn, id: uintID(2)}}, {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: newNode(uintID(1), nil)},
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}}, &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{ wantStatic := []*enode.Node{
{ID: uintID(1)}, newNode(uintID(1), nil),
{ID: uintID(2)}, newNode(uintID(2), nil),
{ID: uintID(3)}, 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: newNode(uintID(1), nil)},
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}}, &dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}}, &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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: staticDialedConn, id: uintID(2)}}, {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: newNode(uintID(1), nil)},
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}}, &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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {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, node: newNode(uintID(1), nil)}},
{rw: &conn{flags: dynDialedConn, id: uintID(2)}}, {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 { func uintID(i uint32) enode.ID {
var id discover.NodeID 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 resolveCalls []*enode.Node
answer *discover.Node answer *enode.Node
} }
func (t *resolveMock) Resolve(id discover.NodeID) *discover.Node { func (t *resolveMock) Resolve(n *enode.Node) *enode.Node {
t.resolveCalls = append(t.resolveCalls, id) 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) Self() *enode.Node { return new(enode.Node) }
func (t *resolveMock) Close() {} func (t *resolveMock) Close() {}
func (t *resolveMock) Bootstrap([]*discover.Node) {} func (t *resolveMock) LookupRandom() []*enode.Node { return nil }
func (t *resolveMock) Lookup(discover.NodeID) []*discover.Node { return nil } func (t *resolveMock) ReadRandomNodes(buf []*enode.Node) int { return 0 }
func (t *resolveMock) ReadRandomNodes(buf []*discover.Node) int { return 0 }

@ -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 { type node struct {
IP net.IP // len 4 for IPv4 or 16 for IPv6 enode.Node
UDP, TCP uint16 // port numbers addedAt time.Time // time when the node was added to the table
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)
} }
func parseComplete(rawurl string) (*Node, error) { type encPubkey [64]byte
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
}
// MarshalText implements encoding.TextMarshaler. func encodePubkey(key *ecdsa.PublicKey) encPubkey {
func (n *Node) MarshalText() ([]byte, error) { var e encPubkey
return []byte(n.String()), nil math.ReadBits(key.X, e[:len(e)/2])
math.ReadBits(key.Y, e[len(e)/2:])
return e
} }
// UnmarshalText implements encoding.TextUnmarshaler. func decodePubkey(e encPubkey) (*ecdsa.PublicKey, error) {
func (n *Node) UnmarshalText(text []byte) error { p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)}
dec, err := ParseNode(string(text)) half := len(e) / 2
if err == nil { p.X.SetBytes(e[:half])
*n = *dec p.Y.SetBytes(e[half:])
} if !p.Curve.IsOnCurve(p.X, p.Y) {
return err return nil, errors.New("invalid secp256k1 curve point")
}
// 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)
} }
return id return p, nil
} }
// HexID converts a hex string to a NodeID. func (e encPubkey) id() enode.ID {
// The string may be prefixed with 0x. return enode.ID(crypto.Keccak256Hash(e[:]))
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
} }
// MustHexID converts a hex string to a NodeID. // recoverNodeKey computes the public key used to sign the
// It panics if the string is not a valid NodeID. // given hash from the signature.
func MustHexID(in string) NodeID { func recoverNodeKey(hash, sig []byte) (key encPubkey, err error) {
id, err := HexID(in) 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 wrapNode(n *enode.Node) *node {
func PubkeyID(pub *ecdsa.PublicKey) NodeID { return &node{Node: *n}
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
} }
// Pubkey returns the public key represented by the node ID. func wrapNodes(ns []*enode.Node) []*node {
// It returns an error if the ID is not a point on the curve. result := make([]*node, len(ns))
func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) { for i, n := range ns {
p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)} result[i] = wrapNode(n)
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")
} }
return p, nil return result
} }
// recoverNodeID computes the public key used to sign the func unwrapNode(n *node) *enode.Node {
// given hash from the signature. return &n.Node
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
} }
// distcmp compares the distances a->target and b->target. func unwrapNodes(ns []*node) []*enode.Node {
// Returns -1 if a is closer to target, 1 if b is closer to target result := make([]*enode.Node, len(ns))
// and 0 if they are equal. for i, n := range ns {
func distcmp(target, a, b common.Hash) int { result[i] = unwrapNode(n)
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 return result
} }
// table of leading zero counts for bytes [0..255] func (n *node) addr() *net.UDPAddr {
var lzcount = [256]int{ return &net.UDPAddr{IP: n.IP(), Port: n.UDP()}
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,
} }
// logdist returns the logarithmic distance between a and b, log2(a ^ b). func (n *node) String() string {
func logdist(a, b common.Hash) int { return n.Node.String()
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
} }

@ -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 ping(enode.ID, *net.UDPAddr) error
findnode(toid NodeID, addr *net.UDPAddr, target NodeID) ([]*Node, 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 entries []*node // live entries, sorted by time of last contact
replacements []*Node // recently seen nodes to be used if revalidation fails 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) { func newTable(t transport, self *enode.Node, db *enode.DB, bootnodes []*enode.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
}
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() *enode.Node {
func (tab *Table) Self() *Node { return unwrapNode(tab.self)
return tab.self
} }
// ReadRandomNodes fills the given slice with random nodes from the // ReadRandomNodes fills the given slice with random nodes from the table. The results
// table. It will not write the same node more than once. The nodes in // are guaranteed to be unique for a single invocation, no node will appear twice.
// the slice are copies and can be modified by the caller. func (tab *Table) ReadRandomNodes(buf []*enode.Node) (n int) {
func (tab *Table) ReadRandomNodes(buf []*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 { if err := n.ValidateComplete(); err != nil {
return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err) return fmt.Errorf("bad bootstrap node %q: %v", n, err)
} }
} }
tab.nursery = make([]*Node, 0, len(nodes)) tab.nursery = wrapNodes(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)
}
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 { if len(cl.entries) > 0 && cl.entries[0].ID() == hash {
return cl.entries[0] 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 { if n.ID() == hash {
return n return unwrapNode(n)
} }
} }
return nil return nil
} }
// Lookup performs a network search for nodes close // LookupRandom finds random nodes in the network.
// to the given target. It approaches the target by querying func (tab *Table) LookupRandom() []*enode.Node {
// nodes that are closer to it on each iteration. var target encPubkey
// The given target does not need to be an actual node crand.Read(target[:])
// identifier. return unwrapNodes(tab.lookup(target, true))
func (tab *Table) Lookup(targetID NodeID) []*Node {
return tab.lookup(targetID, 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[:]) target = enode.ID(crypto.Keccak256Hash(targetKey[:]))
asked = make(map[NodeID]bool) asked = make(map[enode.ID]bool)
seen = make(map[NodeID]bool) seen = make(map[enode.ID]bool)
reply = make(chan []*Node, alpha) 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] { if !asked[n.ID()] {
asked[n.ID] = true 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] { if n != nil && !seen[n.ID()] {
seen[n.ID] = true 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) { func (tab *Table) findnode(n *node, targetKey encPubkey, reply chan<- []*node) {
fails := tab.db.findFails(n.ID) fails := tab.db.FindFails(n.ID())
r, err := tab.net.findnode(n.ID, n.addr(), targetID) 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) tab.db.UpdateFindFails(n.ID(), fails)
log.Trace("Findnode failed", "id", n.ID, "failcount", fails, "err", err) 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)) }} 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) 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 { func (tab *Table) bucket(id enode.ID) *bucket {
d := logdist(tab.self.sha, sha) 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.ID())
b := tab.bucket(n.sha)
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 { func (tab *Table) replace(b *bucket, last *node) *node {
if len(b.entries) == 0 || b.entries[len(b.entries)-1].ID != last.ID { 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 entries []*node
target common.Hash 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)

@ -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[last.ID()] = !lastInBucketIsResponding
transport.dead[pingSender.ID] = !newNodeIsResponding 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 { if l := len(tab.bucket(pingSender.ID()).entries); l != wantSize {
t.Errorf("wrong bucket size after add: got %d, want %d", 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) { prop := func(nodes []*node, bumps []int) (ok bool) {
b := &bucket{entries: make([]*Node, len(nodes))} 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 := nodeAtDistance(tab.self.ID(), i, net.IP{172, 0, 1, byte(i)})
n.IP = 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 := nodeAtDistance(tab.self.ID(), d, net.IP{172, 0, 1, byte(i)})
n.IP = 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 farthestResult := result[len(result)-1].ID()
if distcmp(test.Target, n.sha, farthestResult) < 0 { 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 Self enode.ID
Target common.Hash Target enode.ID
All []*Node 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), Self: gen(enode.ID{}, rand).(enode.ID),
Target: gen(common.Hash{}, rand).(common.Hash), Target: gen(enode.ID{}, rand).(enode.ID),
N: rand.Intn(bucketSize), N: rand.Intn(bucketSize),
} }
for _, id := range gen([]NodeID{}, rand).([]NodeID) { for _, id := range gen([]enode.ID{}, rand).([]enode.ID) {
t.All = append(t.All, &Node{ID: 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, db := newTestTable(lookupTestnet)
tab, _ := newTable(lookupTestnet, self.ID, &net.UDPAddr{}, "", nil)
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) seedKey, _ := decodePubkey(lookupTestnet.dists[256][0])
tab.stuff([]*Node{seed}) 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"), target: hexEncPubkey("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}, targetSha: enode.HexID("5c944ee51c5ae9f72a95eccb8aed0374eecb5119d720cbea6813e8e0d6ad9261"),
dists: [257][]NodeID{ dists: [257][]encPubkey{
240: { 240: {
MustHexID("2001ad5e3e80c71b952161bc0186731cf5ffe942d24a79230a0555802296238e57ea7a32f5b6f18564eadc1c65389448481f8c9338df0a3dbd18f708cbc2cbcb"), hexEncPubkey("2001ad5e3e80c71b952161bc0186731cf5ffe942d24a79230a0555802296238e57ea7a32f5b6f18564eadc1c65389448481f8c9338df0a3dbd18f708cbc2cbcb"),
MustHexID("6ba3f4f57d084b6bf94cc4555b8c657e4a8ac7b7baf23c6874efc21dd1e4f56b7eb2721e07f5242d2f1d8381fc8cae535e860197c69236798ba1ad231b105794"), hexEncPubkey("6ba3f4f57d084b6bf94cc4555b8c657e4a8ac7b7baf23c6874efc21dd1e4f56b7eb2721e07f5242d2f1d8381fc8cae535e860197c69236798ba1ad231b105794"),
}, },
244: { 244: {
MustHexID("696ba1f0a9d55c59246f776600542a9e6432490f0cd78f8bb55a196918df2081a9b521c3c3ba48e465a75c10768807717f8f689b0b4adce00e1c75737552a178"), hexEncPubkey("696ba1f0a9d55c59246f776600542a9e6432490f0cd78f8bb55a196918df2081a9b521c3c3ba48e465a75c10768807717f8f689b0b4adce00e1c75737552a178"),
}, },
246: { 246: {
MustHexID("d6d32178bdc38416f46ffb8b3ec9e4cb2cfff8d04dd7e4311a70e403cb62b10be1b447311b60b4f9ee221a8131fc2cbd45b96dd80deba68a949d467241facfa8"), hexEncPubkey("d6d32178bdc38416f46ffb8b3ec9e4cb2cfff8d04dd7e4311a70e403cb62b10be1b447311b60b4f9ee221a8131fc2cbd45b96dd80deba68a949d467241facfa8"),
MustHexID("3ea3d04a43a3dfb5ac11cffc2319248cf41b6279659393c2f55b8a0a5fc9d12581a9d97ef5d8ff9b5abf3321a290e8f63a4f785f450dc8a672aba3ba2ff4fdab"), hexEncPubkey("3ea3d04a43a3dfb5ac11cffc2319248cf41b6279659393c2f55b8a0a5fc9d12581a9d97ef5d8ff9b5abf3321a290e8f63a4f785f450dc8a672aba3ba2ff4fdab"),
MustHexID("2fc897f05ae585553e5c014effd3078f84f37f9333afacffb109f00ca8e7a3373de810a3946be971cbccdfd40249f9fe7f322118ea459ac71acca85a1ef8b7f4"), hexEncPubkey("2fc897f05ae585553e5c014effd3078f84f37f9333afacffb109f00ca8e7a3373de810a3946be971cbccdfd40249f9fe7f322118ea459ac71acca85a1ef8b7f4"),
}, },
247: { 247: {
MustHexID("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"), hexEncPubkey("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"),
MustHexID("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"), hexEncPubkey("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"),
MustHexID("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"), hexEncPubkey("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"),
MustHexID("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"), hexEncPubkey("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"),
MustHexID("8b58c6073dd98bbad4e310b97186c8f822d3a5c7d57af40e2136e88e315afd115edb27d2d0685a908cfe5aa49d0debdda6e6e63972691d6bd8c5af2d771dd2a9"), hexEncPubkey("8b58c6073dd98bbad4e310b97186c8f822d3a5c7d57af40e2136e88e315afd115edb27d2d0685a908cfe5aa49d0debdda6e6e63972691d6bd8c5af2d771dd2a9"),
MustHexID("2cbb718b7dc682da19652e7d9eb4fefaf7b7147d82c1c2b6805edf77b85e29fde9f6da195741467ff2638dc62c8d3e014ea5686693c15ed0080b6de90354c137"), hexEncPubkey("2cbb718b7dc682da19652e7d9eb4fefaf7b7147d82c1c2b6805edf77b85e29fde9f6da195741467ff2638dc62c8d3e014ea5686693c15ed0080b6de90354c137"),
MustHexID("e84027696d3f12f2de30a9311afea8fbd313c2360daff52bb5fc8c7094d5295758bec3134e4eef24e4cdf377b40da344993284628a7a346eba94f74160998feb"), hexEncPubkey("e84027696d3f12f2de30a9311afea8fbd313c2360daff52bb5fc8c7094d5295758bec3134e4eef24e4cdf377b40da344993284628a7a346eba94f74160998feb"),
MustHexID("f1357a4f04f9d33753a57c0b65ba20a5d8777abbffd04e906014491c9103fb08590e45548d37aa4bd70965e2e81ddba94f31860348df01469eec8c1829200a68"), hexEncPubkey("f1357a4f04f9d33753a57c0b65ba20a5d8777abbffd04e906014491c9103fb08590e45548d37aa4bd70965e2e81ddba94f31860348df01469eec8c1829200a68"),
MustHexID("4ab0a75941b12892369b4490a1928c8ca52a9ad6d3dffbd1d8c0b907bc200fe74c022d011ec39b64808a39c0ca41f1d3254386c3e7733e7044c44259486461b6"), hexEncPubkey("4ab0a75941b12892369b4490a1928c8ca52a9ad6d3dffbd1d8c0b907bc200fe74c022d011ec39b64808a39c0ca41f1d3254386c3e7733e7044c44259486461b6"),
MustHexID("d45150a72dc74388773e68e03133a3b5f51447fe91837d566706b3c035ee4b56f160c878c6273394daee7f56cc398985269052f22f75a8057df2fe6172765354"), hexEncPubkey("d45150a72dc74388773e68e03133a3b5f51447fe91837d566706b3c035ee4b56f160c878c6273394daee7f56cc398985269052f22f75a8057df2fe6172765354"),
}, },
248: { 248: {
MustHexID("6aadfce366a189bab08ac84721567483202c86590642ea6d6a14f37ca78d82bdb6509eb7b8b2f6f63c78ae3ae1d8837c89509e41497d719b23ad53dd81574afa"), hexEncPubkey("6aadfce366a189bab08ac84721567483202c86590642ea6d6a14f37ca78d82bdb6509eb7b8b2f6f63c78ae3ae1d8837c89509e41497d719b23ad53dd81574afa"),
MustHexID("a605ecfd6069a4cf4cf7f5840e5bc0ce10d23a3ac59e2aaa70c6afd5637359d2519b4524f56fc2ca180cdbebe54262f720ccaae8c1b28fd553c485675831624d"), hexEncPubkey("a605ecfd6069a4cf4cf7f5840e5bc0ce10d23a3ac59e2aaa70c6afd5637359d2519b4524f56fc2ca180cdbebe54262f720ccaae8c1b28fd553c485675831624d"),
MustHexID("29701451cb9448ca33fc33680b44b840d815be90146eb521641efbffed0859c154e8892d3906eae9934bfacee72cd1d2fa9dd050fd18888eea49da155ab0efd2"), hexEncPubkey("29701451cb9448ca33fc33680b44b840d815be90146eb521641efbffed0859c154e8892d3906eae9934bfacee72cd1d2fa9dd050fd18888eea49da155ab0efd2"),
MustHexID("3ed426322dee7572b08592e1e079f8b6c6b30e10e6243edd144a6a48fdbdb83df73a6e41b1143722cb82604f2203a32758610b5d9544f44a1a7921ba001528c1"), hexEncPubkey("3ed426322dee7572b08592e1e079f8b6c6b30e10e6243edd144a6a48fdbdb83df73a6e41b1143722cb82604f2203a32758610b5d9544f44a1a7921ba001528c1"),
MustHexID("b2e2a2b7fdd363572a3256e75435fab1da3b16f7891a8bd2015f30995dae665d7eabfd194d87d99d5df628b4bbc7b04e5b492c596422dd8272746c7a1b0b8e4f"), hexEncPubkey("b2e2a2b7fdd363572a3256e75435fab1da3b16f7891a8bd2015f30995dae665d7eabfd194d87d99d5df628b4bbc7b04e5b492c596422dd8272746c7a1b0b8e4f"),
MustHexID("0c69c9756162c593e85615b814ce57a2a8ca2df6c690b9c4e4602731b61e1531a3bbe3f7114271554427ffabea80ad8f36fa95a49fa77b675ae182c6ccac1728"), hexEncPubkey("0c69c9756162c593e85615b814ce57a2a8ca2df6c690b9c4e4602731b61e1531a3bbe3f7114271554427ffabea80ad8f36fa95a49fa77b675ae182c6ccac1728"),
MustHexID("8d28be21d5a97b0876442fa4f5e5387f5bf3faad0b6f13b8607b64d6e448c0991ca28dd7fe2f64eb8eadd7150bff5d5666aa6ed868b84c71311f4ba9a38569dd"), hexEncPubkey("8d28be21d5a97b0876442fa4f5e5387f5bf3faad0b6f13b8607b64d6e448c0991ca28dd7fe2f64eb8eadd7150bff5d5666aa6ed868b84c71311f4ba9a38569dd"),
MustHexID("2c677e1c64b9c9df6359348a7f5f33dc79e22f0177042486d125f8b6ca7f0dc756b1f672aceee5f1746bcff80aaf6f92a8dc0c9fbeb259b3fa0da060de5ab7e8"), hexEncPubkey("2c677e1c64b9c9df6359348a7f5f33dc79e22f0177042486d125f8b6ca7f0dc756b1f672aceee5f1746bcff80aaf6f92a8dc0c9fbeb259b3fa0da060de5ab7e8"),
MustHexID("3994880f94a8678f0cd247a43f474a8af375d2a072128da1ad6cae84a244105ff85e94fc7d8496f639468de7ee998908a91c7e33ef7585fff92e984b210941a1"), hexEncPubkey("3994880f94a8678f0cd247a43f474a8af375d2a072128da1ad6cae84a244105ff85e94fc7d8496f639468de7ee998908a91c7e33ef7585fff92e984b210941a1"),
MustHexID("b45a9153c08d002a48090d15d61a7c7dad8c2af85d4ff5bd36ce23a9a11e0709bf8d56614c7b193bc028c16cbf7f20dfbcc751328b64a924995d47b41e452422"), hexEncPubkey("b45a9153c08d002a48090d15d61a7c7dad8c2af85d4ff5bd36ce23a9a11e0709bf8d56614c7b193bc028c16cbf7f20dfbcc751328b64a924995d47b41e452422"),
MustHexID("057ab3a9e53c7a84b0f3fc586117a525cdd18e313f52a67bf31798d48078e325abe5cfee3f6c2533230cb37d0549289d692a29dd400e899b8552d4b928f6f907"), hexEncPubkey("057ab3a9e53c7a84b0f3fc586117a525cdd18e313f52a67bf31798d48078e325abe5cfee3f6c2533230cb37d0549289d692a29dd400e899b8552d4b928f6f907"),
MustHexID("0ddf663d308791eb92e6bd88a2f8cb45e4f4f35bb16708a0e6ff7f1362aa6a73fedd0a1b1557fb3365e38e1b79d6918e2fae2788728b70c9ab6b51a3b94a4338"), hexEncPubkey("0ddf663d308791eb92e6bd88a2f8cb45e4f4f35bb16708a0e6ff7f1362aa6a73fedd0a1b1557fb3365e38e1b79d6918e2fae2788728b70c9ab6b51a3b94a4338"),
MustHexID("f637e07ff50cc1e3731735841c4798411059f2023abcf3885674f3e8032531b0edca50fd715df6feb489b6177c345374d64f4b07d257a7745de393a107b013a5"), hexEncPubkey("f637e07ff50cc1e3731735841c4798411059f2023abcf3885674f3e8032531b0edca50fd715df6feb489b6177c345374d64f4b07d257a7745de393a107b013a5"),
MustHexID("e24ec7c6eec094f63c7b3239f56d311ec5a3e45bc4e622a1095a65b95eea6fe13e29f3b6b7a2cbfe40906e3989f17ac834c3102dd0cadaaa26e16ee06d782b72"), hexEncPubkey("e24ec7c6eec094f63c7b3239f56d311ec5a3e45bc4e622a1095a65b95eea6fe13e29f3b6b7a2cbfe40906e3989f17ac834c3102dd0cadaaa26e16ee06d782b72"),
MustHexID("b76ea1a6fd6506ef6e3506a4f1f60ed6287fff8114af6141b2ff13e61242331b54082b023cfea5b3083354a4fb3f9eb8be01fb4a518f579e731a5d0707291a6b"), hexEncPubkey("b76ea1a6fd6506ef6e3506a4f1f60ed6287fff8114af6141b2ff13e61242331b54082b023cfea5b3083354a4fb3f9eb8be01fb4a518f579e731a5d0707291a6b"),
MustHexID("9b53a37950ca8890ee349b325032d7b672cab7eced178d3060137b24ef6b92a43977922d5bdfb4a3409a2d80128e02f795f9dae6d7d99973ad0e23a2afb8442f"), hexEncPubkey("9b53a37950ca8890ee349b325032d7b672cab7eced178d3060137b24ef6b92a43977922d5bdfb4a3409a2d80128e02f795f9dae6d7d99973ad0e23a2afb8442f"),
}, },
249: { 249: {
MustHexID("675ae65567c3c72c50c73bc0fd4f61f202ea5f93346ca57b551de3411ccc614fad61cb9035493af47615311b9d44ee7a161972ee4d77c28fe1ec029d01434e6a"), hexEncPubkey("675ae65567c3c72c50c73bc0fd4f61f202ea5f93346ca57b551de3411ccc614fad61cb9035493af47615311b9d44ee7a161972ee4d77c28fe1ec029d01434e6a"),
MustHexID("8eb81408389da88536ae5800392b16ef5109d7ea132c18e9a82928047ecdb502693f6e4a4cdd18b54296caf561db937185731456c456c98bfe7de0baf0eaa495"), hexEncPubkey("8eb81408389da88536ae5800392b16ef5109d7ea132c18e9a82928047ecdb502693f6e4a4cdd18b54296caf561db937185731456c456c98bfe7de0baf0eaa495"),
MustHexID("2adba8b1612a541771cb93a726a38a4b88e97b18eced2593eb7daf82f05a5321ca94a72cc780c306ff21e551a932fc2c6d791e4681907b5ceab7f084c3fa2944"), hexEncPubkey("2adba8b1612a541771cb93a726a38a4b88e97b18eced2593eb7daf82f05a5321ca94a72cc780c306ff21e551a932fc2c6d791e4681907b5ceab7f084c3fa2944"),
MustHexID("b1b4bfbda514d9b8f35b1c28961da5d5216fe50548f4066f69af3b7666a3b2e06eac646735e963e5c8f8138a2fb95af15b13b23ff00c6986eccc0efaa8ee6fb4"), hexEncPubkey("b1b4bfbda514d9b8f35b1c28961da5d5216fe50548f4066f69af3b7666a3b2e06eac646735e963e5c8f8138a2fb95af15b13b23ff00c6986eccc0efaa8ee6fb4"),
MustHexID("d2139281b289ad0e4d7b4243c4364f5c51aac8b60f4806135de06b12b5b369c9e43a6eb494eab860d115c15c6fbb8c5a1b0e382972e0e460af395b8385363de7"), hexEncPubkey("d2139281b289ad0e4d7b4243c4364f5c51aac8b60f4806135de06b12b5b369c9e43a6eb494eab860d115c15c6fbb8c5a1b0e382972e0e460af395b8385363de7"),
MustHexID("4a693df4b8fc5bdc7cec342c3ed2e228d7c5b4ab7321ddaa6cccbeb45b05a9f1d95766b4002e6d4791c2deacb8a667aadea6a700da28a3eea810a30395701bbc"), hexEncPubkey("4a693df4b8fc5bdc7cec342c3ed2e228d7c5b4ab7321ddaa6cccbeb45b05a9f1d95766b4002e6d4791c2deacb8a667aadea6a700da28a3eea810a30395701bbc"),
MustHexID("ab41611195ec3c62bb8cd762ee19fb182d194fd141f4a66780efbef4b07ce916246c022b841237a3a6b512a93431157edd221e854ed2a259b72e9c5351f44d0c"), hexEncPubkey("ab41611195ec3c62bb8cd762ee19fb182d194fd141f4a66780efbef4b07ce916246c022b841237a3a6b512a93431157edd221e854ed2a259b72e9c5351f44d0c"),
MustHexID("68e8e26099030d10c3c703ae7045c0a48061fb88058d853b3e67880014c449d4311014da99d617d3150a20f1a3da5e34bf0f14f1c51fe4dd9d58afd222823176"), hexEncPubkey("68e8e26099030d10c3c703ae7045c0a48061fb88058d853b3e67880014c449d4311014da99d617d3150a20f1a3da5e34bf0f14f1c51fe4dd9d58afd222823176"),
MustHexID("3fbcacf546fb129cd70fc48de3b593ba99d3c473798bc309292aca280320e0eacc04442c914cad5c4cf6950345ba79b0d51302df88285d4e83ee3fe41339eee7"), hexEncPubkey("3fbcacf546fb129cd70fc48de3b593ba99d3c473798bc309292aca280320e0eacc04442c914cad5c4cf6950345ba79b0d51302df88285d4e83ee3fe41339eee7"),
MustHexID("1d4a623659f7c8f80b6c3939596afdf42e78f892f682c768ad36eb7bfba402dbf97aea3a268f3badd8fe7636be216edf3d67ee1e08789ebbc7be625056bd7109"), hexEncPubkey("1d4a623659f7c8f80b6c3939596afdf42e78f892f682c768ad36eb7bfba402dbf97aea3a268f3badd8fe7636be216edf3d67ee1e08789ebbc7be625056bd7109"),
MustHexID("a283c474ab09da02bbc96b16317241d0627646fcc427d1fe790b76a7bf1989ced90f92101a973047ae9940c92720dffbac8eff21df8cae468a50f72f9e159417"), hexEncPubkey("a283c474ab09da02bbc96b16317241d0627646fcc427d1fe790b76a7bf1989ced90f92101a973047ae9940c92720dffbac8eff21df8cae468a50f72f9e159417"),
MustHexID("dbf7e5ad7f87c3dfecae65d87c3039e14ed0bdc56caf00ce81931073e2e16719d746295512ff7937a15c3b03603e7c41a4f9df94fcd37bb200dd8f332767e9cb"), hexEncPubkey("dbf7e5ad7f87c3dfecae65d87c3039e14ed0bdc56caf00ce81931073e2e16719d746295512ff7937a15c3b03603e7c41a4f9df94fcd37bb200dd8f332767e9cb"),
MustHexID("caaa070a26692f64fc77f30d7b5ae980d419b4393a0f442b1c821ef58c0862898b0d22f74a4f8c5d83069493e3ec0b92f17dc1fe6e4cd437c1ec25039e7ce839"), hexEncPubkey("caaa070a26692f64fc77f30d7b5ae980d419b4393a0f442b1c821ef58c0862898b0d22f74a4f8c5d83069493e3ec0b92f17dc1fe6e4cd437c1ec25039e7ce839"),
MustHexID("874cc8d1213beb65c4e0e1de38ef5d8165235893ac74ab5ea937c885eaab25c8d79dad0456e9fd3e9450626cac7e107b004478fb59842f067857f39a47cee695"), hexEncPubkey("874cc8d1213beb65c4e0e1de38ef5d8165235893ac74ab5ea937c885eaab25c8d79dad0456e9fd3e9450626cac7e107b004478fb59842f067857f39a47cee695"),
MustHexID("d94193f236105010972f5df1b7818b55846592a0445b9cdc4eaed811b8c4c0f7c27dc8cc9837a4774656d6b34682d6d329d42b6ebb55da1d475c2474dc3dfdf4"), hexEncPubkey("d94193f236105010972f5df1b7818b55846592a0445b9cdc4eaed811b8c4c0f7c27dc8cc9837a4774656d6b34682d6d329d42b6ebb55da1d475c2474dc3dfdf4"),
MustHexID("edd9af6aded4094e9785637c28fccbd3980cbe28e2eb9a411048a23c2ace4bd6b0b7088a7817997b49a3dd05fc6929ca6c7abbb69438dbdabe65e971d2a794b2"), hexEncPubkey("edd9af6aded4094e9785637c28fccbd3980cbe28e2eb9a411048a23c2ace4bd6b0b7088a7817997b49a3dd05fc6929ca6c7abbb69438dbdabe65e971d2a794b2"),
}, },
250: { 250: {
MustHexID("53a5bd1215d4ab709ae8fdc2ced50bba320bced78bd9c5dc92947fb402250c914891786db0978c898c058493f86fc68b1c5de8a5cb36336150ac7a88655b6c39"), hexEncPubkey("53a5bd1215d4ab709ae8fdc2ced50bba320bced78bd9c5dc92947fb402250c914891786db0978c898c058493f86fc68b1c5de8a5cb36336150ac7a88655b6c39"),
MustHexID("b7f79e3ab59f79262623c9ccefc8f01d682323aee56ffbe295437487e9d5acaf556a9c92e1f1c6a9601f2b9eb6b027ae1aeaebac71d61b9b78e88676efd3e1a3"), hexEncPubkey("b7f79e3ab59f79262623c9ccefc8f01d682323aee56ffbe295437487e9d5acaf556a9c92e1f1c6a9601f2b9eb6b027ae1aeaebac71d61b9b78e88676efd3e1a3"),
MustHexID("d374bf7e8d7ffff69cc00bebff38ef5bc1dcb0a8d51c1a3d70e61ac6b2e2d6617109254b0ac224354dfbf79009fe4239e09020c483cc60c071e00b9238684f30"), hexEncPubkey("d374bf7e8d7ffff69cc00bebff38ef5bc1dcb0a8d51c1a3d70e61ac6b2e2d6617109254b0ac224354dfbf79009fe4239e09020c483cc60c071e00b9238684f30"),
MustHexID("1e1eac1c9add703eb252eb991594f8f5a173255d526a855fab24ae57dc277e055bc3c7a7ae0b45d437c4f47a72d97eb7b126f2ba344ba6c0e14b2c6f27d4b1e6"), hexEncPubkey("1e1eac1c9add703eb252eb991594f8f5a173255d526a855fab24ae57dc277e055bc3c7a7ae0b45d437c4f47a72d97eb7b126f2ba344ba6c0e14b2c6f27d4b1e6"),
MustHexID("ae28953f63d4bc4e706712a59319c111f5ff8f312584f65d7436b4cd3d14b217b958f8486bad666b4481fe879019fb1f767cf15b3e3e2711efc33b56d460448a"), hexEncPubkey("ae28953f63d4bc4e706712a59319c111f5ff8f312584f65d7436b4cd3d14b217b958f8486bad666b4481fe879019fb1f767cf15b3e3e2711efc33b56d460448a"),
MustHexID("934bb1edf9c7a318b82306aca67feb3d6b434421fa275d694f0b4927afd8b1d3935b727fd4ff6e3d012e0c82f1824385174e8c6450ade59c2a43281a4b3446b6"), hexEncPubkey("934bb1edf9c7a318b82306aca67feb3d6b434421fa275d694f0b4927afd8b1d3935b727fd4ff6e3d012e0c82f1824385174e8c6450ade59c2a43281a4b3446b6"),
MustHexID("9eef3f28f70ce19637519a0916555bf76d26de31312ac656cf9d3e379899ea44e4dd7ffcce923b4f3563f8a00489a34bd6936db0cbb4c959d32c49f017e07d05"), hexEncPubkey("9eef3f28f70ce19637519a0916555bf76d26de31312ac656cf9d3e379899ea44e4dd7ffcce923b4f3563f8a00489a34bd6936db0cbb4c959d32c49f017e07d05"),
MustHexID("82200872e8f871c48f1fad13daec6478298099b591bb3dbc4ef6890aa28ebee5860d07d70be62f4c0af85085a90ae8179ee8f937cf37915c67ea73e704b03ee7"), hexEncPubkey("82200872e8f871c48f1fad13daec6478298099b591bb3dbc4ef6890aa28ebee5860d07d70be62f4c0af85085a90ae8179ee8f937cf37915c67ea73e704b03ee7"),
MustHexID("6c75a5834a08476b7fc37ff3dc2011dc3ea3b36524bad7a6d319b18878fad813c0ba76d1f4555cacd3890c865438c21f0e0aed1f80e0a157e642124c69f43a11"), hexEncPubkey("6c75a5834a08476b7fc37ff3dc2011dc3ea3b36524bad7a6d319b18878fad813c0ba76d1f4555cacd3890c865438c21f0e0aed1f80e0a157e642124c69f43a11"),
MustHexID("995b873742206cb02b736e73a88580c2aacb0bd4a3c97a647b647bcab3f5e03c0e0736520a8b3600da09edf4248991fb01091ec7ff3ec7cdc8a1beae011e7aae"), hexEncPubkey("995b873742206cb02b736e73a88580c2aacb0bd4a3c97a647b647bcab3f5e03c0e0736520a8b3600da09edf4248991fb01091ec7ff3ec7cdc8a1beae011e7aae"),
MustHexID("c773a056594b5cdef2e850d30891ff0e927c3b1b9c35cd8e8d53a1017001e237468e1ece3ae33d612ca3e6abb0a9169aa352e9dcda358e5af2ad982b577447db"), hexEncPubkey("c773a056594b5cdef2e850d30891ff0e927c3b1b9c35cd8e8d53a1017001e237468e1ece3ae33d612ca3e6abb0a9169aa352e9dcda358e5af2ad982b577447db"),
MustHexID("2b46a5f6923f475c6be99ec6d134437a6d11f6bb4b4ac6bcd94572fa1092639d1c08aeefcb51f0912f0a060f71d4f38ee4da70ecc16010b05dd4a674aab14c3a"), hexEncPubkey("2b46a5f6923f475c6be99ec6d134437a6d11f6bb4b4ac6bcd94572fa1092639d1c08aeefcb51f0912f0a060f71d4f38ee4da70ecc16010b05dd4a674aab14c3a"),
MustHexID("af6ab501366debbaa0d22e20e9688f32ef6b3b644440580fd78de4fe0e99e2a16eb5636bbae0d1c259df8ddda77b35b9a35cbc36137473e9c68fbc9d203ba842"), hexEncPubkey("af6ab501366debbaa0d22e20e9688f32ef6b3b644440580fd78de4fe0e99e2a16eb5636bbae0d1c259df8ddda77b35b9a35cbc36137473e9c68fbc9d203ba842"),
MustHexID("c9f6f2dd1a941926f03f770695bda289859e85fabaf94baaae20b93e5015dc014ba41150176a36a1884adb52f405194693e63b0c464a6891cc9cc1c80d450326"), hexEncPubkey("c9f6f2dd1a941926f03f770695bda289859e85fabaf94baaae20b93e5015dc014ba41150176a36a1884adb52f405194693e63b0c464a6891cc9cc1c80d450326"),
MustHexID("5b116f0751526868a909b61a30b0c5282c37df6925cc03ddea556ef0d0602a9595fd6c14d371f8ed7d45d89918a032dcd22be4342a8793d88fdbeb3ca3d75bd7"), hexEncPubkey("5b116f0751526868a909b61a30b0c5282c37df6925cc03ddea556ef0d0602a9595fd6c14d371f8ed7d45d89918a032dcd22be4342a8793d88fdbeb3ca3d75bd7"),
MustHexID("50f3222fb6b82481c7c813b2172e1daea43e2710a443b9c2a57a12bd160dd37e20f87aa968c82ad639af6972185609d47036c0d93b4b7269b74ebd7073221c10"), hexEncPubkey("50f3222fb6b82481c7c813b2172e1daea43e2710a443b9c2a57a12bd160dd37e20f87aa968c82ad639af6972185609d47036c0d93b4b7269b74ebd7073221c10"),
}, },
251: { 251: {
MustHexID("9b8f702a62d1bee67bedfeb102eca7f37fa1713e310f0d6651cc0c33ea7c5477575289ccd463e5a2574a00a676a1fdce05658ba447bb9d2827f0ba47b947e894"), hexEncPubkey("9b8f702a62d1bee67bedfeb102eca7f37fa1713e310f0d6651cc0c33ea7c5477575289ccd463e5a2574a00a676a1fdce05658ba447bb9d2827f0ba47b947e894"),
MustHexID("b97532eb83054ed054b4abdf413bb30c00e4205545c93521554dbe77faa3cfaa5bd31ef466a107b0b34a71ec97214c0c83919720142cddac93aa7a3e928d4708"), hexEncPubkey("b97532eb83054ed054b4abdf413bb30c00e4205545c93521554dbe77faa3cfaa5bd31ef466a107b0b34a71ec97214c0c83919720142cddac93aa7a3e928d4708"),
MustHexID("2f7a5e952bfb67f2f90b8441b5fadc9ee13b1dcde3afeeb3dd64bf937f86663cc5c55d1fa83952b5422763c7df1b7f2794b751c6be316ebc0beb4942e65ab8c1"), hexEncPubkey("2f7a5e952bfb67f2f90b8441b5fadc9ee13b1dcde3afeeb3dd64bf937f86663cc5c55d1fa83952b5422763c7df1b7f2794b751c6be316ebc0beb4942e65ab8c1"),
MustHexID("42c7483781727051a0b3660f14faf39e0d33de5e643702ae933837d036508ab856ce7eec8ec89c4929a4901256e5233a3d847d5d4893f91bcf21835a9a880fee"), hexEncPubkey("42c7483781727051a0b3660f14faf39e0d33de5e643702ae933837d036508ab856ce7eec8ec89c4929a4901256e5233a3d847d5d4893f91bcf21835a9a880fee"),
MustHexID("873bae27bf1dc854408fba94046a53ab0c965cebe1e4e12290806fc62b88deb1f4a47f9e18f78fc0e7913a0c6e42ac4d0fc3a20cea6bc65f0c8a0ca90b67521e"), hexEncPubkey("873bae27bf1dc854408fba94046a53ab0c965cebe1e4e12290806fc62b88deb1f4a47f9e18f78fc0e7913a0c6e42ac4d0fc3a20cea6bc65f0c8a0ca90b67521e"),
MustHexID("a7e3a370bbd761d413f8d209e85886f68bf73d5c3089b2dc6fa42aab1ecb5162635497eed95dee2417f3c9c74a3e76319625c48ead2e963c7de877cd4551f347"), hexEncPubkey("a7e3a370bbd761d413f8d209e85886f68bf73d5c3089b2dc6fa42aab1ecb5162635497eed95dee2417f3c9c74a3e76319625c48ead2e963c7de877cd4551f347"),
MustHexID("528597534776a40df2addaaea15b6ff832ce36b9748a265768368f657e76d58569d9f30dbb91e91cf0ae7efe8f402f17aa0ae15f5c55051ba03ba830287f4c42"), hexEncPubkey("528597534776a40df2addaaea15b6ff832ce36b9748a265768368f657e76d58569d9f30dbb91e91cf0ae7efe8f402f17aa0ae15f5c55051ba03ba830287f4c42"),
MustHexID("461d8bd4f13c3c09031fdb84f104ed737a52f630261463ce0bdb5704259bab4b737dda688285b8444dbecaecad7f50f835190b38684ced5e90c54219e5adf1bc"), hexEncPubkey("461d8bd4f13c3c09031fdb84f104ed737a52f630261463ce0bdb5704259bab4b737dda688285b8444dbecaecad7f50f835190b38684ced5e90c54219e5adf1bc"),
MustHexID("6ec50c0be3fd232737090fc0111caaf0bb6b18f72be453428087a11a97fd6b52db0344acbf789a689bd4f5f50f79017ea784f8fd6fe723ad6ae675b9e3b13e21"), hexEncPubkey("6ec50c0be3fd232737090fc0111caaf0bb6b18f72be453428087a11a97fd6b52db0344acbf789a689bd4f5f50f79017ea784f8fd6fe723ad6ae675b9e3b13e21"),
MustHexID("12fc5e2f77a83fdcc727b79d8ae7fe6a516881138d3011847ee136b400fed7cfba1f53fd7a9730253c7aa4f39abeacd04f138417ba7fcb0f36cccc3514e0dab6"), hexEncPubkey("12fc5e2f77a83fdcc727b79d8ae7fe6a516881138d3011847ee136b400fed7cfba1f53fd7a9730253c7aa4f39abeacd04f138417ba7fcb0f36cccc3514e0dab6"),
MustHexID("4fdbe75914ccd0bce02101606a1ccf3657ec963e3b3c20239d5fec87673fe446d649b4f15f1fe1a40e6cfbd446dda2d31d40bb602b1093b8fcd5f139ba0eb46a"), hexEncPubkey("4fdbe75914ccd0bce02101606a1ccf3657ec963e3b3c20239d5fec87673fe446d649b4f15f1fe1a40e6cfbd446dda2d31d40bb602b1093b8fcd5f139ba0eb46a"),
MustHexID("3753668a0f6281e425ea69b52cb2d17ab97afbe6eb84cf5d25425bc5e53009388857640668fadd7c110721e6047c9697803bd8a6487b43bb343bfa32ebf24039"), hexEncPubkey("3753668a0f6281e425ea69b52cb2d17ab97afbe6eb84cf5d25425bc5e53009388857640668fadd7c110721e6047c9697803bd8a6487b43bb343bfa32ebf24039"),
MustHexID("2e81b16346637dec4410fd88e527346145b9c0a849dbf2628049ac7dae016c8f4305649d5659ec77f1e8a0fac0db457b6080547226f06283598e3740ad94849a"), hexEncPubkey("2e81b16346637dec4410fd88e527346145b9c0a849dbf2628049ac7dae016c8f4305649d5659ec77f1e8a0fac0db457b6080547226f06283598e3740ad94849a"),
MustHexID("802c3cc27f91c89213223d758f8d2ecd41135b357b6d698f24d811cdf113033a81c38e0bdff574a5c005b00a8c193dc2531f8c1fa05fa60acf0ab6f2858af09f"), hexEncPubkey("802c3cc27f91c89213223d758f8d2ecd41135b357b6d698f24d811cdf113033a81c38e0bdff574a5c005b00a8c193dc2531f8c1fa05fa60acf0ab6f2858af09f"),
MustHexID("fcc9a2e1ac3667026ff16192876d1813bb75abdbf39b929a92863012fe8b1d890badea7a0de36274d5c1eb1e8f975785532c50d80fd44b1a4b692f437303393f"), hexEncPubkey("fcc9a2e1ac3667026ff16192876d1813bb75abdbf39b929a92863012fe8b1d890badea7a0de36274d5c1eb1e8f975785532c50d80fd44b1a4b692f437303393f"),
MustHexID("6d8b3efb461151dd4f6de809b62726f5b89e9b38e9ba1391967f61cde844f7528fecf821b74049207cee5a527096b31f3ad623928cd3ce51d926fa345a6b2951"), hexEncPubkey("6d8b3efb461151dd4f6de809b62726f5b89e9b38e9ba1391967f61cde844f7528fecf821b74049207cee5a527096b31f3ad623928cd3ce51d926fa345a6b2951"),
}, },
252: { 252: {
MustHexID("f1ae93157cc48c2075dd5868fbf523e79e06caf4b8198f352f6e526680b78ff4227263de92612f7d63472bd09367bb92a636fff16fe46ccf41614f7a72495c2a"), hexEncPubkey("f1ae93157cc48c2075dd5868fbf523e79e06caf4b8198f352f6e526680b78ff4227263de92612f7d63472bd09367bb92a636fff16fe46ccf41614f7a72495c2a"),
MustHexID("587f482d111b239c27c0cb89b51dd5d574db8efd8de14a2e6a1400c54d4567e77c65f89c1da52841212080b91604104768350276b6682f2f961cdaf4039581c7"), hexEncPubkey("587f482d111b239c27c0cb89b51dd5d574db8efd8de14a2e6a1400c54d4567e77c65f89c1da52841212080b91604104768350276b6682f2f961cdaf4039581c7"),
MustHexID("e3f88274d35cefdaabdf205afe0e80e936cc982b8e3e47a84ce664c413b29016a4fb4f3a3ebae0a2f79671f8323661ed462bf4390af94c424dc8ace0c301b90f"), hexEncPubkey("e3f88274d35cefdaabdf205afe0e80e936cc982b8e3e47a84ce664c413b29016a4fb4f3a3ebae0a2f79671f8323661ed462bf4390af94c424dc8ace0c301b90f"),
MustHexID("0ddc736077da9a12ba410dc5ea63cbcbe7659dd08596485b2bff3435221f82c10d263efd9af938e128464be64a178b7cd22e19f400d5802f4c9df54bf89f2619"), hexEncPubkey("0ddc736077da9a12ba410dc5ea63cbcbe7659dd08596485b2bff3435221f82c10d263efd9af938e128464be64a178b7cd22e19f400d5802f4c9df54bf89f2619"),
MustHexID("784aa34d833c6ce63fcc1279630113c3272e82c4ae8c126c5a52a88ac461b6baeed4244e607b05dc14e5b2f41c70a273c3804dea237f14f7a1e546f6d1309d14"), hexEncPubkey("784aa34d833c6ce63fcc1279630113c3272e82c4ae8c126c5a52a88ac461b6baeed4244e607b05dc14e5b2f41c70a273c3804dea237f14f7a1e546f6d1309d14"),
MustHexID("f253a2c354ee0e27cfcae786d726753d4ad24be6516b279a936195a487de4a59dbc296accf20463749ff55293263ed8c1b6365eecb248d44e75e9741c0d18205"), hexEncPubkey("f253a2c354ee0e27cfcae786d726753d4ad24be6516b279a936195a487de4a59dbc296accf20463749ff55293263ed8c1b6365eecb248d44e75e9741c0d18205"),
MustHexID("a1910b80357b3ad9b4593e0628922939614dc9056a5fbf477279c8b2c1d0b4b31d89a0c09d0d41f795271d14d3360ef08a3f821e65e7e1f56c07a36afe49c7c5"), hexEncPubkey("a1910b80357b3ad9b4593e0628922939614dc9056a5fbf477279c8b2c1d0b4b31d89a0c09d0d41f795271d14d3360ef08a3f821e65e7e1f56c07a36afe49c7c5"),
MustHexID("f1168552c2efe541160f0909b0b4a9d6aeedcf595cdf0e9b165c97e3e197471a1ee6320e93389edfba28af6eaf10de98597ad56e7ab1b504ed762451996c3b98"), hexEncPubkey("f1168552c2efe541160f0909b0b4a9d6aeedcf595cdf0e9b165c97e3e197471a1ee6320e93389edfba28af6eaf10de98597ad56e7ab1b504ed762451996c3b98"),
MustHexID("b0c8e5d2c8634a7930e1a6fd082e448c6cf9d2d8b7293558b59238815a4df926c286bf297d2049f14e8296a6eb3256af614ec1812c4f2bbe807673b58bf14c8c"), hexEncPubkey("b0c8e5d2c8634a7930e1a6fd082e448c6cf9d2d8b7293558b59238815a4df926c286bf297d2049f14e8296a6eb3256af614ec1812c4f2bbe807673b58bf14c8c"),
MustHexID("0fb346076396a38badc342df3679b55bd7f40a609ab103411fe45082c01f12ea016729e95914b2b5540e987ff5c9b133e85862648e7f36abdfd23100d248d234"), hexEncPubkey("0fb346076396a38badc342df3679b55bd7f40a609ab103411fe45082c01f12ea016729e95914b2b5540e987ff5c9b133e85862648e7f36abdfd23100d248d234"),
MustHexID("f736e0cc83417feaa280d9483f5d4d72d1b036cd0c6d9cbdeb8ac35ceb2604780de46dddaa32a378474e1d5ccdf79b373331c30c7911ade2ae32f98832e5de1f"), hexEncPubkey("f736e0cc83417feaa280d9483f5d4d72d1b036cd0c6d9cbdeb8ac35ceb2604780de46dddaa32a378474e1d5ccdf79b373331c30c7911ade2ae32f98832e5de1f"),
MustHexID("8b02991457602f42b38b342d3f2259ae4100c354b3843885f7e4e07bd644f64dab94bb7f38a3915f8b7f11d8e3f81c28e07a0078cf79d7397e38a7b7e0c857e2"), hexEncPubkey("8b02991457602f42b38b342d3f2259ae4100c354b3843885f7e4e07bd644f64dab94bb7f38a3915f8b7f11d8e3f81c28e07a0078cf79d7397e38a7b7e0c857e2"),
MustHexID("9221d9f04a8a184993d12baa91116692bb685f887671302999d69300ad103eb2d2c75a09d8979404c6dd28f12362f58a1a43619c493d9108fd47588a23ce5824"), hexEncPubkey("9221d9f04a8a184993d12baa91116692bb685f887671302999d69300ad103eb2d2c75a09d8979404c6dd28f12362f58a1a43619c493d9108fd47588a23ce5824"),
MustHexID("652797801744dada833fff207d67484742eea6835d695925f3e618d71b68ec3c65bdd85b4302b2cdcb835ad3f94fd00d8da07e570b41bc0d2bcf69a8de1b3284"), hexEncPubkey("652797801744dada833fff207d67484742eea6835d695925f3e618d71b68ec3c65bdd85b4302b2cdcb835ad3f94fd00d8da07e570b41bc0d2bcf69a8de1b3284"),
MustHexID("d84f06fe64debc4cd0625e36d19b99014b6218375262cc2209202bdbafd7dffcc4e34ce6398e182e02fd8faeed622c3e175545864902dfd3d1ac57647cddf4c6"), hexEncPubkey("d84f06fe64debc4cd0625e36d19b99014b6218375262cc2209202bdbafd7dffcc4e34ce6398e182e02fd8faeed622c3e175545864902dfd3d1ac57647cddf4c6"),
MustHexID("d0ed87b294f38f1d741eb601020eeec30ac16331d05880fe27868f1e454446de367d7457b41c79e202eaf9525b029e4f1d7e17d85a55f83a557c005c68d7328a"), hexEncPubkey("d0ed87b294f38f1d741eb601020eeec30ac16331d05880fe27868f1e454446de367d7457b41c79e202eaf9525b029e4f1d7e17d85a55f83a557c005c68d7328a"),
}, },
253: { 253: {
MustHexID("ad4485e386e3cc7c7310366a7c38fb810b8896c0d52e55944bfd320ca294e7912d6c53c0a0cf85e7ce226e92491d60430e86f8f15cda0161ed71893fb4a9e3a1"), hexEncPubkey("ad4485e386e3cc7c7310366a7c38fb810b8896c0d52e55944bfd320ca294e7912d6c53c0a0cf85e7ce226e92491d60430e86f8f15cda0161ed71893fb4a9e3a1"),
MustHexID("36d0e7e5b7734f98c6183eeeb8ac5130a85e910a925311a19c4941b1290f945d4fc3996b12ef4966960b6fa0fb29b1604f83a0f81bd5fd6398d2e1a22e46af0c"), hexEncPubkey("36d0e7e5b7734f98c6183eeeb8ac5130a85e910a925311a19c4941b1290f945d4fc3996b12ef4966960b6fa0fb29b1604f83a0f81bd5fd6398d2e1a22e46af0c"),
MustHexID("7d307d8acb4a561afa23bdf0bd945d35c90245e26345ec3a1f9f7df354222a7cdcb81339c9ed6744526c27a1a0c8d10857e98df942fa433602facac71ac68a31"), hexEncPubkey("7d307d8acb4a561afa23bdf0bd945d35c90245e26345ec3a1f9f7df354222a7cdcb81339c9ed6744526c27a1a0c8d10857e98df942fa433602facac71ac68a31"),
MustHexID("d97bf55f88c83fae36232661af115d66ca600fc4bd6d1fb35ff9bb4dad674c02cf8c8d05f317525b5522250db58bb1ecafb7157392bf5aa61b178c61f098d995"), hexEncPubkey("d97bf55f88c83fae36232661af115d66ca600fc4bd6d1fb35ff9bb4dad674c02cf8c8d05f317525b5522250db58bb1ecafb7157392bf5aa61b178c61f098d995"),
MustHexID("7045d678f1f9eb7a4613764d17bd5698796494d0bf977b16f2dbc272b8a0f7858a60805c022fc3d1fe4f31c37e63cdaca0416c0d053ef48a815f8b19121605e0"), hexEncPubkey("7045d678f1f9eb7a4613764d17bd5698796494d0bf977b16f2dbc272b8a0f7858a60805c022fc3d1fe4f31c37e63cdaca0416c0d053ef48a815f8b19121605e0"),
MustHexID("14e1f21418d445748de2a95cd9a8c3b15b506f86a0acabd8af44bb968ce39885b19c8822af61b3dd58a34d1f265baec30e3ae56149dc7d2aa4a538f7319f69c8"), hexEncPubkey("14e1f21418d445748de2a95cd9a8c3b15b506f86a0acabd8af44bb968ce39885b19c8822af61b3dd58a34d1f265baec30e3ae56149dc7d2aa4a538f7319f69c8"),
MustHexID("b9453d78281b66a4eac95a1546017111eaaa5f92a65d0de10b1122940e92b319728a24edf4dec6acc412321b1c95266d39c7b3a5d265c629c3e49a65fb022c09"), hexEncPubkey("b9453d78281b66a4eac95a1546017111eaaa5f92a65d0de10b1122940e92b319728a24edf4dec6acc412321b1c95266d39c7b3a5d265c629c3e49a65fb022c09"),
MustHexID("e8a49248419e3824a00d86af422f22f7366e2d4922b304b7169937616a01d9d6fa5abf5cc01061a352dc866f48e1fa2240dbb453d872b1d7be62bdfc1d5e248c"), hexEncPubkey("e8a49248419e3824a00d86af422f22f7366e2d4922b304b7169937616a01d9d6fa5abf5cc01061a352dc866f48e1fa2240dbb453d872b1d7be62bdfc1d5e248c"),
MustHexID("bebcff24b52362f30e0589ee573ce2d86f073d58d18e6852a592fa86ceb1a6c9b96d7fb9ec7ed1ed98a51b6743039e780279f6bb49d0a04327ac7a182d9a56f6"), hexEncPubkey("bebcff24b52362f30e0589ee573ce2d86f073d58d18e6852a592fa86ceb1a6c9b96d7fb9ec7ed1ed98a51b6743039e780279f6bb49d0a04327ac7a182d9a56f6"),
MustHexID("d0835e5a4291db249b8d2fca9f503049988180c7d247bedaa2cf3a1bad0a76709360a85d4f9a1423b2cbc82bb4d94b47c0cde20afc430224834c49fe312a9ae3"), hexEncPubkey("d0835e5a4291db249b8d2fca9f503049988180c7d247bedaa2cf3a1bad0a76709360a85d4f9a1423b2cbc82bb4d94b47c0cde20afc430224834c49fe312a9ae3"),
MustHexID("6b087fe2a2da5e4f0b0f4777598a4a7fb66bf77dbd5bfc44e8a7eaa432ab585a6e226891f56a7d4f5ed11a7c57b90f1661bba1059590ca4267a35801c2802913"), hexEncPubkey("6b087fe2a2da5e4f0b0f4777598a4a7fb66bf77dbd5bfc44e8a7eaa432ab585a6e226891f56a7d4f5ed11a7c57b90f1661bba1059590ca4267a35801c2802913"),
MustHexID("d901e5bde52d1a0f4ddf010a686a53974cdae4ebe5c6551b3c37d6b6d635d38d5b0e5f80bc0186a2c7809dbf3a42870dd09643e68d32db896c6da8ba734579e7"), hexEncPubkey("d901e5bde52d1a0f4ddf010a686a53974cdae4ebe5c6551b3c37d6b6d635d38d5b0e5f80bc0186a2c7809dbf3a42870dd09643e68d32db896c6da8ba734579e7"),
MustHexID("96419fb80efae4b674402bb969ebaab86c1274f29a83a311e24516d36cdf148fe21754d46c97688cdd7468f24c08b13e4727c29263393638a3b37b99ff60ebca"), hexEncPubkey("96419fb80efae4b674402bb969ebaab86c1274f29a83a311e24516d36cdf148fe21754d46c97688cdd7468f24c08b13e4727c29263393638a3b37b99ff60ebca"),
MustHexID("7b9c1889ae916a5d5abcdfb0aaedcc9c6f9eb1c1a4f68d0c2d034fe79ac610ce917c3abc670744150fa891bfcd8ab14fed6983fca964de920aa393fa7b326748"), hexEncPubkey("7b9c1889ae916a5d5abcdfb0aaedcc9c6f9eb1c1a4f68d0c2d034fe79ac610ce917c3abc670744150fa891bfcd8ab14fed6983fca964de920aa393fa7b326748"),
MustHexID("7a369b2b8962cc4c65900be046482fbf7c14f98a135bbbae25152c82ad168fb2097b3d1429197cf46d3ce9fdeb64808f908a489cc6019725db040060fdfe5405"), hexEncPubkey("7a369b2b8962cc4c65900be046482fbf7c14f98a135bbbae25152c82ad168fb2097b3d1429197cf46d3ce9fdeb64808f908a489cc6019725db040060fdfe5405"),
MustHexID("47bcae48288da5ecc7f5058dfa07cf14d89d06d6e449cb946e237aa6652ea050d9f5a24a65efdc0013ccf232bf88670979eddef249b054f63f38da9d7796dbd8"), hexEncPubkey("47bcae48288da5ecc7f5058dfa07cf14d89d06d6e449cb946e237aa6652ea050d9f5a24a65efdc0013ccf232bf88670979eddef249b054f63f38da9d7796dbd8"),
}, },
254: { 254: {
MustHexID("099739d7abc8abd38ecc7a816c521a1168a4dbd359fa7212a5123ab583ffa1cf485a5fed219575d6475dbcdd541638b2d3631a6c7fce7474e7fe3cba1d4d5853"), hexEncPubkey("099739d7abc8abd38ecc7a816c521a1168a4dbd359fa7212a5123ab583ffa1cf485a5fed219575d6475dbcdd541638b2d3631a6c7fce7474e7fe3cba1d4d5853"),
MustHexID("c2b01603b088a7182d0cf7ef29fb2b04c70acb320fccf78526bf9472e10c74ee70b3fcfa6f4b11d167bd7d3bc4d936b660f2c9bff934793d97cb21750e7c3d31"), hexEncPubkey("c2b01603b088a7182d0cf7ef29fb2b04c70acb320fccf78526bf9472e10c74ee70b3fcfa6f4b11d167bd7d3bc4d936b660f2c9bff934793d97cb21750e7c3d31"),
MustHexID("20e4d8f45f2f863e94b45548c1ef22a11f7d36f263e4f8623761e05a64c4572379b000a52211751e2561b0f14f4fc92dd4130410c8ccc71eb4f0e95a700d4ca9"), hexEncPubkey("20e4d8f45f2f863e94b45548c1ef22a11f7d36f263e4f8623761e05a64c4572379b000a52211751e2561b0f14f4fc92dd4130410c8ccc71eb4f0e95a700d4ca9"),
MustHexID("27f4a16cc085e72d86e25c98bd2eca173eaaee7565c78ec5a52e9e12b2211f35de81b5b45e9195de2ebfe29106742c59112b951a04eb7ae48822911fc1f9389e"), hexEncPubkey("27f4a16cc085e72d86e25c98bd2eca173eaaee7565c78ec5a52e9e12b2211f35de81b5b45e9195de2ebfe29106742c59112b951a04eb7ae48822911fc1f9389e"),
MustHexID("55db5ee7d98e7f0b1c3b9d5be6f2bc619a1b86c3cdd513160ad4dcf267037a5fffad527ac15d50aeb32c59c13d1d4c1e567ebbf4de0d25236130c8361f9aac63"), hexEncPubkey("55db5ee7d98e7f0b1c3b9d5be6f2bc619a1b86c3cdd513160ad4dcf267037a5fffad527ac15d50aeb32c59c13d1d4c1e567ebbf4de0d25236130c8361f9aac63"),
MustHexID("883df308b0130fc928a8559fe50667a0fff80493bc09685d18213b2db241a3ad11310ed86b0ef662b3ce21fc3d9aa7f3fc24b8d9afe17c7407e9afd3345ae548"), hexEncPubkey("883df308b0130fc928a8559fe50667a0fff80493bc09685d18213b2db241a3ad11310ed86b0ef662b3ce21fc3d9aa7f3fc24b8d9afe17c7407e9afd3345ae548"),
MustHexID("c7af968cc9bc8200c3ee1a387405f7563be1dce6710a3439f42ea40657d0eae9d2b3c16c42d779605351fcdece4da637b9804e60ca08cfb89aec32c197beffa6"), hexEncPubkey("c7af968cc9bc8200c3ee1a387405f7563be1dce6710a3439f42ea40657d0eae9d2b3c16c42d779605351fcdece4da637b9804e60ca08cfb89aec32c197beffa6"),
MustHexID("3e66f2b788e3ff1d04106b80597915cd7afa06c405a7ae026556b6e583dca8e05cfbab5039bb9a1b5d06083ffe8de5780b1775550e7218f5e98624bf7af9a0a8"), hexEncPubkey("3e66f2b788e3ff1d04106b80597915cd7afa06c405a7ae026556b6e583dca8e05cfbab5039bb9a1b5d06083ffe8de5780b1775550e7218f5e98624bf7af9a0a8"),
MustHexID("4fc7f53764de3337fdaec0a711d35d3a923e72fa65025444d12230b3552ed43d9b2d1ad08ccb11f2d50c58809e6dd74dde910e195294fca3b47ae5a3967cc479"), hexEncPubkey("4fc7f53764de3337fdaec0a711d35d3a923e72fa65025444d12230b3552ed43d9b2d1ad08ccb11f2d50c58809e6dd74dde910e195294fca3b47ae5a3967cc479"),
MustHexID("bafdfdcf6ccaa989436752fa97c77477b6baa7deb374b16c095492c529eb133e8e2f99e1977012b64767b9d34b2cf6d2048ed489bd822b5139b523f6a423167b"), hexEncPubkey("bafdfdcf6ccaa989436752fa97c77477b6baa7deb374b16c095492c529eb133e8e2f99e1977012b64767b9d34b2cf6d2048ed489bd822b5139b523f6a423167b"),
MustHexID("7f5d78008a4312fe059104ce80202c82b8915c2eb4411c6b812b16f7642e57c00f2c9425121f5cbac4257fe0b3e81ef5dea97ea2dbaa98f6a8b6fd4d1e5980bb"), hexEncPubkey("7f5d78008a4312fe059104ce80202c82b8915c2eb4411c6b812b16f7642e57c00f2c9425121f5cbac4257fe0b3e81ef5dea97ea2dbaa98f6a8b6fd4d1e5980bb"),
MustHexID("598c37fe78f922751a052f463aeb0cb0bc7f52b7c2a4cf2da72ec0931c7c32175d4165d0f8998f7320e87324ac3311c03f9382a5385c55f0407b7a66b2acd864"), hexEncPubkey("598c37fe78f922751a052f463aeb0cb0bc7f52b7c2a4cf2da72ec0931c7c32175d4165d0f8998f7320e87324ac3311c03f9382a5385c55f0407b7a66b2acd864"),
MustHexID("f758c4136e1c148777a7f3275a76e2db0b2b04066fd738554ec398c1c6cc9fb47e14a3b4c87bd47deaeab3ffd2110514c3855685a374794daff87b605b27ee2e"), hexEncPubkey("f758c4136e1c148777a7f3275a76e2db0b2b04066fd738554ec398c1c6cc9fb47e14a3b4c87bd47deaeab3ffd2110514c3855685a374794daff87b605b27ee2e"),
MustHexID("0307bb9e4fd865a49dcf1fe4333d1b944547db650ab580af0b33e53c4fef6c789531110fac801bbcbce21fc4d6f61b6d5b24abdf5b22e3030646d579f6dca9c2"), hexEncPubkey("0307bb9e4fd865a49dcf1fe4333d1b944547db650ab580af0b33e53c4fef6c789531110fac801bbcbce21fc4d6f61b6d5b24abdf5b22e3030646d579f6dca9c2"),
MustHexID("82504b6eb49bb2c0f91a7006ce9cefdbaf6df38706198502c2e06601091fc9dc91e4f15db3410d45c6af355bc270b0f268d3dff560f956985c7332d4b10bd1ed"), hexEncPubkey("82504b6eb49bb2c0f91a7006ce9cefdbaf6df38706198502c2e06601091fc9dc91e4f15db3410d45c6af355bc270b0f268d3dff560f956985c7332d4b10bd1ed"),
MustHexID("b39b5b677b45944ceebe76e76d1f051de2f2a0ec7b0d650da52135743e66a9a5dba45f638258f9a7545d9a790c7fe6d3fdf82c25425c7887323e45d27d06c057"), hexEncPubkey("b39b5b677b45944ceebe76e76d1f051de2f2a0ec7b0d650da52135743e66a9a5dba45f638258f9a7545d9a790c7fe6d3fdf82c25425c7887323e45d27d06c057"),
}, },
255: { 255: {
MustHexID("5c4d58d46e055dd1f093f81ee60a675e1f02f54da6206720adee4dccef9b67a31efc5c2a2949c31a04ee31beadc79aba10da31440a1f9ff2a24093c63c36d784"), hexEncPubkey("5c4d58d46e055dd1f093f81ee60a675e1f02f54da6206720adee4dccef9b67a31efc5c2a2949c31a04ee31beadc79aba10da31440a1f9ff2a24093c63c36d784"),
MustHexID("ea72161ffdd4b1e124c7b93b0684805f4c4b58d617ed498b37a145c670dbc2e04976f8785583d9c805ffbf343c31d492d79f841652bbbd01b61ed85640b23495"), hexEncPubkey("ea72161ffdd4b1e124c7b93b0684805f4c4b58d617ed498b37a145c670dbc2e04976f8785583d9c805ffbf343c31d492d79f841652bbbd01b61ed85640b23495"),
MustHexID("51caa1d93352d47a8e531692a3612adac1e8ac68d0a200d086c1c57ae1e1a91aa285ab242e8c52ef9d7afe374c9485b122ae815f1707b875569d0433c1c3ce85"), hexEncPubkey("51caa1d93352d47a8e531692a3612adac1e8ac68d0a200d086c1c57ae1e1a91aa285ab242e8c52ef9d7afe374c9485b122ae815f1707b875569d0433c1c3ce85"),
MustHexID("c08397d5751b47bd3da044b908be0fb0e510d3149574dff7aeab33749b023bb171b5769990fe17469dbebc100bc150e798aeda426a2dcc766699a225fddd75c6"), hexEncPubkey("c08397d5751b47bd3da044b908be0fb0e510d3149574dff7aeab33749b023bb171b5769990fe17469dbebc100bc150e798aeda426a2dcc766699a225fddd75c6"),
MustHexID("0222c1c194b749736e593f937fad67ee348ac57287a15c7e42877aa38a9b87732a408bca370f812efd0eedbff13e6d5b854bf3ba1dec431a796ed47f32552b09"), hexEncPubkey("0222c1c194b749736e593f937fad67ee348ac57287a15c7e42877aa38a9b87732a408bca370f812efd0eedbff13e6d5b854bf3ba1dec431a796ed47f32552b09"),
MustHexID("03d859cd46ef02d9bfad5268461a6955426845eef4126de6be0fa4e8d7e0727ba2385b78f1a883a8239e95ebb814f2af8379632c7d5b100688eebc5841209582"), hexEncPubkey("03d859cd46ef02d9bfad5268461a6955426845eef4126de6be0fa4e8d7e0727ba2385b78f1a883a8239e95ebb814f2af8379632c7d5b100688eebc5841209582"),
MustHexID("64d5004b7e043c39ff0bd10cb20094c287721d5251715884c280a612b494b3e9e1c64ba6f67614994c7d969a0d0c0295d107d53fc225d47c44c4b82852d6f960"), hexEncPubkey("64d5004b7e043c39ff0bd10cb20094c287721d5251715884c280a612b494b3e9e1c64ba6f67614994c7d969a0d0c0295d107d53fc225d47c44c4b82852d6f960"),
MustHexID("b0a5eefb2dab6f786670f35bf9641eefe6dd87fd3f1362bcab4aaa792903500ab23d88fae68411372e0813b057535a601d46e454323745a948017f6063a47b1f"), hexEncPubkey("b0a5eefb2dab6f786670f35bf9641eefe6dd87fd3f1362bcab4aaa792903500ab23d88fae68411372e0813b057535a601d46e454323745a948017f6063a47b1f"),
MustHexID("0cc6df0a3433d448b5684d2a3ffa9d1a825388177a18f44ad0008c7bd7702f1ec0fc38b83506f7de689c3b6ecb552599927e29699eed6bb867ff08f80068b287"), hexEncPubkey("0cc6df0a3433d448b5684d2a3ffa9d1a825388177a18f44ad0008c7bd7702f1ec0fc38b83506f7de689c3b6ecb552599927e29699eed6bb867ff08f80068b287"),
MustHexID("50772f7b8c03a4e153355fbbf79c8a80cf32af656ff0c7873c99911099d04a0dae0674706c357e0145ad017a0ade65e6052cb1b0d574fcd6f67da3eee0ace66b"), hexEncPubkey("50772f7b8c03a4e153355fbbf79c8a80cf32af656ff0c7873c99911099d04a0dae0674706c357e0145ad017a0ade65e6052cb1b0d574fcd6f67da3eee0ace66b"),
MustHexID("1ae37829c9ef41f8b508b82259ebac76b1ed900d7a45c08b7970f25d2d48ddd1829e2f11423a18749940b6dab8598c6e416cef0efd47e46e51f29a0bc65b37cd"), hexEncPubkey("1ae37829c9ef41f8b508b82259ebac76b1ed900d7a45c08b7970f25d2d48ddd1829e2f11423a18749940b6dab8598c6e416cef0efd47e46e51f29a0bc65b37cd"),
MustHexID("ba973cab31c2af091fc1644a93527d62b2394999e2b6ccbf158dd5ab9796a43d408786f1803ef4e29debfeb62fce2b6caa5ab2b24d1549c822a11c40c2856665"), hexEncPubkey("ba973cab31c2af091fc1644a93527d62b2394999e2b6ccbf158dd5ab9796a43d408786f1803ef4e29debfeb62fce2b6caa5ab2b24d1549c822a11c40c2856665"),
MustHexID("bc413ad270dd6ea25bddba78f3298b03b8ba6f8608ac03d06007d4116fa78ef5a0cfe8c80155089382fc7a193243ee5500082660cb5d7793f60f2d7d18650964"), hexEncPubkey("bc413ad270dd6ea25bddba78f3298b03b8ba6f8608ac03d06007d4116fa78ef5a0cfe8c80155089382fc7a193243ee5500082660cb5d7793f60f2d7d18650964"),
MustHexID("5a6a9ef07634d9eec3baa87c997b529b92652afa11473dfee41ef7037d5c06e0ddb9fe842364462d79dd31cff8a59a1b8d5bc2b810dea1d4cbbd3beb80ecec83"), hexEncPubkey("5a6a9ef07634d9eec3baa87c997b529b92652afa11473dfee41ef7037d5c06e0ddb9fe842364462d79dd31cff8a59a1b8d5bc2b810dea1d4cbbd3beb80ecec83"),
MustHexID("f492c6ee2696d5f682f7f537757e52744c2ae560f1090a07024609e903d334e9e174fc01609c5a229ddbcac36c9d21adaf6457dab38a25bfd44f2f0ee4277998"), hexEncPubkey("f492c6ee2696d5f682f7f537757e52744c2ae560f1090a07024609e903d334e9e174fc01609c5a229ddbcac36c9d21adaf6457dab38a25bfd44f2f0ee4277998"),
MustHexID("459e4db99298cb0467a90acee6888b08bb857450deac11015cced5104853be5adce5b69c740968bc7f931495d671a70cad9f48546d7cd203357fe9af0e8d2164"), hexEncPubkey("459e4db99298cb0467a90acee6888b08bb857450deac11015cced5104853be5adce5b69c740968bc7f931495d671a70cad9f48546d7cd203357fe9af0e8d2164"),
}, },
256: { 256: {
MustHexID("a8593af8a4aef7b806b5197612017951bac8845a1917ca9a6a15dd6086d608505144990b245785c4cd2d67a295701c7aac2aa18823fb0033987284b019656268"), hexEncPubkey("a8593af8a4aef7b806b5197612017951bac8845a1917ca9a6a15dd6086d608505144990b245785c4cd2d67a295701c7aac2aa18823fb0033987284b019656268"),
MustHexID("d2eebef914928c3aad77fc1b2a495f52d2294acf5edaa7d8a530b540f094b861a68fe8348a46a7c302f08ab609d85912a4968eacfea0740847b29421b4795d9e"), hexEncPubkey("d2eebef914928c3aad77fc1b2a495f52d2294acf5edaa7d8a530b540f094b861a68fe8348a46a7c302f08ab609d85912a4968eacfea0740847b29421b4795d9e"),
MustHexID("b14bfcb31495f32b650b63cf7d08492e3e29071fdc73cf2da0da48d4b191a70ba1a65f42ad8c343206101f00f8a48e8db4b08bf3f622c0853e7323b250835b91"), hexEncPubkey("b14bfcb31495f32b650b63cf7d08492e3e29071fdc73cf2da0da48d4b191a70ba1a65f42ad8c343206101f00f8a48e8db4b08bf3f622c0853e7323b250835b91"),
MustHexID("7feaee0d818c03eb30e4e0bf03ade0f3c21ca38e938a761aa1781cf70bda8cc5cd631a6cc53dd44f1d4a6d3e2dae6513c6c66ee50cb2f0e9ad6f7e319b309fd9"), hexEncPubkey("7feaee0d818c03eb30e4e0bf03ade0f3c21ca38e938a761aa1781cf70bda8cc5cd631a6cc53dd44f1d4a6d3e2dae6513c6c66ee50cb2f0e9ad6f7e319b309fd9"),
MustHexID("4ca3b657b139311db8d583c25dd5963005e46689e1317620496cc64129c7f3e52870820e0ec7941d28809311df6db8a2867bbd4f235b4248af24d7a9c22d1232"), hexEncPubkey("4ca3b657b139311db8d583c25dd5963005e46689e1317620496cc64129c7f3e52870820e0ec7941d28809311df6db8a2867bbd4f235b4248af24d7a9c22d1232"),
MustHexID("1181defb1d16851d42dd951d84424d6bd1479137f587fa184d5a8152be6b6b16ed08bcdb2c2ed8539bcde98c80c432875f9f724737c316a2bd385a39d3cab1d8"), hexEncPubkey("1181defb1d16851d42dd951d84424d6bd1479137f587fa184d5a8152be6b6b16ed08bcdb2c2ed8539bcde98c80c432875f9f724737c316a2bd385a39d3cab1d8"),
MustHexID("d9dd818769fa0c3ec9f553c759b92476f082817252a04a47dc1777740b1731d280058c66f982812f173a294acf4944a85ba08346e2de153ba3ba41ce8a62cb64"), hexEncPubkey("d9dd818769fa0c3ec9f553c759b92476f082817252a04a47dc1777740b1731d280058c66f982812f173a294acf4944a85ba08346e2de153ba3ba41ce8a62cb64"),
MustHexID("bd7c4f8a9e770aa915c771b15e107ca123d838762da0d3ffc53aa6b53e9cd076cffc534ec4d2e4c334c683f1f5ea72e0e123f6c261915ed5b58ac1b59f003d88"), hexEncPubkey("bd7c4f8a9e770aa915c771b15e107ca123d838762da0d3ffc53aa6b53e9cd076cffc534ec4d2e4c334c683f1f5ea72e0e123f6c261915ed5b58ac1b59f003d88"),
MustHexID("3dd5739c73649d510456a70e9d6b46a855864a4a3f744e088fd8c8da11b18e4c9b5f2d7da50b1c147b2bae5ca9609ae01f7a3cdea9dce34f80a91d29cd82f918"), hexEncPubkey("3dd5739c73649d510456a70e9d6b46a855864a4a3f744e088fd8c8da11b18e4c9b5f2d7da50b1c147b2bae5ca9609ae01f7a3cdea9dce34f80a91d29cd82f918"),
MustHexID("f0d7df1efc439b4bcc0b762118c1cfa99b2a6143a9f4b10e3c9465125f4c9fca4ab88a2504169bbcad65492cf2f50da9dd5d077c39574a944f94d8246529066b"), hexEncPubkey("f0d7df1efc439b4bcc0b762118c1cfa99b2a6143a9f4b10e3c9465125f4c9fca4ab88a2504169bbcad65492cf2f50da9dd5d077c39574a944f94d8246529066b"),
MustHexID("dd598b9ba441448e5fb1a6ec6c5f5aa9605bad6e223297c729b1705d11d05f6bfd3d41988b694681ae69bb03b9a08bff4beab5596503d12a39bffb5cd6e94c7c"), hexEncPubkey("dd598b9ba441448e5fb1a6ec6c5f5aa9605bad6e223297c729b1705d11d05f6bfd3d41988b694681ae69bb03b9a08bff4beab5596503d12a39bffb5cd6e94c7c"),
MustHexID("3fce284ac97e567aebae681b15b7a2b6df9d873945536335883e4bbc26460c064370537f323fd1ada828ea43154992d14ac0cec0940a2bd2a3f42ec156d60c83"), hexEncPubkey("3fce284ac97e567aebae681b15b7a2b6df9d873945536335883e4bbc26460c064370537f323fd1ada828ea43154992d14ac0cec0940a2bd2a3f42ec156d60c83"),
MustHexID("7c8dfa8c1311cb14fb29a8ac11bca23ecc115e56d9fcf7b7ac1db9066aa4eb39f8b1dabf46e192a65be95ebfb4e839b5ab4533fef414921825e996b210dd53bd"), hexEncPubkey("7c8dfa8c1311cb14fb29a8ac11bca23ecc115e56d9fcf7b7ac1db9066aa4eb39f8b1dabf46e192a65be95ebfb4e839b5ab4533fef414921825e996b210dd53bd"),
MustHexID("cafa6934f82120456620573d7f801390ed5e16ed619613a37e409e44ab355ef755e83565a913b48a9466db786f8d4fbd590bfec474c2524d4a2608d4eafd6abd"), hexEncPubkey("cafa6934f82120456620573d7f801390ed5e16ed619613a37e409e44ab355ef755e83565a913b48a9466db786f8d4fbd590bfec474c2524d4a2608d4eafd6abd"),
MustHexID("9d16600d0dd310d77045769fed2cb427f32db88cd57d86e49390c2ba8a9698cfa856f775be2013237226e7bf47b248871cf865d23015937d1edeb20db5e3e760"), hexEncPubkey("9d16600d0dd310d77045769fed2cb427f32db88cd57d86e49390c2ba8a9698cfa856f775be2013237226e7bf47b248871cf865d23015937d1edeb20db5e3e760"),
MustHexID("17be6b6ba54199b1d80eff866d348ea11d8a4b341d63ad9a6681d3ef8a43853ac564d153eb2a8737f0afc9ab320f6f95c55aa11aaa13bbb1ff422fd16bdf8188"), hexEncPubkey("17be6b6ba54199b1d80eff866d348ea11d8a4b341d63ad9a6681d3ef8a43853ac564d153eb2a8737f0afc9ab320f6f95c55aa11aaa13bbb1ff422fd16bdf8188"),
}, },
}, },
} }
type preminedTestnet struct { type preminedTestnet struct {
target NodeID target encPubkey
targetSha common.Hash // sha3(target) targetSha enode.ID // sha3(target)
dists [hashBits + 1][]NodeID 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 next := toaddr.Port - 1
var result []*Node var result []*node
for i, id := range tn.dists[toaddr.Port] { for i, ekey := range tn.dists[toaddr.Port] {
result = append(result, NewNode(id, net.ParseIP("127.0.0.1"), next, uint16(i))) 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) close() {}
func (*preminedTestnet) waitping(from NodeID) error { return nil } func (*preminedTestnet) waitping(from enode.ID) error { return nil }
func (*preminedTestnet) ping(toid NodeID, toaddr *net.UDPAddr) 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) key := encodePubkey(&k.PublicKey)
sha := crypto.Keccak256Hash(id[:]) ld := enode.LogDist(tn.targetSha, key.id())
ld := logdist(tn.targetSha, sha)
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 {

@ -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
}

@ -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 respTimeout = 500 * time.Millisecond
expiration = 20 * time.Second 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) key, err := decodePubkey(rn.ID)
err := n.validateComplete() 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 { func nodeToRPC(n *node) rpcNode {
return rpcNode{ID: n.ID, IP: n.IP, UDP: n.UDP, TCP: n.TCP} 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) n := wrapNode(enode.NewV4(key, from.IP, int(req.From.TCP), from.Port))
t.handleReply(n.ID(), pingPacket, req)
// Add the node to the table. Before doing so, ensure that we have a recent enough pong if time.Since(t.db.LastPongReceived(n.ID())) > bondExpiration {
// recorded in the database so their findnode requests will be accepted later. t.sendPing(n.ID(), from, func() { t.addThroughPing(n) })
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) })
} 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

@ -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} toid := enode.ID{1, 2, 3, 4}
target := NodeID{4, 5, 6, 7} 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: testTarget.id()}
nodes := &nodesByDistance{target: targetHash}
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) rid := enode.PubkeyToIDV4(&test.remotekey.PublicKey)
test.table.db.updateLastPingReceived(rid, time.Now()) 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{ list := []*node{
MustParseNode("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304"), wrapNode(enode.MustParseV4("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304")),
MustParseNode("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"), wrapNode(enode.MustParseV4("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303")),
MustParseNode("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17"), wrapNode(enode.MustParseV4("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17")),
MustParseNode("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"), 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) added := make(chan *node, 1)
test.table.nodeAddedHook = func(n *Node) { added <- n } 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) rid := encodePubkey(&test.remotekey.PublicKey).id()
if n.ID != rid { if n.ID() != rid {
t.Errorf("node has wrong ID: got %v, want %v", n.ID, rid) t.Errorf("node has wrong ID: got %v, want %v", n.ID(), rid)
} }
if !n.IP.Equal(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) t.Errorf("node has wrong IP: got %v, want: %v", n.IP(), test.remoteaddr.IP)
} }
if int(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) t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP(), test.remoteaddr.Port)
} }
if 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) 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 { if nodekey != wantNodeKey {
t.Errorf("got id %v\nwant id %v", nodeid, wantNodeID) t.Errorf("got id %v\nwant id %v", nodekey, wantNodeKey)
} }
} }
} }

@ -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. // List of known secure identity schemes.
var schemes sync.Map var ValidSchemes = enr.SchemeMap{
"v4": V4ID{},
// 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")
}
} }
// FindIdentityScheme resolves name to an identity scheme in the global registry. var ValidSchemesForTesting = enr.SchemeMap{
func FindIdentityScheme(name string) IdentityScheme { "v4": V4ID{},
s, ok := schemes.Load(name) "null": NullID{},
if !ok {
return nil
}
return s.(IdentityScheme)
} }
// v4ID is the "v4" identity scheme. // v4ID is the "v4" identity scheme.
type v4ID struct{} type V4ID struct{}
func init() {
RegisterIdentityScheme("v4", v4ID{})
}
// 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. func (V4ID) Verify(r *enr.Record, sig []byte) error {
type s256raw []byte
func (s256raw) ENRKey() string { return "secp256k1" }
func (v4ID) Verify(r *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}
}

@ -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)
}

@ -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
}

@ -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)
}
}
}

@ -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 package enode
// metadata about them for QoS purposes.
package discover
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. // DB is the node database, storing previously seen nodes and any collected metadata about
type nodeDB struct { // 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 // OpenDB opens a node database for storing and retrieving infos about known peers in the
// known peers in the network. If no path is given, an in-memory, temporary // network. If no path is given an in-memory, temporary database is constructed.
// database is constructed. func OpenDB(path string) (*DB, error) {
func newNodeDB(path string, version int, self NodeID) (*nodeDB, 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 // newMemoryNodeDB creates a new in-memory node database without a persistent backend.
// backend. func newMemoryDB() (*DB, error) {
func newMemoryNodeDB(self NodeID) (*nodeDB, 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{ return &DB{lvl: db, quit: make(chan struct{})}, nil
lvl: db,
self: self,
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{ return &DB{lvl: db, quit: make(chan struct{})}, nil
lvl: db,
self: self,
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 { func makeKey(id ID, field string) []byte {
if bytes.Equal(id[:], nodeDBNilNodeID[:]) { 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. // Node retrieves a node with a given id from the database.
func (db *nodeDB) node(id NodeID) *Node { 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 { if err := rlp.DecodeBytes(data, &node.r); err != nil {
log.Error("Failed to decode node RLP", "err", err) panic(fmt.Errorf("p2p/enode: can't decode node %x in DB: %v", id, err))
return nil
} }
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. // UpdateNode inserts - potentially overwriting - a node into the peer database.
func (db *nodeDB) updateNode(node *Node) error { func (db *DB) UpdateNode(node *Node) error {
blob, err := rlp.EncodeToBytes(node) 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. // DeleteNode deletes all information/keys associated with a node.
func (db *nodeDB) deleteNode(id NodeID) error { 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) {
if seen := db.lastPongReceived(id); seen.After(threshold) { continue
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. // LastPingReceived retrieves the time of the last ping packet received from
func (db *nodeDB) lastPingReceived(id NodeID) time.Time { // 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. // UpdateLastPingReceived updates the last time we tried contacting a remote node.
func (db *nodeDB) updateLastPingReceived(id NodeID, instance time.Time) error { 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. // LastPongReceived retrieves the time of the last successful pong from remote node.
func (db *nodeDB) lastPongReceived(id NodeID) time.Time { 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. // UpdateLastPongReceived updates the last pong time of a node.
func (db *nodeDB) hasBond(id NodeID) bool { func (db *DB) UpdateLastPongReceived(id ID, instance time.Time) error {
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 {
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. // FindFails retrieves the number of findnode failures since bonding.
func (db *nodeDB) findFails(id NodeID) int { 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. // UpdateFindFails updates the number of findnode failures since bonding.
func (db *nodeDB) updateFindFails(id NodeID, fails int) error { 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 { if now.Sub(db.LastPongReceived(n.ID())) > maxAge {
continue seek
}
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 return mustDecodeNode(id[:], it.Value())
if err := rlp.DecodeBytes(it.Value(), &n); err != nil {
log.Warn("Failed to decode node RLP", "id", id, "err", err)
continue
}
return &n
} }
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()
} }

@ -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 key: []byte{
0x1d, 0xd9, 0xd6, 0x5c, 0x45, 0x52, 0xb5, 0xeb, // node id 0x6e, 0x3a, // prefix
0x43, 0xd5, 0xad, 0x55, 0xa2, 0xee, 0x3f, 0x56, // 0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, // node id
0xc6, 0xcb, 0xc1, 0xc6, 0x4a, 0x5c, 0x8d, 0x65, //
0x9f, 0x51, 0xfc, 0xd5, 0x1b, 0xac, 0xe2, 0x43, //
0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, //
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) { func TestDBInt64(t *testing.T) {
db, _ := newNodeDB("", nodeDBVersion, NodeID{}) db, _ := OpenDB("")
defer db.close() 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) { func TestDBFetchStore(t *testing.T) {
node := NewNode( node := NewV4(
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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{}) db, _ := OpenDB("")
defer db.close() 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( node: NewV4(
MustHexID("0x84d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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( node: NewV4(
MustHexID("0x57d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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( node: NewV4(
MustHexID("0x22d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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( node: NewV4(
MustHexID("0x44d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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( node: NewV4(
MustHexID("0xe2d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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) { func testSeedQuery() error {
db, _ := newNodeDB("", nodeDBVersion, nodeDBSeedQueryNodes[1].node.ID) db, _ := OpenDB("")
defer db.close() 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 { if err := db.UpdateNode(seed.node); err != nil {
t.Fatalf("node %d: failed to insert: %v", i, err) return fmt.Errorf("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 insert bondTime: %v", i, err) 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) seeds := db.QuerySeeds(len(nodeDBSeedQueryNodes)*2, time.Hour)
have := make(map[NodeID]struct{}) 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{}) want := make(map[ID]struct{})
for _, seed := range nodeDBSeedQueryNodes[2:] { for _, seed := range nodeDBSeedQueryNodes[1:] {
want[seed.node.ID] = struct{}{} 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() 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()
} }
var nodeDBExpirationNodes = []struct { var nodeDBExpirationNodes = []struct {
@ -303,8 +326,8 @@ var nodeDBExpirationNodes = []struct {
exp bool exp bool
}{ }{
{ {
node: NewNode( node: NewV4(
MustHexID("0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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( node: NewV4(
MustHexID("0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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) { func TestDBExpiration(t *testing.T) {
db, _ := newNodeDB("", nodeDBVersion, NodeID{}) db, _ := OpenDB("")
defer db.close() 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")
}
}

@ -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))
}

@ -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( wantResult: NewV4(
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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( wantResult: NewV4(
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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( wantResult: NewV4(
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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( wantResult: NewV4(
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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( wantResult: NewV4(
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
nil, 0, 0, nil, 0, 0,
), ),
}, },
{ {
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439",
wantResult: NewNode( wantResult: NewV4(
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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} 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 := MustHexID("0x000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") id1 := HexID("0x00000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
id2 := MustHexID("000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") 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) { func TestID_textEncoding(t *testing.T) {
ref := NodeID{ 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, 0x31, 0x32,
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50,
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x60,
0x61, 0x62, 0x63, 0x64,
} }
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} base := ID{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} x := ID{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
if distcmp(base, x, x) != 0 { if DistCmp(base, x, x) != 0 {
t.Errorf("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} x := ID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
if logdist(x, x) != 0 { if LogDist(x, x) != 0 {
t.Errorf("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())
} }
return reflect.ValueOf(id)
} }

@ -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. // 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
// Records contain named keys. 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 // Signature Handling
// its signature. When creating a record, set the entries you want, then call Sign to add the //
// signature. Modifying a record invalidates the signature. // 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 on node record")
errInvalidSig = errors.New("invalid signature")
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 dec, raw, s.ListEnd()
return err }
}
_, scheme := dec.idScheme() // IdentityScheme returns the name of the identity scheme in the record.
if scheme == nil { func (r *Record) IdentityScheme() string {
return errNoID var id ID
} r.Load(&id)
if err := scheme.Verify(&dec, dec.signature); err != nil { return string(id)
return err
}
*r = dec
return nil
} }
// NodeAddr returns the node address. The return value will be nil if the record is // VerifySignature checks whether the record is signed using the given identity scheme.
// unsigned or uses an unknown identity scheme. func (r *Record) VerifySignature(s IdentityScheme) error {
func (r *Record) NodeAddr() []byte { return s.Verify(r, r.signature)
_, scheme := r.idScheme()
if scheme == nil {
return nil
}
return scheme.NodeAddr(r)
} }
// 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 // You can also use SetSig to remove the signature explicitly by passing a nil scheme
// inconsitencies here are always implementation bugs in the signing function calling // and signature.
// this method. //
id, s := r.idScheme() // SetSig panics when either the scheme or the signature (but not both) are nil.
if s == nil { func (r *Record) SetSig(s IdentityScheme, sig []byte) error {
panic(errNoID) switch {
} // Prevent storing invalid data.
if id != idscheme { case s == nil && sig != nil:
panic(fmt.Errorf("identity scheme mismatch in Sign: record has %s, want %s", id, idscheme)) 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 against the scheme. // Verify if we have a scheme.
if err := s.Verify(r, sig); err != nil { case s != nil:
return err if err := s.Verify(r, sig); err != nil {
} return err
raw, err := r.encode(sig) }
if err != nil { raw, err := r.encode(sig)
return err 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))
}

@ -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)) require.NoError(t, signTest([]byte{5}, &r))
if !r.Signed() { if len(r.signature) == 0 {
t.Error("Signed return false for signed record") 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() { if len(r.signature) != 0 {
t.Error("Signed returned true for modified record") 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) { type testSig struct{}
var r Record
for i := 0; i < b.N; i++ { type testID []byte
rlp.DecodeBytes(pyRecord, &r)
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() return id
r.NodeAddr()
} }

@ -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

@ -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)
}
}

@ -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,

@ -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"` Type PeerEventType `json:"type"`
Peer discover.NodeID `json:"peer"` Peer enode.ID `json:"peer"`
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`
Protocol string `json:"protocol,omitempty"` Protocol string `json:"protocol,omitempty"`
MsgCode *uint64 `json:"msg_code,omitempty"` MsgCode *uint64 `json:"msg_code,omitempty"`
MsgSize *uint32 `json:"msg_size,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 { func (p *Peer) ID() enode.ID {
return p.rw.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
} }

@ -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)} c1 := &conn{fd: fd1, node: newNode(randomID(), nil), transport: newTestTransport(&newkey().PublicKey, fd1)}
c2 := &conn{fd: fd2, transport: newTestTransport(randomID(), fd2)} 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())

@ -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 {

@ -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] node := s.Nodes[0]
if err := s.TestExchanges(protoHandshakeExchange(id, proto)...); err != nil { 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] node := s.Nodes[0]
if err := s.TestExchanges(protoHandshakeExchange(id, &protoHandshake{42, "420"})...); err != nil { 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]) { if !pp.Has(s.Nodes[1].ID()) {
t.Fatalf("missing peer test-1: %v (%v)", pp, s.IDs) 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]}, Msg: &kill{s.Nodes[peer].ID()},
Peer: s.IDs[0], 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]) { if pp.Has(s.Nodes[peer].ID()) {
t.Fatalf("peer test-%v not dropped: %v (%v)", peer, pp, s.IDs) t.Fatalf("peer test-%v not dropped: %v (%v)", peer, pp, s.Nodes)
} }
} }

@ -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 initiator bool
remoteID discover.NodeID remote *ecies.PublicKey // remote-pubk
remotePub *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, Remote: h.remote,
AES: aesSecret, AES: aesSecret,
MAC: crypto.Keccak256(ecdheSecret, 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) { func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remote *ecdsa.PublicKey) (s secrets, err error) {
h := &encHandshake{initiator: true, remoteID: remoteID} 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[:] rpub, err := importPublicKey(msg.InitiatorPubkey[:])
h.remoteID = msg.InitiatorPubkey
rpub, err := h.remoteID.Pubkey()
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
} }

@ -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 side string
id discover.NodeID pubkey *ecdsa.PublicKey
err error 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.pubkey, r.err = c0.doEncHandshake(prv0, &prv1.PublicKey)
r.id, r.err = c0.doEncHandshake(prv0, dest)
if r.err != nil { if r.err != nil {
return return
} }
id1 := discover.PubkeyID(&prv1.PublicKey) if !reflect.DeepEqual(r.pubkey, &prv1.PublicKey) {
if r.id != id1 { r.err = fmt.Errorf("remote pubkey mismatch: got %v, want: %v", r.pubkey, &prv1.PublicKey)
r.err = fmt.Errorf("remote ID mismatch: got %v, want: %v", r.id, id1)
} }
}() }()
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 !reflect.DeepEqual(r.pubkey, &prv0.PublicKey) {
if r.id != id0 { r.err = fmt.Errorf("remote ID mismatch: got %v, want: %v", r.pubkey, &prv0.PublicKey)
r.err = fmt.Errorf("remote ID mismatch: got %v, want: %v", r.id, id0)
} }
}() }()
@ -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} pub0 = crypto.FromECDSAPub(&prv0.PublicKey)[1:]
hs0 = &protoHandshake{Version: 3, ID: node0.ID, Caps: []Cap{{"a", 0}, {"b", 2}}} 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} pub1 = crypto.FromECDSAPub(&prv1.PublicKey)[1:]
hs1 = &protoHandshake{Version: 3, ID: node1.ID, Caps: []Cap{{"c", 1}, {"d", 3}}} 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 { if !reflect.DeepEqual(rpubkey, &prv1.PublicKey) {
t.Errorf("dial side remote id mismatch: got %v, want %v", remid, node1.ID) 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 { if !reflect.DeepEqual(rpubkey, &prv0.PublicKey) {
t.Errorf("listen side remote id mismatch: got %v, want %v", remid, node0.ID) t.Errorf("listen side remote pubkey mismatch: got %v, want %v", rpubkey, &prv0.PublicKey)
return return
} }

@ -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 addstatic chan *enode.Node
removestatic chan *discover.Node removestatic chan *enode.Node
addtrusted chan *discover.Node addtrusted chan *enode.Node
removetrusted chan *discover.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. 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
caps []Cap // valid after the protocol handshake name string // 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{}) { if (c.node.ID() != enode.ID{}) {
s += " " + c.id.String() 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 { if !running {
return &discover.Node{IP: net.ParseIP("0.0.0.0")} 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 { func (srv *Server) makeSelf(listener net.Listener, ntab discoverTable) *enode.Node {
// If the server's not running, return an empty 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. addr := srv.tcpAddr(listener)
if listener == nil { return enode.NewV4(&srv.PrivateKey.PublicKey, addr.IP, addr.Port, 0)
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),
}
} }
// 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.addstatic = make(chan *enode.Node)
srv.removestatic = make(chan *discover.Node) srv.removestatic = make(chan *enode.Node)
srv.addtrusted = make(chan *discover.Node) srv.addtrusted = make(chan *enode.Node)
srv.removetrusted = make(chan *discover.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) addStatic(*enode.Node)
removeStatic(*discover.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 { if _, ok := trusted[n.ID()]; ok {
delete(trusted, n.ID) 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 remotePubkey, err := c.doEncHandshake(srv.PrivateKey, dialPubkey)
if c.id, err = c.doEncHandshake(srv.PrivateKey, dialDest); err != nil { 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) if dialDest != nil {
// For dialed connections, check that the remote public key matches. // For dialed connections, check that the remote public key matches.
if dialDest != nil && c.id != dialDest.ID { if dialPubkey.X.Cmp(remotePubkey.X) != 0 || dialPubkey.Y.Cmp(remotePubkey.Y) != 0 {
clog.Trace("Dialed identity mismatch", "want", c, dialDest.ID) return DiscUnexpectedIdentity
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 { if id := c.node.ID(); !bytes.Equal(crypto.Keccak256(phs.ID), id[:]) {
clog.Trace("Wrong devp2p handshake identity", "err", phs.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(), ID: node.ID().String(),
IP: node.IP.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.Discovery = node.UDP()
info.Ports.Listener = int(node.TCP) 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 {

@ -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() { // func init() {
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlError, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) // 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) { func (c *testTransport) doEncHandshake(prv *ecdsa.PrivateKey, dialDest *ecdsa.PublicKey) (*ecdsa.PublicKey, error) {
return c.id, nil 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) tx := newTestTransport(&trustedNode.PublicKey, fd)
return &conn{fd: fd, transport: tx, flags: inboundConn, id: id, cont: make(chan error)} 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() var tp = &setupTransport{
clientnode := &discover.Node{ID: clientid} pubkey: &clientkey.PublicKey,
phs: protoHandshake{
var tp *setupTransport = &setupTransport{ ID: crypto.FromECDSAPub(&clientkey.PublicKey)[1:],
id: clientid,
phs: &protoHandshake{
ID: clientid,
// 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() var (
srvkey := newkey() clientkey, srvkey = newkey(), newkey()
srvid := discover.PubkeyID(&srvkey.PublicKey) 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}, tt: &setupTransport{pubkey: clientpub},
dialDest: &discover.Node{ID: randomID()}, 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()}}, tt: &setupTransport{pubkey: clientpub, phs: protoHandshake{ID: randomID().Bytes()}},
dialDest: &discover.Node{ID: id}, 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")}, tt: &setupTransport{pubkey: clientpub, protoHandshakeErr: errors.New("foo")},
dialDest: &discover.Node{ID: id}, 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 pubkey *ecdsa.PublicKey
encHandshakeErr error encHandshakeErr error
phs protoHandshake
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))
} }

@ -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
} }

@ -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)

@ -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) { func (s *SimAdapter) Dial(dest *enode.Node) (conn net.Conn, err error) {
node, ok := s.GetNode(dest.ID) 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 // Node returns a node descriptor representing the SimNode
func (sn *SimNode) Node() *discover.Node { func (sn *SimNode) Node() *enode.Node {
return discover.NewNode(sn.ID, net.IP{127, 0, 0, 1}, 30303, 30303) 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

@ -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 := n.ID.UnmarshalText([]byte(confJSON.ID)); err != nil {
if 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

@ -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),

@ -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)

@ -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), One: enode.HexID(one),
Other: discover.MustHexID(other), Other: enode.HexID(other),
Up: up, Up: up,
}, },
} }

@ -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) { func connectNodesInRing(net *Network, nodeCount int) ([]enode.ID, error) {
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 := net.NewNodeWithConfig(conf) node, err := net.NewNodeWithConfig(conf)

@ -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

@ -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"` One enode.ID `json:"one"`
Other discover.NodeID `json:"other"` Other enode.ID `json:"other"`
Protocol string `json:"protocol"` Protocol string `json:"protocol"`
Code uint64 `json:"code"` Code uint64 `json:"code"`
Received bool `json:"received"` 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 { func ConnLabel(source, target enode.ID) string {
var first, second discover.NodeID 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

@ -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 {

@ -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

@ -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]

@ -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 Msg interface{} // type of message to be sent
Code uint64 // code of message is given Code uint64 // code of message is given
Peer discover.NodeID // the peer to send the message to Peer enode.ID // the peer to send the message to
Timeout time.Duration // timeout duration for the sending 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 Msg interface{} // type of message to expect
Code uint64 // code of message is now given Code uint64 // code of message is now given
Peer discover.NodeID // the peer that expects the message Peer enode.ID // the peer that expects the message
Timeout time.Duration // timeout duration for receiving 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 Peer enode.ID // discconnected peer
Error error // disconnect reason 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
} }

@ -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 {

@ -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)

@ -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) {

@ -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] node := s.Nodes[0]
raddr := NewAddrFromNodeID(id) 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(),
}, },
}, },
}) })

@ -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 protoRequestFunc RequestFunc // request function fetcher calls to issue retrieve request for a chunk
addr storage.Address // the address of the chunk to be fetched addr storage.Address // the address of the chunk to be fetched
offerC chan *discover.NodeID // channel of sources (peer node id strings) 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 Addr storage.Address // chunk address
Source *discover.NodeID // nodeID of peer to request from (can be nil) Source *enode.ID // nodeID of peer to request from (can be nil)
SkipCheck bool // whether to offer the chunk first or deliver directly 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) 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 doRequest bool // determines if retrieval is initiated in the current iteration
wait *time.Timer // timer for search timeout wait *time.Timer // timer for search timeout
waitC <-chan time.Time // timer channel waitC <-chan time.Time // timer channel
sources []*discover.NodeID // known sources, ie. peers that offered the chunk sources []*enode.ID // known sources, ie. peers that offered the chunk
requested bool // true if the chunk was actually requested 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{

@ -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 requestedPeerID = enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8")
var sourcePeerID = discover.MustHexID("2dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439") 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{ peers := []enode.ID{
discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8"),
discover.MustHexID("2dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 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)

@ -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 *HiveParams // settings
*Kademlia // the overlay connectiviy driver *Kademlia // the overlay connectiviy driver
Store state.Store // storage interface to save peers across sessions Store state.Store // storage interface to save peers across sessions
addPeer func(*discover.Node) // server callback to connect to a peer 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{} { func (h *Hive) PeerInfo(id enode.ID) interface{} {
addr := NewAddrFromNodeID(id) 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

@ -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] node := s.Nodes[0]
raddr := NewAddrFromNodeID(id) 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 { for _, node := range s.Nodes {
raddr := NewAddrFromNodeID(id) 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 { if i != 5 {
t.Fatalf("invalid peers loaded") 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)
} }
} }

@ -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 nodeMap map[int][]enode.ID
kademlias map[discover.NodeID]*Kademlia 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) kademlias = make(map[enode.ID]*Kademlia)
kademlia := func(id discover.NodeID) *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 {

@ -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 { func NewBzzPeer(p *protocols.Peer) *BzzPeer {
return &BzzPeer{ return &BzzPeer{Peer: p, BzzAddr: NewAddr(p.Node())}
Peer: p,
localAddr: addr,
BzzAddr: NewAddrFromNodeID(p.ID()),
}
} }
// 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 // ID returns the node identifier in the underlay.
func (a *BzzAddr) ID() discover.NodeID { func (a *BzzAddr) ID() enode.ID {
return discover.MustParseNode(string(a.UAddr)).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) node := enode.NewV4(&key.PublicKey, net.IP{127, 0, 0, 1}, 30303, 30303)
var id discover.NodeID return NewAddr(node)
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()),
}
} }
// ToOverlayAddr creates an overlayaddress from a byte slice // NewAddr constucts a BzzAddr from a node record.
func ToOverlayAddr(id []byte) []byte { func NewAddr(node *enode.Node) *BzzAddr {
return crypto.Keccak256(id) return &BzzAddr{OAddr: node.ID().Bytes(), UAddr: []byte(node.String())}
} }

@ -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{ return srv(&BzzPeer{Peer: protocols.NewPeer(p, rw, spec), BzzAddr: NewAddr(p.Node())})
Peer: protocols.NewPeer(p, rw, spec),
localAddr: addr,
BzzAddr: NewAddrFromNodeID(p.ID()),
})
} }
s := p2ptest.NewProtocolTester(t, NewNodeIDFromAddr(addr), n, protocol) s := p2ptest.NewProtocolTester(t, addr.ID(), n, protocol)
for _, id := range s.IDs { for _, node := range s.Nodes {
cs[id.String()] = make(chan bool) 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 var peers []enode.ID
id := NewNodeIDFromAddr(rhs.Addr) 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)}, &HandshakeMsg{Version: TestProtocolVersion, NetworkID: 321, Addr: NewAddr(node)},
&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): network id mismatch 321 (!= 3)")}, &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)}, &HandshakeMsg{Version: 0, NetworkID: TestProtocolNetworkID, Addr: NewAddr(node)},
&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): version mismatch 0 (!= %d)", TestProtocolVersion)}, &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] node := pt.Nodes[0]
addr := NewAddrFromNodeID(id) 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 { if pt.bzz.handshakes[node.ID()].LightNode != test.lightNode {
t.Fatalf("peer LightNode flag is %v, should be %v", pt.bzz.handshakes[id].LightNode, test.lightNode) t.Fatalf("peer LightNode flag is %v, should be %v", pt.bzz.handshakes[node.ID()].LightNode, test.lightNode)
} }
}) })
} }

@ -16,15 +16,13 @@
package simulation package simulation
import ( import "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/discover"
)
// 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

@ -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))
} }

@ -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++ {

@ -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()

@ -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{

@ -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 {

@ -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{

@ -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) { func (s *Simulation) AddNodes(count int, opts ...AddNodeOption) (ids []enode.ID, err error) {
ids = make([]discover.NodeID, 0, count) 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) { func (s *Simulation) StartRandomNodes(count int) (ids []enode.ID, err error) {
ids = make([]discover.NodeID, 0, count) 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) { func (s *Simulation) StopRandomNodes(count int) (ids []enode.ID, err error) {
ids = make([]discover.NodeID, 0, count) 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 {

@ -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{

@ -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

@ -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 buckets map[enode.ID]*sync.Map
pivotNodeID *discover.NodeID 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{}),
} }

@ -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) trigger := make(chan enode.ID)
ids := make([]discover.NodeID, nodes) 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[id.String()]); err != nil {
if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); 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) trigger := make(chan enode.ID)
ids := make([]discover.NodeID, nodes) 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[id.String()]); err != nil {
if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); 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() node := enode.NewV4(&ctx.Config.PrivateKey.PublicKey, adapters.ExternalIP(), int(ctx.Config.Port), int(ctx.Config.Port))
addr := network.NewAddr(node)
addr := network.NewAddrFromNodeIDAndPort(ctx.Config.ID, host, ctx.Config.Port)
kp := network.NewKadParams() kp := network.NewKadParams()
kp.MinProxBinSize = testMinProxBinSize kp.MinProxBinSize = testMinProxBinSize

File diff suppressed because one or more lines are too long

@ -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}]}

@ -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

@ -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()

@ -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()))

@ -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

@ -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] node := tester.Nodes[0]
peer := streamer.getPeer(peerID) 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) {
node := ctx.Config.Node()
id := ctx.Config.ID addr := network.NewAddr(node)
addr := network.NewAddrFromNodeID(id) store, datadir, err := createTestLocalStorageForID(node.ID(), addr)
store, datadir, err := createTestLocalStorageForID(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 node := ctx.Config.Node()
addr := network.NewAddrFromNodeID(id) addr := network.NewAddr(node)
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
} }
@ -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,

@ -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) {
n := ctx.Config.Node()
id := ctx.Config.ID addr := network.NewAddr(n)
addr := network.NewAddrFromNodeID(id) store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
store, datadir, err := createTestLocalStorageForID(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)

@ -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
} }

@ -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) {
node := ctx.Config.Node()
id := ctx.Config.ID addr := network.NewAddr(node)
addr := network.NewAddrFromNodeID(id) store, datadir, err := createTestLocalStorageForID(node.ID(), addr)
store, datadir, err := createTestLocalStorageForID(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) {
node := ctx.Config.Node()
id := ctx.Config.ID addr := network.NewAddr(node)
addr := network.NewAddrFromNodeID(id) store, datadir, err := createTestLocalStorageForID(node.ID(), addr)
store, datadir, err := createTestLocalStorageForID(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
} }

@ -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) {
n := ctx.Config.Node()
id := ctx.Config.ID addr := network.NewAddr(n)
addr := network.NewAddrFromNodeID(id) store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
store, datadir, err := createTestLocalStorageForID(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) {
n := ctx.Config.Node()
id := ctx.Config.ID addr := network.NewAddr(n)
addr := network.NewAddrFromNodeID(id) store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
store, datadir, err := createTestLocalStorageForID(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

@ -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 { 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.ID(), p.ID(), bin)) 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)
} }

@ -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(),
}, },
}, },
}) })

@ -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

@ -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 node := ctx.Config.Node()
addr := network.NewAddrFromNodeID(id) 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 have changed in this diff Show More

Loading…
Cancel
Save