Skip to content

Commit 524aaf5

Browse files
authored
p2p/discover: implement v5.1 wire protocol (#21647)
This change implements the Discovery v5.1 wire protocol and also adds an interactive test suite for this protocol.
1 parent 4eb01b2 commit 524aaf5

24 files changed

+3220
-1411
lines changed

cmd/devp2p/README.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# The devp2p command
2+
3+
The devp2p command line tool is a utility for low-level peer-to-peer debugging and
4+
protocol development purposes. It can do many things.
5+
6+
### ENR Decoding
7+
8+
Use `devp2p enrdump <base64>` to verify and display an Ethereum Node Record.
9+
10+
### Node Key Management
11+
12+
The `devp2p key ...` command family deals with node key files.
13+
14+
Run `devp2p key generate mynode.key` to create a new node key in the `mynode.key` file.
15+
16+
Run `devp2p key to-enode mynode.key -ip 127.0.0.1 -tcp 30303` to create an enode:// URL
17+
corresponding to the given node key and address information.
18+
19+
### Maintaining DNS Discovery Node Lists
20+
21+
The devp2p command can create and publish DNS discovery node lists.
22+
23+
Run `devp2p dns sign <directory>` to update the signature of a DNS discovery tree.
24+
25+
Run `devp2p dns sync <enrtree-URL>` to download a complete DNS discovery tree.
26+
27+
Run `devp2p dns to-cloudflare <directory>` to publish a tree to CloudFlare DNS.
28+
29+
Run `devp2p dns to-route53 <directory>` to publish a tree to Amazon Route53.
30+
31+
You can find more information about these commands in the [DNS Discovery Setup Guide][dns-tutorial].
32+
33+
### Discovery v4 Utilities
34+
35+
The `devp2p discv4 ...` command family deals with the [Node Discovery v4][discv4]
36+
protocol.
37+
38+
Run `devp2p discv4 ping <enode/ENR>` to ping a node.
39+
40+
Run `devp2p discv4 resolve <enode/ENR>` to find the most recent node record of a node in
41+
the DHT.
42+
43+
Run `devp2p discv4 crawl <nodes.json path>` to create or update a JSON node set.
44+
45+
### Discovery v5 Utilities
46+
47+
The `devp2p discv5 ...` command family deals with the [Node Discovery v5][discv5]
48+
protocol. This protocol is currently under active development.
49+
50+
Run `devp2p discv5 ping <ENR>` to ping a node.
51+
52+
Run `devp2p discv5 resolve <ENR>` to find the most recent node record of a node in
53+
the discv5 DHT.
54+
55+
Run `devp2p discv5 listen` to run a Discovery v5 node.
56+
57+
Run `devp2p discv5 crawl <nodes.json path>` to create or update a JSON node set containing
58+
discv5 nodes.
59+
60+
### Discovery Test Suites
61+
62+
The devp2p command also contains interactive test suites for Discovery v4 and Discovery
63+
v5.
64+
65+
To run these tests against your implementation, you need to set up a networking
66+
environment where two separate UDP listening addresses are available on the same machine.
67+
The two listening addresses must also be routed such that they are able to reach the node
68+
you want to test.
69+
70+
For example, if you want to run the test on your local host, and the node under test is
71+
also on the local host, you need to assign two IP addresses (or a larger range) to your
72+
loopback interface. On macOS, this can be done by executing the following command:
73+
74+
sudo ifconfig lo0 add 127.0.0.2
75+
76+
You can now run either test suite as follows: Start the node under test first, ensuring
77+
that it won't talk to the Internet (i.e. disable bootstrapping). An easy way to prevent
78+
unintended connections to the global DHT is listening on `127.0.0.1`.
79+
80+
Now get the ENR of your node and store it in the `NODE` environment variable.
81+
82+
Start the test by running `devp2p discv5 test -listen1 127.0.0.1 -listen2 127.0.0.2 $NODE`.
83+
84+
[dns-tutorial]: https://geth.ethereum.org/docs/developers/dns-discovery-setup
85+
[discv4]: https://github.com/ethereum/devp2p/tree/master/discv4.md
86+
[discv5]: https://github.com/ethereum/devp2p/tree/master/discv5/discv5.md

cmd/devp2p/discv4cmd.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,15 +286,23 @@ func listen(ln *enode.LocalNode, addr string) *net.UDPConn {
286286
}
287287
usocket := socket.(*net.UDPConn)
288288
uaddr := socket.LocalAddr().(*net.UDPAddr)
289-
ln.SetFallbackIP(net.IP{127, 0, 0, 1})
289+
if uaddr.IP.IsUnspecified() {
290+
ln.SetFallbackIP(net.IP{127, 0, 0, 1})
291+
} else {
292+
ln.SetFallbackIP(uaddr.IP)
293+
}
290294
ln.SetFallbackUDP(uaddr.Port)
291295
return usocket
292296
}
293297

294298
func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) {
295299
s := params.RinkebyBootnodes
296300
if ctx.IsSet(bootnodesFlag.Name) {
297-
s = strings.Split(ctx.String(bootnodesFlag.Name), ",")
301+
input := ctx.String(bootnodesFlag.Name)
302+
if input == "" {
303+
return nil, nil
304+
}
305+
s = strings.Split(input, ",")
298306
}
299307
nodes := make([]*enode.Node, len(s))
300308
var err error

cmd/devp2p/discv5cmd.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ package main
1818

1919
import (
2020
"fmt"
21+
"os"
2122
"time"
2223

24+
"github.com/ethereum/go-ethereum/cmd/devp2p/internal/v5test"
2325
"github.com/ethereum/go-ethereum/common"
26+
"github.com/ethereum/go-ethereum/internal/utesting"
27+
"github.com/ethereum/go-ethereum/log"
2428
"github.com/ethereum/go-ethereum/p2p/discover"
2529
"gopkg.in/urfave/cli.v1"
2630
)
@@ -33,6 +37,7 @@ var (
3337
discv5PingCommand,
3438
discv5ResolveCommand,
3539
discv5CrawlCommand,
40+
discv5TestCommand,
3641
discv5ListenCommand,
3742
},
3843
}
@@ -53,6 +58,12 @@ var (
5358
Action: discv5Crawl,
5459
Flags: []cli.Flag{bootnodesFlag, crawlTimeoutFlag},
5560
}
61+
discv5TestCommand = cli.Command{
62+
Name: "test",
63+
Usage: "Runs protocol tests against a node",
64+
Action: discv5Test,
65+
Flags: []cli.Flag{testPatternFlag, testListen1Flag, testListen2Flag},
66+
}
5667
discv5ListenCommand = cli.Command{
5768
Name: "listen",
5869
Usage: "Runs a node",
@@ -103,6 +114,30 @@ func discv5Crawl(ctx *cli.Context) error {
103114
return nil
104115
}
105116

117+
func discv5Test(ctx *cli.Context) error {
118+
// Disable logging unless explicitly enabled.
119+
if !ctx.GlobalIsSet("verbosity") && !ctx.GlobalIsSet("vmodule") {
120+
log.Root().SetHandler(log.DiscardHandler())
121+
}
122+
123+
// Filter and run test cases.
124+
suite := &v5test.Suite{
125+
Dest: getNodeArg(ctx),
126+
Listen1: ctx.String(testListen1Flag.Name),
127+
Listen2: ctx.String(testListen2Flag.Name),
128+
}
129+
tests := suite.AllTests()
130+
if ctx.IsSet(testPatternFlag.Name) {
131+
tests = utesting.MatchTests(tests, ctx.String(testPatternFlag.Name))
132+
}
133+
results := utesting.RunTests(tests, os.Stdout)
134+
if fails := utesting.CountFailures(results); fails > 0 {
135+
return fmt.Errorf("%v/%v tests passed.", len(tests)-fails, len(tests))
136+
}
137+
fmt.Printf("%v/%v passed\n", len(tests), len(tests))
138+
return nil
139+
}
140+
106141
func discv5Listen(ctx *cli.Context) error {
107142
disc := startV5(ctx)
108143
defer disc.Close()

0 commit comments

Comments
 (0)