mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-24 08:49:29 +00:00
internal/era: update to latest ere spec
This commit is contained in:
parent
ea1cf7bf5e
commit
77d19df307
8 changed files with 51 additions and 43 deletions
|
|
@ -183,11 +183,11 @@ func open(ctx *cli.Context, epoch uint64) (era.Era, error) {
|
|||
return openByPath(path)
|
||||
}
|
||||
|
||||
// openByPath tries to open a single file as either eraE or era1 based on extension,
|
||||
// openByPath tries to open a single file as either Ere or Era1 based on extension,
|
||||
// falling back to the other reader if needed.
|
||||
func openByPath(path string) (era.Era, error) {
|
||||
switch strings.ToLower(filepath.Ext(path)) {
|
||||
case ".erae":
|
||||
case ".ere":
|
||||
if e, err := execdb.Open(path); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
|
|
@ -229,7 +229,7 @@ func verify(ctx *cli.Context) error {
|
|||
|
||||
// Build the verification list respecting the rule:
|
||||
// era1: must have accumulator, always verify
|
||||
// erae: verify only if accumulator exists (pre-merge)
|
||||
// ere: verify only if accumulator exists (pre-merge / transition)
|
||||
|
||||
// Build list of files to verify.
|
||||
verify := make([]string, 0, len(entries))
|
||||
|
|
@ -251,15 +251,15 @@ func verify(ctx *cli.Context) error {
|
|||
}
|
||||
verify = append(verify, path)
|
||||
|
||||
case ".erae":
|
||||
case ".ere":
|
||||
e, err := execdb.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening erae file %s: %w", name, err)
|
||||
return fmt.Errorf("error opening ere file %s: %w", name, err)
|
||||
}
|
||||
_, accErr := e.Accumulator()
|
||||
e.Close()
|
||||
if accErr == nil {
|
||||
verify = append(verify, path) // pre-merge only
|
||||
verify = append(verify, path) // pre-merge / transition only
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported era file: %s", name)
|
||||
|
|
|
|||
|
|
@ -533,10 +533,10 @@ func importHistory(ctx *cli.Context) error {
|
|||
switch format {
|
||||
case "era1", "era":
|
||||
from = onedb.From
|
||||
case "erae":
|
||||
case "ere":
|
||||
from = execdb.From
|
||||
default:
|
||||
return fmt.Errorf("unknown --era.format %q (expected 'era1' or 'erae')", format)
|
||||
return fmt.Errorf("unknown --era.format %q (expected 'era1' or 'ere')", format)
|
||||
}
|
||||
if err := utils.ImportHistory(chain, dir, network, from); err != nil {
|
||||
return err
|
||||
|
|
@ -582,11 +582,11 @@ func exportHistory(ctx *cli.Context) error {
|
|||
case "era1", "era":
|
||||
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":
|
||||
case "ere":
|
||||
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)
|
||||
return fmt.Errorf("unknown archive format %q (use 'era1' or 'ere')", format)
|
||||
}
|
||||
if err := utils.ExportHistory(chain, dir, uint64(first), uint64(last), newBuilder, filename); err != nil {
|
||||
utils.Fatalf("Export error: %v\n", err)
|
||||
|
|
|
|||
|
|
@ -1110,7 +1110,7 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
|||
// Era flags are a group of flags related to the era archive format.
|
||||
EraFormatFlag = &cli.StringFlag{
|
||||
Name: "era.format",
|
||||
Usage: "Archive format: 'era1' or 'erae'",
|
||||
Usage: "Archive format: 'era1' or 'ere'",
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func TestHistoryImportAndExport(t *testing.T) {
|
|||
from func(f era.ReadAtSeekCloser) (era.Era, error)
|
||||
}{
|
||||
{"era1", onedb.NewBuilder, onedb.Filename, onedb.From},
|
||||
{"erae", execdb.NewBuilder, execdb.Filename, execdb.From},
|
||||
{"ere", execdb.NewBuilder, execdb.Filename, execdb.From},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// Type constants for the e2store entries in the Era1 and EraE formats.
|
||||
// Type constants for the e2store entries in the Era1 and Ere formats.
|
||||
var (
|
||||
TypeVersion uint16 = 0x3265
|
||||
TypeCompressedHeader uint16 = 0x03
|
||||
|
|
@ -40,10 +40,9 @@ var (
|
|||
TypeCompressedSlimReceipts uint16 = 0x0a // uses eth/69 encoding
|
||||
TypeProof uint16 = 0x0b
|
||||
TypeBlockIndex uint16 = 0x3266
|
||||
TypeComponentIndex uint16 = 0x3267
|
||||
TypeDynamicBlockIndex uint16 = 0x3267
|
||||
|
||||
MaxSize = 8192
|
||||
// headerSize uint64 = 8
|
||||
)
|
||||
|
||||
type ReadAtSeekCloser interface {
|
||||
|
|
@ -93,7 +92,7 @@ type Builder interface {
|
|||
|
||||
// Finalize writes all collected entries and returns the epoch identifier.
|
||||
// For Era1 (onedb): returns the accumulator root.
|
||||
// For EraE (execdb): returns the last block hash.
|
||||
// For Ere (execdb): returns the last block hash.
|
||||
Finalize() (common.Hash, error)
|
||||
|
||||
// Accumulator returns the accumulator root after Finalize has been called.
|
||||
|
|
@ -115,7 +114,7 @@ type Era interface {
|
|||
}
|
||||
|
||||
// ReadDir reads all the era files in a directory for a given network.
|
||||
// Format: <network>-<epoch>-<hexroot>.erae or <network>-<epoch>-<hexroot>.era1
|
||||
// Format: <network>-<epoch>-<hexroot>(-<profile>)*.ere or <network>-<epoch>-<hexroot>.era1
|
||||
func ReadDir(dir, network string) ([]string, error) {
|
||||
entries, err := os.ReadDir(dir)
|
||||
|
||||
|
|
@ -129,14 +128,16 @@ func ReadDir(dir, network string) ([]string, error) {
|
|||
)
|
||||
for _, entry := range entries {
|
||||
ext := path.Ext(entry.Name())
|
||||
if ext != ".erae" && ext != ".era1" {
|
||||
if ext != ".ere" && ext != ".era1" {
|
||||
continue
|
||||
}
|
||||
if dirType == "" {
|
||||
dirType = ext
|
||||
}
|
||||
parts := strings.Split(entry.Name(), "-")
|
||||
if len(parts) != 3 || parts[0] != network {
|
||||
// Ere files may carry an optional profile postfix (e.g. "-noproofs"),
|
||||
// so the filename has at least 3 dash-separated parts.
|
||||
if len(parts) < 3 || parts[0] != network {
|
||||
// Invalid era filename, skip.
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,40 +16,44 @@
|
|||
|
||||
package execdb
|
||||
|
||||
// EraE file format specification.
|
||||
// Ere file format specification.
|
||||
//
|
||||
// See https://github.com/eth-clients/e2store-format-specs/blob/main/formats/ere.md.
|
||||
//
|
||||
// The format can be summarized with the following expression:
|
||||
//
|
||||
// eraE := Version | CompressedHeader* | CompressedBody* | CompressedSlimReceipts* | TotalDifficulty* | other-entries* | Accumulator? | ComponentIndex
|
||||
// ere := Version | CompressedHeader+ | CompressedBody+ | CompressedSlimReceipts* | Proof* | TotalDifficulty* | other-entries* | Accumulator? | DynamicBlockIndex
|
||||
//
|
||||
// Each basic element is its own e2store entry:
|
||||
//
|
||||
// Version = { type: 0x3265, data: nil }
|
||||
// CompressedHeader = { type: 0x03, data: snappyFramed(rlp(header)) }
|
||||
// CompressedBody = { type: 0x04, data: snappyFramed(rlp(body)) }
|
||||
// CompressedSlimReceipts = { type: 0x0a, data: snappyFramed(rlp([tx-type, post-state-or-status, cumulative-gas, logs])) }
|
||||
// TotalDifficulty = { type: 0x06, data: uint256 (header.total_difficulty) }
|
||||
// AccumulatorRoot = { type: 0x07, data: hash_tree_root(List(HeaderRecord, 8192)) }
|
||||
// ComponentIndex = { type: 0x3267, data: component-index }
|
||||
// Version = { type: [0x65, 0x32], data: nil }
|
||||
// CompressedHeader = { type: [0x03, 0x00], data: snappyFramed(rlp(header)) }
|
||||
// CompressedBody = { type: [0x04, 0x00], data: snappyFramed(rlp(body)) }
|
||||
// CompressedSlimReceipts = { type: [0x0a, 0x00], data: snappyFramed(rlp([tx-type, post-state-or-status, cumulative-gas, logs])) }
|
||||
// TotalDifficulty = { type: [0x06, 0x00], data: uint256(header.total_difficulty) }
|
||||
// Proof = { type: [0x0b, 0x00], data: snappyFramed(rlp([proof-type, ssz(proof)])) }
|
||||
// AccumulatorRoot = { type: [0x07, 0x00], data: hash_tree_root(List(HeaderRecord, 8192)) }
|
||||
// DynamicBlockIndex = { type: [0x67, 0x32], data: block-index }
|
||||
//
|
||||
// Notes:
|
||||
// - TotalDifficulty is present for pre-merge and merge transition epochs.
|
||||
// For pure post-merge epochs, TotalDifficulty is omitted entirely.
|
||||
// - In merge transition epochs, post-merge blocks store the final total
|
||||
// difficulty (the TD at which the merge occurred).
|
||||
// - AccumulatorRoot is only written for pre-merge epochs.
|
||||
// - AccumulatorRoot is only written for pre-merge or transition epochs.
|
||||
// - HeaderRecord is defined in the Portal Network specification.
|
||||
// - Proofs (type 0x09) are defined in the spec but not yet supported in this implementation.
|
||||
// - Proof entries are recommended by the spec but not produced by this
|
||||
// implementation; files written here use the "noproofs" profile postfix.
|
||||
//
|
||||
// ComponentIndex stores relative offsets to each block's components:
|
||||
// DynamicBlockIndex stores relative offsets to each block's components:
|
||||
//
|
||||
// component-index := starting-number | indexes | indexes | ... | component-count | count
|
||||
// indexes := header-offset | body-offset | receipts-offset | td-offset?
|
||||
// block-index := starting-number | indexes | indexes | ... | component-count | count
|
||||
// indexes := header-index | body-index | receipts-index? | difficulty-index? | proof-index?
|
||||
//
|
||||
// All values are little-endian uint64.
|
||||
//
|
||||
// Due to the accumulator size limit of 8192, the maximum number of blocks in an
|
||||
// EraE file is also 8192.
|
||||
// Ere file is also 8192.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
@ -67,7 +71,7 @@ import (
|
|||
"github.com/golang/snappy"
|
||||
)
|
||||
|
||||
// Builder is used to build an EraE e2store file. It collects block entries and
|
||||
// Builder is used to build an Ere e2store file. It collects block entries and
|
||||
// writes them to the underlying e2store.Writer.
|
||||
type Builder struct {
|
||||
w *e2store.Writer
|
||||
|
|
@ -326,7 +330,7 @@ func (b *Builder) writeIndex(o *offsets) error {
|
|||
write(uint64(componentCount))
|
||||
write(uint64(count))
|
||||
|
||||
n, err := b.w.Write(era.TypeComponentIndex, buf.Bytes())
|
||||
n, err := b.w.Write(era.TypeDynamicBlockIndex, buf.Bytes())
|
||||
b.written += uint64(n)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
func TestEraE(t *testing.T) {
|
||||
func TestEre(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
|
|
@ -74,7 +74,7 @@ func TestEraE(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f, err := os.CreateTemp(t.TempDir(), "erae-test")
|
||||
f, err := os.CreateTemp(t.TempDir(), "ere-test")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating temp file: %v", err)
|
||||
}
|
||||
|
|
@ -295,7 +295,7 @@ func TestEraE(t *testing.T) {
|
|||
func TestInitialTD(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f, err := os.CreateTemp(t.TempDir(), "erae-initial-td-test")
|
||||
f, err := os.CreateTemp(t.TempDir(), "ere-initial-td-test")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating temp file: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,10 +39,13 @@ type Era struct {
|
|||
m metadata // metadata for the Era file
|
||||
}
|
||||
|
||||
// Filename returns a recognizable filename for an EraE file.
|
||||
// Filename returns a recognizable filename for an Ere file.
|
||||
// The filename uses the last block hash to uniquely identify the epoch's content.
|
||||
//
|
||||
// Files produced by this builder do not include Proof entries, so the
|
||||
// "noproofs" profile postfix is appended per the Ere spec.
|
||||
func Filename(network string, epoch int, lastBlockHash common.Hash) string {
|
||||
return fmt.Sprintf("%s-%05d-%s.erae", network, epoch, lastBlockHash.Hex()[2:10])
|
||||
return fmt.Sprintf("%s-%05d-%s-noproofs.ere", network, epoch, lastBlockHash.Hex()[2:10])
|
||||
}
|
||||
|
||||
// Open accesses the era file.
|
||||
|
|
@ -210,8 +213,8 @@ func (e *Era) InitialTD() (*big.Int, error) {
|
|||
return new(big.Int).Sub(firstTD, header.Difficulty), nil
|
||||
}
|
||||
|
||||
// Accumulator reads the accumulator entry in the EraE file if it exists.
|
||||
// Note that one premerge erae files will contain an accumulator entry.
|
||||
// Accumulator reads the accumulator entry in the Ere file if it exists.
|
||||
// Only pre-merge and merge-transition Ere files contain an accumulator entry.
|
||||
func (e *Era) Accumulator() (common.Hash, error) {
|
||||
entry, err := e.s.Find(era.TypeAccumulator)
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Reference in a new issue