all cmd changes to pass in functions in chaincmd

This commit is contained in:
shantichanal 2025-07-25 19:58:41 +02:00 committed by lightclient
parent 327655693d
commit 272dcd5c43
No known key found for this signature in database
GPG key ID: 657913021EF45A6A
13 changed files with 131 additions and 209 deletions

View file

@ -170,14 +170,15 @@ func open(ctx *cli.Context, epoch uint64) (*onedb.Era, error) {
dir = ctx.String(dirFlag.Name)
network = ctx.String(networkFlag.Name)
)
entries, err := onedb.ReadDir(dir, network)
entries, err := era.ReadDir(dir, network)
if err != nil {
return nil, fmt.Errorf("error reading era dir: %w", err)
}
if epoch >= uint64(len(entries)) {
return nil, fmt.Errorf("epoch out-of-bounds: last %d, want %d", len(entries)-1, epoch)
}
return onedb.Open(filepath.Join(dir, entries[epoch]))
era, err := onedb.Open(filepath.Join(dir, entries[epoch]))
return era.(*onedb.Era), err
}
// verify checks each era1 file in a directory to ensure it is well-formed and
@ -199,7 +200,7 @@ func verify(ctx *cli.Context) error {
reported = time.Now()
)
entries, err := onedb.ReadDir(dir, network)
entries, err := era.ReadDir(dir, network)
if err != nil {
return fmt.Errorf("error reading %s: %w", dir, err)
}
@ -214,18 +215,19 @@ func verify(ctx *cli.Context) error {
err := func() error {
name := entries[i]
e, err := onedb.Open(filepath.Join(dir, name))
eraPointer := e.(*onedb.Era)
if err != nil {
return fmt.Errorf("error opening era1 file %s: %w", name, err)
}
defer e.Close()
// Read accumulator and check against expected.
if got, err := e.Accumulator(); err != nil {
if got, err := eraPointer.Accumulator(); err != nil {
return fmt.Errorf("error retrieving accumulator for %s: %w", name, err)
} else if got != want {
return fmt.Errorf("invalid root %s: got %s, want %s", name, got, want)
}
// Recompute accumulator.
if err := checkAccumulator(e); err != nil {
if err := checkAccumulator(eraPointer); err != nil {
return fmt.Errorf("error verify era1 file %s: %w", name, err)
}
// Give the user some feedback that something is happening.

View file

@ -43,6 +43,8 @@ import (
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/internal/era"
"github.com/ethereum/go-ethereum/internal/era/eradl"
"github.com/ethereum/go-ethereum/internal/era/execdb"
"github.com/ethereum/go-ethereum/internal/era/onedb"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
@ -518,11 +520,11 @@ func importHistory(ctx *cli.Context) error {
format := ctx.String(utils.EraFormatFlag.Name)
switch format {
case "era1", "era":
if err := utils.ImportHistory(chain, dir, network, utils.Era1); err != nil {
if err := utils.ImportHistory(chain, dir, network, onedb.From, onedb.NewIterator); err != nil {
return err
}
case "erae":
if err := utils.ImportHistory(chain, dir, network, utils.EraE); err != nil {
if err := utils.ImportHistory(chain, dir, network, execdb.From, execdb.NewIterator); err != nil {
return err
}
default:
@ -563,11 +565,11 @@ func exportHistory(ctx *cli.Context) error {
format := ctx.String(utils.EraFormatFlag.Get(ctx))
switch format {
case "era1", "era":
if err := utils.ExportHistory(chain, dir, uint64(first), uint64(last), uint64(era.MaxEra1Size), utils.Era1); err != nil {
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)
}
case "erae":
if err := utils.ExportHistory(chain, dir, uint64(first), uint64(last), uint64(era2.MaxEraESize), utils.EraE); err != nil {
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)
}
default:

View file

@ -44,7 +44,7 @@ import (
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/debug"
era2 "github.com/ethereum/go-ethereum/internal/era/execdb"
"github.com/ethereum/go-ethereum/internal/era"
"github.com/ethereum/go-ethereum/internal/era/onedb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
@ -253,11 +253,11 @@ 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) error {
func ImportHistory(chain *core.BlockChain, dir string, network string, from era.FromFn, iterator era.NewIteratorFn) error {
if chain.CurrentSnapBlock().Number.BitLen() != 0 {
return errors.New("history import only supported when starting from genesis")
}
entries, err := onedb.ReadDir(dir, network)
entries, err := era.ReadDir(dir, network)
if err != nil {
return fmt.Errorf("error reading %s: %w", dir, err)
}
@ -301,11 +301,11 @@ func ImportHistory(chain *core.BlockChain, dir string, network string) error {
return fmt.Errorf("%s checksum mismatch: have %s want %s", file, got, want)
}
// Import all block data from Era1.
e, err := onedb.From(f)
e, err := from(f)
if err != nil {
return fmt.Errorf("error opening era: %w", err)
}
it, err := onedb.NewIterator(e)
it, err := iterator(e)
if err != nil {
return fmt.Errorf("error creating iterator: %w", err)
}
@ -347,105 +347,6 @@ func ImportHistory(chain *core.BlockChain, dir string, network string) error {
return nil
}
// ImportHistoryEraE imports Era-E files containing historical block information,
// starting from genesis. Currently this function follows the assumptions that the provided chain
// segment in Era-E file should all be canonical and verified. In the future, this function will be
// extended to support importing Era-E files with proof verification.
func ImportHistoryEraE(chain *core.BlockChain, dir, network string) error {
if chain.CurrentSnapBlock().Number.Sign() != 0 {
return errors.New("history import only supported when starting from genesis")
}
entries, err := era2.ReadDir(dir, network)
if err != nil {
return fmt.Errorf("reading %q: %w", dir, err)
}
checksums, err := readList(filepath.Join(dir, "checksums.txt"))
if err != nil {
return fmt.Errorf("reading checksums.txt: %w", err)
}
if len(entries) != len(checksums) {
return fmt.Errorf("mismatch: %d erae files, %d checksums", len(entries), len(checksums))
}
var (
start = time.Now()
reported = time.Now()
imported int
h = sha256.New()
scratch bytes.Buffer
)
for i, file := range entries {
path := filepath.Join(dir, file)
// validate against checksum file in directory
f, err := os.Open(path)
if err != nil {
return fmt.Errorf("open %s: %w", path, err)
}
if _, err := io.Copy(h, f); err != nil {
f.Close()
return fmt.Errorf("checksum %s: %w", path, err)
}
got := common.BytesToHash(h.Sum(scratch.Bytes()[:])).Hex()
want := checksums[i]
h.Reset()
scratch.Reset()
if got != want {
f.Close()
return fmt.Errorf("%s checksum mismatch: have %s want %s", file, got, want)
}
// rewind for reading
if _, err := f.Seek(0, io.SeekStart); err != nil {
f.Close()
return fmt.Errorf("rewind %s: %w", file, err)
}
// import archive
e, err := era2.From(f)
if err != nil {
f.Close()
return fmt.Errorf("open erae %s: %w", file, err)
}
blockCount := e.Count()
for j := uint64(0); j < blockCount; j++ {
hdr, err := e.GetHeader(e.Start() + j)
if err != nil {
return fmt.Errorf("header #%d: %w", hdr.Number.Uint64(), err)
}
if hdr.Number.Sign() == 0 { // skip genesis
continue
}
body, err := e.GetBody(hdr.Number.Uint64())
if err != nil {
return fmt.Errorf("body #%d: %w", hdr.Number.Uint64(), err)
}
rcpts, err := e.GetReceipts(hdr.Number.Uint64(), 1)
if err != nil {
return fmt.Errorf("receipts #%d: %w", hdr.Number.Uint64(), err)
}
blk := types.NewBlockWithHeader(hdr).WithBody(*body)
enc := types.EncodeBlockReceiptLists(rcpts)
if _, err := chain.InsertReceiptChain([]*types.Block{blk}, enc, ^uint64(0)); err != nil {
return fmt.Errorf("insert #%d: %w", hdr.Number.Uint64(), err)
}
imported++
if time.Since(reported) >= 8*time.Second {
log.Info("Importing EraE", "head", hdr.Number, "imported", imported, "elapsed", common.PrettyDuration(time.Since(start)))
reported = time.Now()
imported = 0
}
}
f.Close()
}
log.Info("EraE import complete", "duration", common.PrettyDuration(time.Since(start)))
return nil
}
func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block {
head := chain.CurrentBlock()
for i, block := range blocks {
@ -516,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) error {
func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64, builderfn era.NewBuilderFn, filename era.FilenameFn) 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)
@ -545,7 +446,7 @@ func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64) er
for batch := first; batch <= last; batch += step {
idx := int(batch / step)
tmpPath := filepath.Join(dir, onedb.Filename(network, idx, common.Hash{}))
tmpPath := filepath.Join(dir, filename(network, idx, common.Hash{}))
if err := func() error {
fh, err := os.Create(tmpPath)
@ -554,7 +455,7 @@ func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64) er
}
defer fh.Close()
bldr := onedb.NewBuilder(fh)
bldr := builderfn(fh)
for j := uint64(0); j < step && batch+j <= last; j++ {
n := batch + j

View file

@ -308,7 +308,7 @@ func (db *Store) openEraFile(epoch uint64) (*onedb.Era, error) {
return nil, fmt.Errorf("pre-merge era1 file has invalid boundary. %d %% %d != 0", e.Start(), era.MaxSize)
}
log.Debug("Opened era1 file", "epoch", epoch)
return e, nil
return e.(*onedb.Era), nil
}
// doneWithFile signals that the caller has finished using a file.

View file

@ -1,11 +1,17 @@
package era
import (
"fmt"
"io"
"math/big"
"os"
"path"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
// Removed import of internal/era to avoid import cycle and missing metadata error
)
// Type constants for the e2store entries in the Era1 and EraE formats.
@ -36,12 +42,74 @@ type Iterator interface {
Next() bool
Number() uint64
Block() (*types.Block, error)
BlockAndReceipts() (*types.Block, types.Receipts, error)
Receipts() (types.Receipts, error)
Error() error
}
// Builder represents the interface for various types of era formats.
type Builder interface {
Add(block *types.Block, receipts types.Receipts, td *big.Int, proof []byte) error
Add(block *types.Block, receipts types.Receipts, td *big.Int, proof Proof) error
AddRLP(header, body, receipts, proof []byte, number uint64, hash common.Hash, td, difficulty *big.Int) error
Finalize() (common.Hash, error)
}
// Era represents the interface for reading era data.
type Era interface {
Close() error
Start() uint64
Count() uint64
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: <network>-<epoch>-<hexroot>.erae or <network>-<epoch>-<hexroot>.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
)
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" {
continue
}
parts := strings.Split(entry.Name(), "-")
if len(parts) != 3 || parts[0] != network {
// Invalid era1 filename, skip.
continue
}
if epoch, err := strconv.ParseUint(parts[1], 10, 64); err != nil {
return nil, fmt.Errorf("malformed era filenames: %s", entry.Name())
} else if epoch != next {
return nil, fmt.Errorf("missing epoch %d", next)
}
next += 1
eras = append(eras, entry.Name())
}
return eras, nil
}

View file

@ -51,9 +51,11 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/history"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/era"
"github.com/ethereum/go-ethereum/internal/era/e2store"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/golang/snappy"
)
@ -91,7 +93,7 @@ type Builder struct {
}
// NewBuilder returns a new Builder instance.
func NewBuilder(w io.Writer) *Builder {
func NewBuilder(w io.Writer) era.Builder {
tmp := bytes.NewBuffer(nil)
return &Builder{
w: e2store.NewWriter(w),
@ -137,12 +139,15 @@ func (b *Builder) Add(block *types.Block, receipts types.Receipts, td *big.Int,
return b.AddRLP(
eh, eb, er, ep,
header.Number.Uint64(),
header.Hash(), td,
header.Hash(), td, nil,
)
}
// AddRLP takes the RLP encoded block components and writes them to the underlying e2store file.
func (b *Builder) AddRLP(headerRLP []byte, bodyRLP []byte, receipts []byte, proof []byte, blockNum uint64, blockHash common.Hash, td *big.Int) error {
func (b *Builder) AddRLP(headerRLP []byte, bodyRLP []byte, receipts []byte, proof []byte, blockNum uint64, blockHash common.Hash, td, difficulty *big.Int) error {
if difficulty != nil {
return fmt.Errorf("block difficulty not allowed in erae format")
}
if len(b.buff.headers) >= era.MaxSize {
return fmt.Errorf("exceeds max size %d", era.MaxSize)
}
@ -156,6 +161,11 @@ func (b *Builder) AddRLP(headerRLP []byte, bodyRLP []byte, receipts []byte, proo
b.buff.proofs = append(b.buff.proofs, proof)
}
mergeblock := history.PrunePoints[params.MainnetGenesisHash]
if mergeblock != nil && blockNum <= mergeblock.BlockNumber {
b.buff.tds = append(b.buff.tds, new(big.Int).Set(td))
}
// Write Era2 version before writing any blocks.
if b.startNum == nil {
b.startNum = new(uint64)

View file

@ -139,7 +139,7 @@ func TestEra2Builder(t *testing.T) {
t.Fatalf("body %d mismatch", i)
}
rawBody, err := era.GetRawBodyFrameByNumber(bn)
rawBody, err := era.GetRawBodyByNumber(bn)
if err != nil {
t.Fatalf("raw body %d: %v", i, err)
}
@ -153,7 +153,7 @@ func TestEra2Builder(t *testing.T) {
t.Fatalf("body frame %d mismatch", i)
}
rawRcpt, err := era.GetRawReceiptsFrameByNumber(bn)
rawRcpt, err := era.GetRawReceiptsByNumber(bn)
if err != nil {
t.Fatalf("raw receipts %d: %v", i, err)
}

View file

@ -35,8 +35,8 @@ type Iterator struct {
// NewIterator returns a header/body/receipt iterator over the archive.
// Call Next immediately to position on the first block.
func NewIterator(e *Era) (*Iterator, error) {
inner, err := NewRawIterator(e)
func NewIterator(e era.Era) (era.Iterator, error) {
inner, err := NewRawIterator(e.(*Era))
if err != nil {
return nil, err
}

View file

@ -22,10 +22,7 @@ import (
"io"
"math/big"
"os"
"path"
"slices"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
@ -72,7 +69,7 @@ func (e *Era) Close() error {
}
// From returns an Era backed by f.
func From(f era.ReadAtSeekCloser) (*Era, error) {
func From(f era.ReadAtSeekCloser) (era.Era, error) {
e := &Era{f: f, s: e2store.NewReader(f)}
if err := e.loadIndex(); err != nil {
f.Close()
@ -81,37 +78,6 @@ func From(f era.ReadAtSeekCloser) (*Era, error) {
return e, nil
}
// ReadDir reads all the era1 files in a directory for a given network.
// Format: <network>-<epoch>-<hexroot>.erae
func ReadDir(dir, network string) ([]string, error) {
entries, err := os.ReadDir(dir)
if err != nil {
return nil, fmt.Errorf("error reading directory %s: %w", dir, err)
}
var (
next = uint64(0)
eras []string
)
for _, entry := range entries {
if path.Ext(entry.Name()) != ".erae" {
continue
}
parts := strings.Split(entry.Name(), "-")
if len(parts) != 3 || parts[0] != network {
// Invalid era1 filename, skip.
continue
}
if epoch, err := strconv.ParseUint(parts[1], 10, 64); err != nil {
return nil, fmt.Errorf("malformed era1 filename: %s", entry.Name())
} else if epoch != next {
return nil, fmt.Errorf("missing epoch %d", next)
}
next += 1
eras = append(eras, entry.Name())
}
return eras, nil
}
// Start retrieves the starting block number.
func (e *Era) Start() uint64 {
return e.m.start
@ -185,8 +151,8 @@ func (e *Era) getTD(blockNum uint64) (*big.Int, error) {
return td, nil
}
// GetRawBodyFrameByNumber retrieves the raw body frame in bytes of a specific block.
func (e *Era) GetRawBodyFrameByNumber(blockNum uint64) ([]byte, error) {
// GetRawBodyByNumber returns the RLP-encoded body for the given block number.
func (e *Era) GetRawBodyByNumber(blockNum uint64) ([]byte, error) {
off, err := e.bodyOff(blockNum)
if err != nil {
return nil, err
@ -198,8 +164,8 @@ func (e *Era) GetRawBodyFrameByNumber(blockNum uint64) ([]byte, error) {
return io.ReadAll(r)
}
// GetRawReceiptsFrameByNumber retrieves the raw receipts frame in bytes of a specific block.
func (e *Era) GetRawReceiptsFrameByNumber(blockNum uint64) ([]byte, error) {
// GetRawReceiptsByNumber returns the RLP-encoded receipts for the given block number.
func (e *Era) GetRawReceiptsByNumber(blockNum uint64) ([]byte, error) {
off, err := e.receiptOff(blockNum)
if err != nil {
return nil, err
@ -211,7 +177,7 @@ func (e *Era) GetRawReceiptsFrameByNumber(blockNum uint64) ([]byte, error) {
return io.ReadAll(r)
}
// GetRawProofFrameByNumber retrieves the raw proof frame in bytes of a specific block proof.
// GetRawProofFrameByNumber returns the RLP-encoded receipts for the given block number.
func (e *Era) GetRawProofFrameByNumber(blockNum uint64) ([]byte, error) {
off, err := e.proofOff(blockNum)
if err != nil {

View file

@ -86,7 +86,7 @@ type Builder struct {
}
// NewBuilder returns a new Builder instance.
func NewBuilder(w io.Writer) *Builder {
func NewBuilder(w io.Writer) era.Builder {
buf := bytes.NewBuffer(nil)
return &Builder{
w: e2store.NewWriter(w),
@ -113,12 +113,15 @@ func (b *Builder) Add(block *types.Block, receipts types.Receipts, td *big.Int,
if err != nil {
return err
}
return b.AddRLP(eh, eb, er, block.NumberU64(), block.Hash(), td, block.Difficulty())
return b.AddRLP(eh, eb, er, nil, block.NumberU64(), block.Hash(), td, block.Difficulty())
}
// AddRLP writes a compressed block entry and compressed receipts entry to the
// underlying e2store file.
func (b *Builder) AddRLP(header, body, receipts []byte, number uint64, hash common.Hash, td, difficulty *big.Int) error {
func (b *Builder) AddRLP(header, body, receipts, proof []byte, number uint64, hash common.Hash, td, difficulty *big.Int) error {
if proof != nil {
return fmt.Errorf("proof not allowed in era1 format")
}
// Write Era1 version entry before first block.
if b.startNum == nil {
n, err := b.w.Write(era.TypeVersion, nil)

View file

@ -67,7 +67,7 @@ func TestEra1Builder(t *testing.T) {
hash = common.Hash{byte(i)}
td = chain.tds[i]
)
if err = builder.AddRLP(header, body, receipts, uint64(i), hash, td, big.NewInt(1)); err != nil {
if err = builder.AddRLP(header, body, receipts, nil, uint64(i), hash, td, big.NewInt(1)); err != nil {
t.Fatalf("error adding entry: %v", err)
}
}
@ -83,7 +83,11 @@ func TestEra1Builder(t *testing.T) {
t.Fatalf("failed to open era: %v", err)
}
defer e.Close()
it, err := NewRawIterator(e)
eraPtr, ok := e.(*Era)
if !ok {
t.Fatalf("failed to assert *Era type")
}
it, err := NewRawIterator(eraPtr)
if err != nil {
t.Fatalf("failed to make iterator: %s", err)
}
@ -120,7 +124,7 @@ func TestEra1Builder(t *testing.T) {
if !bytes.Equal(rawReceipts, chain.receipts[i]) {
t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], rawReceipts)
}
receipts, err := getReceiptsByNumber(e, i)
receipts, err := getReceiptsByNumber(eraPtr, i)
if err != nil {
t.Fatalf("error reading receipts: %v", err)
}

View file

@ -34,8 +34,8 @@ type Iterator struct {
// NewIterator returns a new Iterator instance. Next must be immediately
// called on new iterators to load the first item.
func NewIterator(e *Era) (*Iterator, error) {
inner, err := NewRawIterator(e)
func NewIterator(e era.Era) (era.Iterator, error) {
inner, err := NewRawIterator(e.(*Era))
if err != nil {
return nil, err
}

View file

@ -22,10 +22,7 @@ import (
"io"
"math/big"
"os"
"path"
"slices"
"strconv"
"strings"
"sync"
"github.com/ethereum/go-ethereum/common"
@ -42,37 +39,6 @@ func Filename(network string, epoch int, root common.Hash) string {
return fmt.Sprintf("%s-%05d-%s.era1", network, epoch, root.Hex()[2:10])
}
// ReadDir reads all the era1 files in a directory for a given network.
// Format: <network>-<epoch>-<hexroot>.era1
func ReadDir(dir, network string) ([]string, error) {
entries, err := os.ReadDir(dir)
if err != nil {
return nil, fmt.Errorf("error reading directory %s: %w", dir, err)
}
var (
next = uint64(0)
eras []string
)
for _, entry := range entries {
if path.Ext(entry.Name()) != ".era1" {
continue
}
parts := strings.Split(entry.Name(), "-")
if len(parts) != 3 || parts[0] != network {
// Invalid era1 filename, skip.
continue
}
if epoch, err := strconv.ParseUint(parts[1], 10, 64); err != nil {
return nil, fmt.Errorf("malformed era1 filename: %s", entry.Name())
} else if epoch != next {
return nil, fmt.Errorf("missing epoch %d", next)
}
next += 1
eras = append(eras, entry.Name())
}
return eras, nil
}
type ReadAtSeekCloser interface {
io.ReaderAt
io.Seeker
@ -89,7 +55,7 @@ type Era struct {
}
// From returns an Era backed by f.
func From(f ReadAtSeekCloser) (*Era, error) {
func From(f era.ReadAtSeekCloser) (era.Era, error) {
m, err := readMetadata(f)
if err != nil {
return nil, err
@ -103,7 +69,7 @@ func From(f ReadAtSeekCloser) (*Era, error) {
}
// Open returns an Era backed by the given filename.
func Open(filename string) (*Era, error) {
func Open(filename string) (era.Era, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err