From dfa3bbffae66589bd7869f0be8f8f675386b3b13 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 6 May 2026 18:07:06 +0200 Subject: [PATCH] cmd/devp2p: add --mode flag selecting crawl iterator Wire the new discover.CrawlIterator into devp2p discv4/discv5 crawl behind a --mode flag (default 'lookup', i.e. existing behaviour). devp2p discv4 crawl --mode=fast --timeout 30s nodes.json devp2p discv5 crawl --mode=fast --timeout 30s nodes.json Smoke test against mainnet bootnodes for 30s on a residential link yields ~2.4x more nodes under --mode=fast (587 vs 240 in one run), with the new per-tick LogDist log showing a much more uniform distribution of query distances. Workers default to the existing --parallel value (16); pacing is RTT-driven. The 'lookup' default keeps existing behaviour byte-identical for any operator running the saved devp2p discv4 crawl from a script. --- cmd/devp2p/discv4cmd.go | 29 ++++++++++++++++++++++++++--- cmd/devp2p/discv5cmd.go | 23 ++++++++++++++++++++++- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/cmd/devp2p/discv4cmd.go b/cmd/devp2p/discv4cmd.go index 84c7ef0c44..388e0c523a 100644 --- a/cmd/devp2p/discv4cmd.go +++ b/cmd/devp2p/discv4cmd.go @@ -91,7 +91,7 @@ var ( Name: "crawl", Usage: "Updates a nodes.json file with random nodes found in the DHT", Action: discv4Crawl, - Flags: slices.Concat(discoveryNodeFlags, []cli.Flag{crawlTimeoutFlag, crawlParallelismFlag}), + Flags: slices.Concat(discoveryNodeFlags, []cli.Flag{crawlTimeoutFlag, crawlParallelismFlag, crawlModeFlag}), } discv4TestCommand = &cli.Command{ Name: "test", @@ -135,9 +135,14 @@ var ( } crawlParallelismFlag = &cli.IntFlag{ Name: "parallel", - Usage: "How many parallel discoveries to attempt.", + Usage: "How many parallel discoveries to attempt. Used both as the crawler harness's RequestENR worker count and (under -mode=fast) as the FINDNODE iterator's worker count.", Value: 16, } + crawlModeFlag = &cli.StringFlag{ + Name: "mode", + Usage: "Crawl iterator mode: 'lookup' (alpha-bounded Kademlia lookup) or 'fast' (one FINDNODE per peer with rotating prefix; sized by -parallel).", + Value: "lookup", + } remoteEnodeFlag = &cli.StringFlag{ Name: "remote", Usage: "Enode of the remote node under test", @@ -259,7 +264,11 @@ func discv4Crawl(ctx *cli.Context) error { disc, config := startV4(ctx) defer disc.Close() - c, err := newCrawler(inputSet, config.Bootnodes, disc, disc.RandomNodes()) + iter, err := newDiscv4CrawlIterator(disc, config.Bootnodes, ctx.String(crawlModeFlag.Name), ctx.Int(crawlParallelismFlag.Name)) + if err != nil { + return err + } + c, err := newCrawler(inputSet, config.Bootnodes, disc, iter) if err != nil { return err } @@ -269,6 +278,20 @@ func discv4Crawl(ctx *cli.Context) error { return nil } +func newDiscv4CrawlIterator(disc *discover.UDPv4, bootnodes []*enode.Node, mode string, parallel int) (enode.Iterator, error) { + switch mode { + case "", "lookup": + return disc.RandomNodes(), nil + case "fast": + return disc.CrawlIterator(discover.CrawlOptions{ + Workers: parallel, + Seeds: bootnodes, + }), nil + default: + return nil, fmt.Errorf("unknown -%s value %q (want 'lookup' or 'fast')", crawlModeFlag.Name, mode) + } +} + // discv4Test runs the protocol test suite. func discv4Test(ctx *cli.Context) error { // Configure test package globals. diff --git a/cmd/devp2p/discv5cmd.go b/cmd/devp2p/discv5cmd.go index dd253dd082..7497cccc55 100644 --- a/cmd/devp2p/discv5cmd.go +++ b/cmd/devp2p/discv5cmd.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/cmd/devp2p/internal/v5test" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/enode" "github.com/urfave/cli/v2" ) @@ -58,6 +59,8 @@ var ( Action: discv5Crawl, Flags: slices.Concat(discoveryNodeFlags, []cli.Flag{ crawlTimeoutFlag, + crawlParallelismFlag, + crawlModeFlag, }), } discv5TestCommand = &cli.Command{ @@ -111,7 +114,11 @@ func discv5Crawl(ctx *cli.Context) error { disc, config := startV5(ctx) defer disc.Close() - c, err := newCrawler(inputSet, config.Bootnodes, disc, disc.RandomNodes()) + iter, err := newDiscv5CrawlIterator(disc, config.Bootnodes, ctx.String(crawlModeFlag.Name), ctx.Int(crawlParallelismFlag.Name)) + if err != nil { + return err + } + c, err := newCrawler(inputSet, config.Bootnodes, disc, iter) if err != nil { return err } @@ -121,6 +128,20 @@ func discv5Crawl(ctx *cli.Context) error { return nil } +func newDiscv5CrawlIterator(disc *discover.UDPv5, bootnodes []*enode.Node, mode string, parallel int) (enode.Iterator, error) { + switch mode { + case "", "lookup": + return disc.RandomNodes(), nil + case "fast": + return disc.CrawlIterator(discover.CrawlOptions{ + Workers: parallel, + Seeds: bootnodes, + }), nil + default: + return nil, fmt.Errorf("unknown -%s value %q (want 'lookup' or 'fast')", crawlModeFlag.Name, mode) + } +} + // discv5Test runs the protocol test suite. func discv5Test(ctx *cli.Context) error { suite := &v5test.Suite{