Skip to content

Commit f458188

Browse files
fjlDargon789
authored andcommitted
cmd/geth, internal/era/eradl: add era1 downloader tool (ethereum#31823)
This adds a geth subcommand for downloading era1 files and placing them into the correct location. The tool can be used even while geth is already running on the datadir. Downloads are checked against a hard-coded list of checksums for mainnet and sepolia. ``` ./geth download-era --server $SERVER --block 333333 ./geth download-era --server $SERVER --block 333333-444444 ./geth download-era --server $SERVER --epoch 0-10 ./geth download-era --server $SERVER --all ``` The implementation reuses the file downloader we already had for fetching build tools. I've done some refactoring on it to make sure it can support the new use case, and there are some changes to the build here as well.
1 parent 7a76f93 commit f458188

File tree

9 files changed

+2642
-236
lines changed

9 files changed

+2642
-236
lines changed

build/ci.go

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import (
5959
"github.com/cespare/cp"
6060
"github.com/ethereum/go-ethereum/crypto/signify"
6161
"github.com/ethereum/go-ethereum/internal/build"
62+
"github.com/ethereum/go-ethereum/internal/download"
6263
"github.com/ethereum/go-ethereum/internal/version"
6364
)
6465

@@ -190,7 +191,7 @@ func doInstall(cmdline []string) {
190191
// Configure the toolchain.
191192
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
192193
if *dlgo {
193-
csdb := build.MustLoadChecksums("build/checksums.txt")
194+
csdb := download.MustLoadChecksums("build/checksums.txt")
194195
tc.Root = build.DownloadGo(csdb)
195196
}
196197
// Disable CLI markdown doc generation in release builds.
@@ -285,7 +286,7 @@ func doTest(cmdline []string) {
285286
flag.CommandLine.Parse(cmdline)
286287

287288
// Get test fixtures.
288-
csdb := build.MustLoadChecksums("build/checksums.txt")
289+
csdb := download.MustLoadChecksums("build/checksums.txt")
289290
downloadSpecTestFixtures(csdb, *cachedir)
290291

291292
// Configure the toolchain.
@@ -329,16 +330,11 @@ func doTest(cmdline []string) {
329330
}
330331

331332
// downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures.
332-
func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
333-
executionSpecTestsVersion, err := build.Version(csdb, "spec-tests")
334-
if err != nil {
335-
log.Fatal(err)
336-
}
333+
func downloadSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string {
337334
ext := ".tar.gz"
338335
base := "fixtures_pectra-devnet-6" // TODO(s1na) rename once the version becomes part of the filename
339-
url := fmt.Sprintf("https://github.com/ethereum/execution-spec-tests/releases/download/%s/%s%s", executionSpecTestsVersion, base, ext)
340336
archivePath := filepath.Join(cachedir, base+ext)
341-
if err := csdb.DownloadFile(url, archivePath); err != nil {
337+
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
342338
log.Fatal(err)
343339
}
344340
if err := build.ExtractArchive(archivePath, executionSpecTestsDir); err != nil {
@@ -444,24 +440,22 @@ func doLint(cmdline []string) {
444440

445441
// downloadLinter downloads and unpacks golangci-lint.
446442
func downloadLinter(cachedir string) string {
447-
csdb := build.MustLoadChecksums("build/checksums.txt")
448-
version, err := build.Version(csdb, "golangci")
443+
csdb := download.MustLoadChecksums("build/checksums.txt")
444+
version, err := csdb.FindVersion("golangci")
449445
if err != nil {
450446
log.Fatal(err)
451447
}
452448
arch := runtime.GOARCH
453449
ext := ".tar.gz"
454-
455450
if runtime.GOOS == "windows" {
456451
ext = ".zip"
457452
}
458453
if arch == "arm" {
459454
arch += "v" + os.Getenv("GOARM")
460455
}
461456
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, arch)
462-
url := fmt.Sprintf("https://github.com/golangci/golangci-lint/releases/download/v%s/%s%s", version, base, ext)
463457
archivePath := filepath.Join(cachedir, base+ext)
464-
if err := csdb.DownloadFile(url, archivePath); err != nil {
458+
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
465459
log.Fatal(err)
466460
}
467461
if err := build.ExtractArchive(archivePath, cachedir); err != nil {
@@ -497,8 +491,8 @@ func protocArchiveBaseName() (string, error) {
497491
// in the generate command. It returns the full path of the directory
498492
// containing the 'protoc-gen-go' executable.
499493
func downloadProtocGenGo(cachedir string) string {
500-
csdb := build.MustLoadChecksums("build/checksums.txt")
501-
version, err := build.Version(csdb, "protoc-gen-go")
494+
csdb := download.MustLoadChecksums("build/checksums.txt")
495+
version, err := csdb.FindVersion("protoc-gen-go")
502496
if err != nil {
503497
log.Fatal(err)
504498
}
@@ -510,10 +504,8 @@ func downloadProtocGenGo(cachedir string) string {
510504
archiveName += ".tar.gz"
511505
}
512506

513-
url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf-go/releases/download/v%s/%s", version, archiveName)
514-
515507
archivePath := path.Join(cachedir, archiveName)
516-
if err := csdb.DownloadFile(url, archivePath); err != nil {
508+
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
517509
log.Fatal(err)
518510
}
519511
extractDest := filepath.Join(cachedir, baseName)
@@ -531,8 +523,8 @@ func downloadProtocGenGo(cachedir string) string {
531523
// files as a CI step. It returns the full path to the directory containing
532524
// the protoc executable.
533525
func downloadProtoc(cachedir string) string {
534-
csdb := build.MustLoadChecksums("build/checksums.txt")
535-
version, err := build.Version(csdb, "protoc")
526+
csdb := download.MustLoadChecksums("build/checksums.txt")
527+
version, err := csdb.FindVersion("protoc")
536528
if err != nil {
537529
log.Fatal(err)
538530
}
@@ -543,10 +535,8 @@ func downloadProtoc(cachedir string) string {
543535

544536
fileName := fmt.Sprintf("protoc-%s-%s", version, baseName)
545537
archiveFileName := fileName + ".zip"
546-
url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf/releases/download/v%s/%s", version, archiveFileName)
547538
archivePath := filepath.Join(cachedir, archiveFileName)
548-
549-
if err := csdb.DownloadFile(url, archivePath); err != nil {
539+
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
550540
log.Fatal(err)
551541
}
552542
extractDest := filepath.Join(cachedir, fileName)
@@ -826,18 +816,17 @@ func doDebianSource(cmdline []string) {
826816
// downloadGoBootstrapSources downloads the Go source tarball(s) that will be used
827817
// to bootstrap the builder Go.
828818
func downloadGoBootstrapSources(cachedir string) []string {
829-
csdb := build.MustLoadChecksums("build/checksums.txt")
819+
csdb := download.MustLoadChecksums("build/checksums.txt")
830820

831821
var bundles []string
832822
for _, booter := range []string{"ppa-builder-1.19", "ppa-builder-1.21", "ppa-builder-1.23"} {
833-
gobootVersion, err := build.Version(csdb, booter)
823+
gobootVersion, err := csdb.FindVersion(booter)
834824
if err != nil {
835825
log.Fatal(err)
836826
}
837827
file := fmt.Sprintf("go%s.src.tar.gz", gobootVersion)
838-
url := "https://dl.google.com/go/" + file
839828
dst := filepath.Join(cachedir, file)
840-
if err := csdb.DownloadFile(url, dst); err != nil {
829+
if err := csdb.DownloadFileFromKnownURL(dst); err != nil {
841830
log.Fatal(err)
842831
}
843832
bundles = append(bundles, dst)
@@ -847,15 +836,14 @@ func downloadGoBootstrapSources(cachedir string) []string {
847836

848837
// downloadGoSources downloads the Go source tarball.
849838
func downloadGoSources(cachedir string) string {
850-
csdb := build.MustLoadChecksums("build/checksums.txt")
851-
dlgoVersion, err := build.Version(csdb, "golang")
839+
csdb := download.MustLoadChecksums("build/checksums.txt")
840+
dlgoVersion, err := csdb.FindVersion("golang")
852841
if err != nil {
853842
log.Fatal(err)
854843
}
855844
file := fmt.Sprintf("go%s.src.tar.gz", dlgoVersion)
856-
url := "https://dl.google.com/go/" + file
857845
dst := filepath.Join(cachedir, file)
858-
if err := csdb.DownloadFile(url, dst); err != nil {
846+
if err := csdb.DownloadFileFromKnownURL(dst); err != nil {
859847
log.Fatal(err)
860848
}
861849
return dst
@@ -1181,5 +1169,6 @@ func doPurge(cmdline []string) {
11811169
}
11821170

11831171
func doSanityCheck() {
1184-
build.DownloadAndVerifyChecksums(build.MustLoadChecksums("build/checksums.txt"))
1172+
csdb := download.MustLoadChecksums("build/checksums.txt")
1173+
csdb.DownloadAndVerifyAll()
11851174
}

cmd/geth/chaincmd.go

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ import (
2121
"errors"
2222
"fmt"
2323
"os"
24+
"path/filepath"
25+
"regexp"
2426
"runtime"
2527
"slices"
2628
"strconv"
29+
"strings"
2730
"sync/atomic"
2831
"time"
2932

@@ -39,6 +42,7 @@ import (
3942
"github.com/ethereum/go-ethereum/ethdb"
4043
"github.com/ethereum/go-ethereum/internal/debug"
4144
"github.com/ethereum/go-ethereum/internal/era"
45+
"github.com/ethereum/go-ethereum/internal/era/eradl"
4246
"github.com/ethereum/go-ethereum/internal/flags"
4347
"github.com/ethereum/go-ethereum/log"
4448
"github.com/ethereum/go-ethereum/params"
@@ -190,7 +194,7 @@ This command dumps out the state for a given block (or latest, if none provided)
190194
`,
191195
}
192196

193-
pruneCommand = &cli.Command{
197+
pruneHistoryCommand = &cli.Command{
194198
Action: pruneHistory,
195199
Name: "prune-history",
196200
Usage: "Prune blockchain history (block bodies and receipts) up to the merge block",
@@ -201,6 +205,42 @@ The prune-history command removes historical block bodies and receipts from the
201205
blockchain database up to the merge block, while preserving block headers. This
202206
helps reduce storage requirements for nodes that don't need full historical data.`,
203207
}
208+
209+
downloadEraCommand = &cli.Command{
210+
Action: downloadEra,
211+
Name: "download-era",
212+
Usage: "Fetches era1 files (pre-merge history) from an HTTP endpoint",
213+
ArgsUsage: "",
214+
Flags: slices.Concat(
215+
utils.DatabaseFlags,
216+
utils.NetworkFlags,
217+
[]cli.Flag{
218+
eraBlockFlag,
219+
eraEpochFlag,
220+
eraAllFlag,
221+
eraServerFlag,
222+
},
223+
),
224+
}
225+
)
226+
227+
var (
228+
eraBlockFlag = &cli.StringFlag{
229+
Name: "block",
230+
Usage: "Block number to fetch. (can also be a range <start>-<end>)",
231+
}
232+
eraEpochFlag = &cli.StringFlag{
233+
Name: "epoch",
234+
Usage: "Epoch number to fetch (can also be a range <start>-<end>)",
235+
}
236+
eraAllFlag = &cli.BoolFlag{
237+
Name: "all",
238+
Usage: "Download all available era1 files",
239+
}
240+
eraServerFlag = &cli.StringFlag{
241+
Name: "server",
242+
Usage: "era1 server URL",
243+
}
204244
)
205245

206246
// initGenesis will initialise the given JSON format genesis file and writes it as
@@ -665,3 +705,83 @@ func pruneHistory(ctx *cli.Context) error {
665705

666706
return nil
667707
}
708+
709+
// downladEra is the era1 file downloader tool.
710+
func downloadEra(ctx *cli.Context) error {
711+
flags.CheckExclusive(ctx, eraBlockFlag, eraEpochFlag, eraAllFlag)
712+
713+
// Resolve the network.
714+
var network = "mainnet"
715+
if utils.IsNetworkPreset(ctx) {
716+
switch {
717+
case ctx.IsSet(utils.MainnetFlag.Name):
718+
case ctx.IsSet(utils.SepoliaFlag.Name):
719+
network = "sepolia"
720+
default:
721+
return fmt.Errorf("unsupported network, no known era1 checksums")
722+
}
723+
}
724+
725+
// Resolve the destination directory.
726+
stack, _ := makeConfigNode(ctx)
727+
defer stack.Close()
728+
ancients := stack.ResolveAncient("chaindata", "")
729+
dir := filepath.Join(ancients, "era")
730+
731+
baseURL := ctx.String(eraServerFlag.Name)
732+
if baseURL == "" {
733+
return fmt.Errorf("need --%s flag to download", eraServerFlag.Name)
734+
}
735+
736+
l, err := eradl.New(baseURL, network)
737+
if err != nil {
738+
return err
739+
}
740+
switch {
741+
case ctx.IsSet(eraAllFlag.Name):
742+
return l.DownloadAll(dir)
743+
744+
case ctx.IsSet(eraBlockFlag.Name):
745+
s := ctx.String(eraBlockFlag.Name)
746+
start, end, ok := parseRange(s)
747+
if !ok {
748+
return fmt.Errorf("invalid block range: %q", s)
749+
}
750+
return l.DownloadBlockRange(start, end, dir)
751+
752+
case ctx.IsSet(eraEpochFlag.Name):
753+
s := ctx.String(eraEpochFlag.Name)
754+
start, end, ok := parseRange(s)
755+
if !ok {
756+
return fmt.Errorf("invalid epoch range: %q", s)
757+
}
758+
return l.DownloadEpochRange(start, end, dir)
759+
760+
default:
761+
return fmt.Errorf("specify one of --%s, --%s, or --%s to download", eraAllFlag.Name, eraBlockFlag.Name, eraEpochFlag.Name)
762+
}
763+
}
764+
765+
func parseRange(s string) (start uint64, end uint64, ok bool) {
766+
if m, _ := regexp.MatchString("[0-9]+", s); m {
767+
start, err := strconv.ParseUint(s, 10, 64)
768+
if err != nil {
769+
return 0, 0, false
770+
}
771+
end = start
772+
return start, end, true
773+
}
774+
if m, _ := regexp.MatchString("[0-9]+-[0-9]+", s); m {
775+
s1, s2, _ := strings.Cut(s, "-")
776+
start, err := strconv.ParseUint(s1, 10, 64)
777+
if err != nil {
778+
return 0, 0, false
779+
}
780+
end, err = strconv.ParseUint(s2, 10, 64)
781+
if err != nil {
782+
return 0, 0, false
783+
}
784+
return start, end, true
785+
}
786+
return 0, 0, false
787+
}

cmd/geth/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ func init() {
225225
removedbCommand,
226226
dumpCommand,
227227
dumpGenesisCommand,
228-
pruneCommand,
228+
pruneHistoryCommand,
229+
downloadEraCommand,
229230
// See accountcmd.go:
230231
accountCommand,
231232
walletCommand,

0 commit comments

Comments
 (0)