cmd/devp2p: add --extaddr flag (#26312)

The new flag allows configuring an explicit endpoint which is to be
announced in the DHT. This feature was originally developed for the
discv5 wormhole experiment (#25798), but it's useful in other contexts
as well.
This commit is contained in:
Felix Lange 2022-12-06 16:25:53 +01:00 committed by GitHub
parent 01953b3470
commit b44abf56a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 17 deletions

View File

@ -19,6 +19,7 @@ package main
import ( import (
"fmt" "fmt"
"net" "net"
"strconv"
"strings" "strings"
"time" "time"
@ -50,34 +51,34 @@ var (
Usage: "Sends ping to a node", Usage: "Sends ping to a node",
Action: discv4Ping, Action: discv4Ping,
ArgsUsage: "<node>", ArgsUsage: "<node>",
Flags: v4NodeFlags, Flags: discoveryNodeFlags,
} }
discv4RequestRecordCommand = &cli.Command{ discv4RequestRecordCommand = &cli.Command{
Name: "requestenr", Name: "requestenr",
Usage: "Requests a node record using EIP-868 enrRequest", Usage: "Requests a node record using EIP-868 enrRequest",
Action: discv4RequestRecord, Action: discv4RequestRecord,
ArgsUsage: "<node>", ArgsUsage: "<node>",
Flags: v4NodeFlags, Flags: discoveryNodeFlags,
} }
discv4ResolveCommand = &cli.Command{ discv4ResolveCommand = &cli.Command{
Name: "resolve", Name: "resolve",
Usage: "Finds a node in the DHT", Usage: "Finds a node in the DHT",
Action: discv4Resolve, Action: discv4Resolve,
ArgsUsage: "<node>", ArgsUsage: "<node>",
Flags: v4NodeFlags, Flags: discoveryNodeFlags,
} }
discv4ResolveJSONCommand = &cli.Command{ discv4ResolveJSONCommand = &cli.Command{
Name: "resolve-json", Name: "resolve-json",
Usage: "Re-resolves nodes in a nodes.json file", Usage: "Re-resolves nodes in a nodes.json file",
Action: discv4ResolveJSON, Action: discv4ResolveJSON,
Flags: v4NodeFlags, Flags: discoveryNodeFlags,
ArgsUsage: "<nodes.json file>", ArgsUsage: "<nodes.json file>",
} }
discv4CrawlCommand = &cli.Command{ discv4CrawlCommand = &cli.Command{
Name: "crawl", Name: "crawl",
Usage: "Updates a nodes.json file with random nodes found in the DHT", Usage: "Updates a nodes.json file with random nodes found in the DHT",
Action: discv4Crawl, Action: discv4Crawl,
Flags: flags.Merge(v4NodeFlags, []cli.Flag{crawlTimeoutFlag}), Flags: flags.Merge(discoveryNodeFlags, []cli.Flag{crawlTimeoutFlag}),
} }
discv4TestCommand = &cli.Command{ discv4TestCommand = &cli.Command{
Name: "test", Name: "test",
@ -110,6 +111,10 @@ var (
Name: "addr", Name: "addr",
Usage: "Listening address", Usage: "Listening address",
} }
extAddrFlag = &cli.StringFlag{
Name: "extaddr",
Usage: "UDP endpoint announced in ENR. You can provide a bare IP address or IP:port as the value of this flag.",
}
crawlTimeoutFlag = &cli.DurationFlag{ crawlTimeoutFlag = &cli.DurationFlag{
Name: "timeout", Name: "timeout",
Usage: "Time limit for the crawl.", Usage: "Time limit for the crawl.",
@ -122,11 +127,12 @@ var (
} }
) )
var v4NodeFlags = []cli.Flag{ var discoveryNodeFlags = []cli.Flag{
bootnodesFlag, bootnodesFlag,
nodekeyFlag, nodekeyFlag,
nodedbFlag, nodedbFlag,
listenAddrFlag, listenAddrFlag,
extAddrFlag,
} }
func discv4Ping(ctx *cli.Context) error { func discv4Ping(ctx *cli.Context) error {
@ -228,7 +234,7 @@ func discv4Test(ctx *cli.Context) error {
// startV4 starts an ephemeral discovery V4 node. // startV4 starts an ephemeral discovery V4 node.
func startV4(ctx *cli.Context) *discover.UDPv4 { func startV4(ctx *cli.Context) *discover.UDPv4 {
ln, config := makeDiscoveryConfig(ctx) ln, config := makeDiscoveryConfig(ctx)
socket := listen(ln, ctx.String(listenAddrFlag.Name)) socket := listen(ctx, ln)
disc, err := discover.ListenV4(socket, ln, config) disc, err := discover.ListenV4(socket, ln, config)
if err != nil { if err != nil {
exit(err) exit(err)
@ -266,7 +272,28 @@ func makeDiscoveryConfig(ctx *cli.Context) (*enode.LocalNode, discover.Config) {
return ln, cfg return ln, cfg
} }
func listen(ln *enode.LocalNode, addr string) *net.UDPConn { func parseExtAddr(spec string) (ip net.IP, port int, ok bool) {
ip = net.ParseIP(spec)
if ip != nil {
return ip, 0, true
}
host, portstr, err := net.SplitHostPort(spec)
if err != nil {
return nil, 0, false
}
ip = net.ParseIP(host)
if ip == nil {
return nil, 0, false
}
port, err = strconv.Atoi(portstr)
if err != nil {
return nil, 0, false
}
return ip, port, true
}
func listen(ctx *cli.Context, ln *enode.LocalNode) *net.UDPConn {
addr := ctx.String(listenAddrFlag.Name)
if addr == "" { if addr == "" {
addr = "0.0.0.0:0" addr = "0.0.0.0:0"
} }
@ -274,6 +301,8 @@ func listen(ln *enode.LocalNode, addr string) *net.UDPConn {
if err != nil { if err != nil {
exit(err) exit(err)
} }
// Configure UDP endpoint in ENR from listener address.
usocket := socket.(*net.UDPConn) usocket := socket.(*net.UDPConn)
uaddr := socket.LocalAddr().(*net.UDPAddr) uaddr := socket.LocalAddr().(*net.UDPAddr)
if uaddr.IP.IsUnspecified() { if uaddr.IP.IsUnspecified() {
@ -282,6 +311,22 @@ func listen(ln *enode.LocalNode, addr string) *net.UDPConn {
ln.SetFallbackIP(uaddr.IP) ln.SetFallbackIP(uaddr.IP)
} }
ln.SetFallbackUDP(uaddr.Port) ln.SetFallbackUDP(uaddr.Port)
// If an ENR endpoint is set explicitly on the command-line, override
// the information from the listening address. Note this is careful not
// to set the UDP port if the external address doesn't have it.
extAddr := ctx.String(extAddrFlag.Name)
if extAddr != "" {
ip, port, ok := parseExtAddr(extAddr)
if !ok {
exit(fmt.Errorf("-%s: invalid external address %q", extAddrFlag.Name, extAddr))
}
ln.SetStaticIP(ip)
if port != 0 {
ln.SetFallbackUDP(port)
}
}
return usocket return usocket
} }

View File

@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/devp2p/internal/v5test" "github.com/ethereum/go-ethereum/cmd/devp2p/internal/v5test"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -42,18 +43,21 @@ var (
Name: "ping", Name: "ping",
Usage: "Sends ping to a node", Usage: "Sends ping to a node",
Action: discv5Ping, Action: discv5Ping,
Flags: discoveryNodeFlags,
} }
discv5ResolveCommand = &cli.Command{ discv5ResolveCommand = &cli.Command{
Name: "resolve", Name: "resolve",
Usage: "Finds a node in the DHT", Usage: "Finds a node in the DHT",
Action: discv5Resolve, Action: discv5Resolve,
Flags: []cli.Flag{bootnodesFlag}, Flags: discoveryNodeFlags,
} }
discv5CrawlCommand = &cli.Command{ discv5CrawlCommand = &cli.Command{
Name: "crawl", Name: "crawl",
Usage: "Updates a nodes.json file with random nodes found in the DHT", Usage: "Updates a nodes.json file with random nodes found in the DHT",
Action: discv5Crawl, Action: discv5Crawl,
Flags: []cli.Flag{bootnodesFlag, crawlTimeoutFlag}, Flags: flags.Merge(discoveryNodeFlags, []cli.Flag{
crawlTimeoutFlag,
}),
} }
discv5TestCommand = &cli.Command{ discv5TestCommand = &cli.Command{
Name: "test", Name: "test",
@ -70,12 +74,7 @@ var (
Name: "listen", Name: "listen",
Usage: "Runs a node", Usage: "Runs a node",
Action: discv5Listen, Action: discv5Listen,
Flags: []cli.Flag{ Flags: discoveryNodeFlags,
bootnodesFlag,
nodekeyFlag,
nodedbFlag,
listenAddrFlag,
},
} }
) )
@ -137,7 +136,7 @@ func discv5Listen(ctx *cli.Context) error {
// startV5 starts an ephemeral discovery v5 node. // startV5 starts an ephemeral discovery v5 node.
func startV5(ctx *cli.Context) *discover.UDPv5 { func startV5(ctx *cli.Context) *discover.UDPv5 {
ln, config := makeDiscoveryConfig(ctx) ln, config := makeDiscoveryConfig(ctx)
socket := listen(ln, ctx.String(listenAddrFlag.Name)) socket := listen(ctx, ln)
disc, err := discover.ListenV5(socket, ln, config) disc, err := discover.ListenV5(socket, ln, config)
if err != nil { if err != nil {
exit(err) exit(err)