diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 2638560db2..52d48d9b9b 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -20,6 +20,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "os" "path/filepath" "regexp" @@ -517,19 +518,21 @@ func importHistory(ctx *cli.Context) error { network = networks[0] } - format := ctx.String(utils.EraFormatFlag.Name) + var ( + format = ctx.String(utils.EraFormatFlag.Name) + from func(era.ReadAtSeekCloser) (era.Era, error) + ) switch format { case "era1", "era": - if err := utils.ImportHistory(chain, dir, network, onedb.From, onedb.NewIterator); err != nil { - return err - } + from = onedb.From case "erae": - if err := utils.ImportHistory(chain, dir, network, execdb.From, execdb.NewIterator); err != nil { - return err - } + from = execdb.From default: return fmt.Errorf("unknown --era.format %q (expected 'era1' or 'erae')", format) } + if err := utils.ImportHistory(chain, dir, network, from); err != nil { + return err + } fmt.Printf("Import done in %v\n", time.Since(start)) return nil @@ -562,19 +565,24 @@ func exportHistory(ctx *cli.Context) error { utils.Fatalf("Export error: block number %d larger than head block %d\n", uint64(last), head.Number.Uint64()) } - format := ctx.String(utils.EraFormatFlag.Get(ctx)) + var ( + format = ctx.String(utils.EraFormatFlag.Get(ctx)) + filename func(network string, epoch int, root common.Hash) string + newBuilder func(w io.Writer) era.Builder + ) switch format { case "era1", "era": - if err := utils.ExportHistory(chain, dir, uint64(first), uint64(last), uint64(era.MaxSize), onedb.NewBuilder, onedb.Filename); err != nil { - utils.Fatalf("Export error: %v\n", err) - } + newBuilder = func(w io.Writer) era.Builder { return onedb.NewBuilder(w) } + filename = func(network string, epoch int, root common.Hash) string { return onedb.Filename(network, epoch, root) } case "erae": - if err := utils.ExportHistory(chain, dir, uint64(first), uint64(last), uint64(era.MaxSize), execdb.NewBuilder, execdb.Filename); err != nil { - utils.Fatalf("Export error: %v\n", err) - } + newBuilder = func(w io.Writer) era.Builder { return execdb.NewBuilder(w) } + filename = func(network string, epoch int, root common.Hash) string { return execdb.Filename(network, epoch, root) } default: return fmt.Errorf("unknown archive format %q (use 'era1' or 'erae')", format) } + if err := utils.ExportHistory(chain, dir, uint64(first), uint64(last), uint64(era.MaxSize), newBuilder, filename); err != nil { + utils.Fatalf("Export error: %v\n", err) + } fmt.Printf("Export done in %v\n", time.Since(start)) return nil diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index 2be60426d5..c0fefbbb40 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -253,7 +253,7 @@ func readList(filename string) ([]string, error) { // ImportHistory imports Era1 files containing historical block information, // starting from genesis. The assumption is held that the provided chain // segment in Era1 file should all be canonical and verified. -func ImportHistory(chain *core.BlockChain, dir string, network string, from era.FromFn, iterator era.NewIteratorFn) error { +func ImportHistory(chain *core.BlockChain, dir string, network string, from func(f era.ReadAtSeekCloser) (era.Era, error)) error { if chain.CurrentSnapBlock().Number.BitLen() != 0 { return errors.New("history import only supported when starting from genesis") } @@ -305,7 +305,7 @@ func ImportHistory(chain *core.BlockChain, dir string, network string, from era. if err != nil { return fmt.Errorf("error opening era: %w", err) } - it, err := iterator(e) + it, err := e.Iterator() if err != nil { return fmt.Errorf("error creating iterator: %w", err) } @@ -417,7 +417,7 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las // ExportHistory exports blockchain history into the specified directory, // following the Era format. -func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64, builderfn era.NewBuilderFn, filename era.FilenameFn) error { +func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64, newBuilder func(io.Writer) era.Builder, filename func(network string, epoch int, root common.Hash) string) error { log.Info("Exporting blockchain history", "dir", dir) if head := bc.CurrentBlock().Number.Uint64(); head < last { log.Warn("Last block beyond head, setting last = head", "head", head, "last", last) @@ -455,7 +455,7 @@ func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64, bu } defer fh.Close() - bldr := builderfn(fh) + bldr := newBuilder(fh) for j := uint64(0); j < step && batch+j <= last; j++ { n := batch + j diff --git a/cmd/utils/history_test.go b/cmd/utils/history_test.go index 14e41795a8..b1e7bdf961 100644 --- a/cmd/utils/history_test.go +++ b/cmd/utils/history_test.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/internal/era" "github.com/ethereum/go-ethereum/internal/era/onedb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" @@ -89,7 +90,7 @@ func TestHistoryImportAndExport(t *testing.T) { dir := t.TempDir() // Export history to temp directory. - if err := ExportHistory(chain, dir, 0, count, step); err != nil { + if err := ExportHistory(chain, dir, 0, count, step, onedb.NewBuilder, onedb.Filename); err != nil { t.Fatalf("error exporting history: %v", err) } @@ -101,7 +102,7 @@ func TestHistoryImportAndExport(t *testing.T) { checksums := strings.Split(string(b), "\n") // Verify each Era. - entries, _ := onedb.ReadDir(dir, "mainnet") + entries, _ := era.ReadDir(dir, "mainnet") for i, filename := range entries { func() { f, err := os.Open(filepath.Join(dir, filename)) @@ -170,7 +171,7 @@ func TestHistoryImportAndExport(t *testing.T) { if err != nil { t.Fatalf("unable to initialize chain: %v", err) } - if err := ImportHistory(imported, dir, "mainnet"); err != nil { + if err := ImportHistory(imported, dir, "mainnet", onedb.From); err != nil { t.Fatalf("failed to import chain: %v", err) } if have, want := imported.CurrentHeader(), chain.CurrentHeader(); have.Hash() != want.Hash() { diff --git a/internal/era/era.go b/internal/era/era.go index 14811939a6..6f41b3097b 100644 --- a/internal/era/era.go +++ b/internal/era/era.go @@ -59,45 +59,34 @@ type Era interface { Close() error Start() uint64 Count() uint64 + Iterator() (Iterator, error) GetBlockByNumber(num uint64) (*types.Block, error) GetRawBodyByNumber(num uint64) ([]byte, error) GetRawReceiptsByNumber(num uint64) ([]byte, error) } -// NewBuilderFn defines a function type for creating a new Builder. -type NewBuilderFn func(w io.Writer) Builder - -// FilenameFn defines a function type for generating a filename based on network, epoch, and root hash. -type FilenameFn func(network string, epoch int, root common.Hash) string - -// FromFn defines a function type for creating an Era from a ReadAtSeekCloser. -type FromFn func(f ReadAtSeekCloser) (Era, error) - -// NewIteratorFn defines a function type for creating a new Iterator from an Era. -type NewIteratorFn func(e Era) (Iterator, error) - // ReadDir reads all the era1 files in a directory for a given network. // Format: --.erae or --.era1 func ReadDir(dir, network string) ([]string, error) { entries, err := os.ReadDir(dir) - var directoryExtension string + if err != nil { return nil, fmt.Errorf("error reading directory %s: %w", dir, err) } var ( - next = uint64(0) - eras []string + next = uint64(0) + eras []string + dirType string ) - for i, entry := range entries { - fileExtension := path.Ext(entry.Name()) - if i == 0 { - directoryExtension = fileExtension - } else if directoryExtension != fileExtension { - return nil, fmt.Errorf("directory %s contains mixed era file formats", dir) - } - if fileExtension != ".erae" || fileExtension != ".era1" { + fmt.Println("entries", entries) + for _, entry := range entries { + ext := path.Ext(entry.Name()) + if ext != ".erae" && ext != ".era1" { continue } + if dirType == "" { + dirType = ext + } parts := strings.Split(entry.Name(), "-") if len(parts) != 3 || parts[0] != network { // Invalid era1 filename, skip. @@ -108,6 +97,9 @@ func ReadDir(dir, network string) ([]string, error) { } else if epoch != next { return nil, fmt.Errorf("missing epoch %d", next) } + if dirType != ext { + return nil, fmt.Errorf("directory %s contains mixed era file formats: want %s, have %s", dir, dirType, ext) + } next += 1 eras = append(eras, entry.Name()) } diff --git a/internal/era/execdb/builder.go b/internal/era/execdb/builder.go index 047986a17c..a708998681 100644 --- a/internal/era/execdb/builder.go +++ b/internal/era/execdb/builder.go @@ -89,7 +89,6 @@ type Builder struct { written uint64 expectsProofs bool isPreMerge bool - numPreMerge int // number of pre-merge blocks finalTD *big.Int // final total difficulty, used for pre-merge and merge straddling files } diff --git a/internal/era/execdb/reader.go b/internal/era/execdb/reader.go index 30c99a46be..9687e5dae3 100644 --- a/internal/era/execdb/reader.go +++ b/internal/era/execdb/reader.go @@ -88,6 +88,11 @@ func (e *Era) Count() uint64 { return e.m.count } +// Iterator returns an iterator over the era file. +func (e *Era) Iterator() (era.Iterator, error) { + return NewIterator(e) +} + // GetBlockByNumber retrieves the block if present within the era file. func (e *Era) GetBlockByNumber(blockNum uint64) (*types.Block, error) { h, err := e.GetHeader(blockNum) diff --git a/internal/era/onedb/reader.go b/internal/era/onedb/reader.go index 1748627850..df93ca8211 100644 --- a/internal/era/onedb/reader.go +++ b/internal/era/onedb/reader.go @@ -81,6 +81,11 @@ func (e *Era) Close() error { return e.f.Close() } +// Iterator returns an iterator over the era file. +func (e *Era) Iterator() (era.Iterator, error) { + return NewIterator(e) +} + // GetBlockByNumber returns the block for the given block number. func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) { if e.m.start > num || e.m.start+e.m.count <= num { diff --git a/internal/era/proof.go b/internal/era/proof.go index 667379bb05..3c75aa5c6e 100644 --- a/internal/era/proof.go +++ b/internal/era/proof.go @@ -110,7 +110,3 @@ func (p *BlockProofHistoricalSummariesDeneb) EncodeRLP(w io.Writer) error { // Variant returns the variant type of the BlockProofHistoricalSummariesDeneb. func (*BlockProofHistoricalSummariesDeneb) Variant() variant { return proofDeneb } - -func variantOf(p Proof) variant { - return p.Variant() -}