From 730bbb7244380350e2d9cf0655cc8a9cd3e14813 Mon Sep 17 00:00:00 2001
From: shantichanal <158101918+shantichanal@users.noreply.github.com>
Date: Mon, 7 Jul 2025 12:54:15 +0200
Subject: [PATCH] sequential access completed (without iterator)
---
internal/era2/accumulator.go | 2 +-
internal/era2/builder2.go | 18 ++--
internal/era2/era2.go | 151 ++++++++++++++++++++++++++-
internal/era2/iterator.go | 197 -----------------------------------
4 files changed, 160 insertions(+), 208 deletions(-)
delete mode 100644 internal/era2/iterator.go
diff --git a/internal/era2/accumulator.go b/internal/era2/accumulator.go
index 5eab7c3e9b..da75e7dceb 100644
--- a/internal/era2/accumulator.go
+++ b/internal/era2/accumulator.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package era
+package era2
import (
"errors"
diff --git a/internal/era2/builder2.go b/internal/era2/builder2.go
index 80dd9163a8..cfeb2bdb47 100644
--- a/internal/era2/builder2.go
+++ b/internal/era2/builder2.go
@@ -1,4 +1,4 @@
-package era
+package era2
// The format can be summarized with the following expression:
@@ -60,7 +60,7 @@ const (
type proofvar uint16
-type Builder2 struct {
+type Builder struct {
w *e2store.Writer
buf *bytes.Buffer
sn *snappy.Writer
@@ -88,16 +88,16 @@ type Builder2 struct {
}
// NewBuilder returns a new EraE Builder writing into the given io.Writer.
-func NewBuilder2(w io.Writer) *Builder2 {
+func NewBuilder(w io.Writer) *Builder {
buf := bytes.NewBuffer(nil)
- return &Builder2{
+ return &Builder{
w: e2store.NewWriter(w),
buf: buf,
sn: snappy.NewBufferedWriter(buf),
}
}
-func (b *Builder2) Add(block *types.Block, receipts types.Receipts, td *big.Int, proofBytes []byte, proofty proofvar) error {
+func (b *Builder) Add(block *types.Block, receipts types.Receipts, td *big.Int, proofBytes []byte, proofty proofvar) error {
if len(b.headersRLP) >= MaxEraESize {
return fmt.Errorf("exceeds MaxEraESize %d", MaxEraESize)
}
@@ -146,7 +146,7 @@ func (b *Builder2) Add(block *types.Block, receipts types.Receipts, td *big.Int,
return nil
}
-func (b *Builder2) Finalize() error {
+func (b *Builder) Finalize() error {
if b.startNum == nil {
return errors.New("no blocks added, cannot finalize")
}
@@ -215,7 +215,7 @@ func decodeBigs(raw [][]byte) []*big.Int {
}
// snappyWrite is a small helper to take care snappy encoding and writing an e2store entry.
-func (b *Builder2) snappyWrite(typ uint16, in []byte) error {
+func (b *Builder) snappyWrite(typ uint16, in []byte) error {
var (
buf = b.buf
s = b.sn
@@ -236,7 +236,7 @@ func (b *Builder2) snappyWrite(typ uint16, in []byte) error {
return nil
}
-func (b *Builder2) writeSection(typ uint16, list [][]byte, useSnappy bool) ([]uint64, error) {
+func (b *Builder) writeSection(typ uint16, list [][]byte, useSnappy bool) ([]uint64, error) {
if len(list) == 0 {
return nil, errors.New("cannot write empty section")
}
@@ -261,7 +261,7 @@ func (b *Builder2) writeSection(typ uint16, list [][]byte, useSnappy bool) ([]ui
return offs, nil
}
-func (b *Builder2) writeIndex() error {
+func (b *Builder) writeIndex() error {
count := uint64(len(b.headeroffsets))
compcount := uint64(3)
if len(b.tds) > 0 {
diff --git a/internal/era2/era2.go b/internal/era2/era2.go
index f1637a4cca..dea5828566 100644
--- a/internal/era2/era2.go
+++ b/internal/era2/era2.go
@@ -1,4 +1,4 @@
-package era
+package era2
import (
"encoding/binary"
@@ -64,6 +64,7 @@ type Era2 struct {
buf [8]byte // buffer reading entry offset
headeroff, bodyoff, receiptsoff, tdoff, proofsoff []uint64 // offsets for each entry type
+ rootheader uint64 // offset of the root header in the file if present
indstart int64
}
@@ -185,6 +186,26 @@ func (e *Era2) GetRawReceiptsFrameByNumber(n uint64) ([]byte, error) {
return io.ReadAll(sr)
}
+func (e *Era2) GetRawProofFrameByNumber(n uint64) ([]byte, error) {
+ if len(e.proofsoff) == 0 {
+ return nil, fmt.Errorf("proofs section not present")
+ }
+ if n < e.m.start || n >= e.m.start+e.m.count {
+ return nil, fmt.Errorf("block number %d out of range [%d, %d)", n, e.m.start, e.m.start+e.m.count)
+ }
+ start := e.proofsoff[n-e.m.start]
+
+ // fallback next proof frame → AccRoot header → BlockIndex
+ var nextSec []uint64
+ if e.rootheader != 0 {
+ nextSec = []uint64{e.rootheader}
+ }
+ end := e.nextBoundary(n, e.proofsoff, nextSec)
+ length := end - start
+ sr := io.NewSectionReader(e.f, int64(start), int64(length))
+ return io.ReadAll(sr)
+}
+
func (e *Era2) rawPayload(abs uint64) ([]byte, error) {
sr := io.NewSectionReader(e.f, int64(abs), e.indstart-int64(abs))
return io.ReadAll(sr)
@@ -269,7 +290,135 @@ func (e *Era2) loadIndex() error {
e.proofsoff[i] = toAbs(base + 4)
}
}
+
+ var off int64 = 0 // start at byte-0 of file
+
+ for off < e.indstart { // never enter the Block-Index TLV
+ typ, length, err := e.s.ReadMetadataAt(off)
+ if err != nil {
+ return fmt.Errorf("error reading TLV at offset %d: %w", off, err)
+ }
+ if typ == TypeAccumulatorRoot {
+ e.rootheader = uint64(off) // remember absolute header offset
+ break // found
+ }
+ off += 8 + int64(length) // headersize plus length to jump to next TLV header
+ }
return nil
+
+}
+
+func (e *Era2) BatchRange(first, count uint64, wantHeaders, wantBodies, wantReceipts, wantProofs bool) (hdrs []*types.Header, bods []*types.Body, recs []types.Receipts, prfs [][]byte, err error) {
+ if count == 0 {
+ err = fmt.Errorf("count must be greater than 0")
+ return
+ }
+
+ if first < e.m.start || first+count > e.m.start+e.m.count {
+ err = fmt.Errorf("range [%d,%d) out of bounds [%d,%d)", first, first+count, e.m.start, e.m.start+e.m.count)
+ return
+ }
+
+ id0 := first - e.m.start
+ if wantHeaders {
+ hdrs = make([]*types.Header, count)
+ }
+ if wantBodies {
+ bods = make([]*types.Body, count)
+ }
+ if wantReceipts {
+ recs = make([]types.Receipts, count)
+ }
+ if wantProofs {
+ prfs = make([][]byte, count)
+ }
+
+ stream := func(startOff uint64, endOff uint64, decode func(io.Reader, uint64) error) error {
+ length := int64(endOff) - int64(startOff)
+ r := snappy.NewReader(io.NewSectionReader(e.f, int64(startOff), length))
+ for i := uint64(0); i < count; i++ {
+ if err := decode(r, i); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+
+ if wantHeaders {
+ err = stream(e.headeroff[id0], e.bodyoff[0], func(r io.Reader, i uint64) error {
+ var hdr types.Header
+ if err := rlp.Decode(r, &hdr); err != nil {
+ return fmt.Errorf("error decoding header for block %d: %w", first+i, err)
+ }
+ hdrs[i] = &hdr
+ return nil
+ })
+ if err != nil {
+ return
+ }
+ }
+
+ if wantBodies {
+ err = stream(e.bodyoff[id0], e.receiptsoff[0], func(r io.Reader, i uint64) error {
+ var body types.Body
+ if err := rlp.Decode(r, &body); err != nil {
+ return fmt.Errorf("error decoding body for block %d: %w", first+i, err)
+ }
+ bods[i] = &body
+ return nil
+ })
+ if err != nil {
+ return
+ }
+ }
+
+ if wantReceipts {
+ var receiptsEnd uint64
+ if len(e.tdoff) > 0 {
+ receiptsEnd = e.tdoff[0]
+ } else {
+ receiptsEnd = uint64(e.indstart)
+ }
+ err = stream(e.receiptsoff[id0], receiptsEnd, func(r io.Reader, i uint64) error {
+ var rct types.Receipts
+ if err := rlp.Decode(r, &rct); err != nil {
+ return fmt.Errorf("error decoding receipts for block %d: %w", first+i, err)
+ }
+ recs[i] = rct
+ return nil
+ })
+ if err != nil {
+ return
+ }
+ }
+
+ if wantProofs {
+ if len(prfs) == 0 {
+ err = fmt.Errorf("proofs section is not present")
+ return
+ }
+
+ for i := uint64(0); i < count; i++ {
+ start := e.proofsoff[id0+i]
+ var end uint64
+ if id0+i+1 < uint64(len(e.proofsoff)) {
+ end = e.proofsoff[id0+i+1] // next proof frame
+ } else if e.rootheader != 0 {
+ end = e.rootheader // AccRoot header
+ } else {
+ end = uint64(e.indstart) // BlockIndex header
+ }
+ length := int64(end - start)
+ frame, readErr := io.ReadAll(io.NewSectionReader(e.f, int64(start), length))
+ if readErr != nil {
+ err = fmt.Errorf("proof read %d: %w", first+i, readErr)
+ return
+ }
+ prfs[i] = frame
+ }
+ }
+
+ return
}
func reverseOrder32(b []byte) []byte {
diff --git a/internal/era2/iterator.go b/internal/era2/iterator.go
deleted file mode 100644
index 3c4f82d850..0000000000
--- a/internal/era2/iterator.go
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2024 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package era
-
-import (
- "errors"
- "io"
- "math/big"
-
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/rlp"
-)
-
-// Iterator wraps RawIterator and returns decoded Era1 entries.
-type Iterator struct {
- inner *RawIterator
-}
-
-// 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)
- if err != nil {
- return nil, err
- }
- return &Iterator{inner}, nil
-}
-
-// Next moves the iterator to the next block entry. It returns false when all
-// items have been read or an error has halted its progress. Block, Receipts,
-// and BlockAndReceipts should no longer be called after false is returned.
-func (it *Iterator) Next() bool {
- return it.inner.Next()
-}
-
-// Number returns the current number block the iterator will return.
-func (it *Iterator) Number() uint64 {
- return it.inner.next - 1
-}
-
-// Error returns the error status of the iterator. It should be called before
-// reading from any of the iterator's values.
-func (it *Iterator) Error() error {
- return it.inner.Error()
-}
-
-// Block returns the block for the iterator's current position.
-func (it *Iterator) Block() (*types.Block, error) {
- if it.inner.Header == nil || it.inner.Body == nil {
- return nil, errors.New("header and body must be non-nil")
- }
- var (
- header types.Header
- body types.Body
- )
- if err := rlp.Decode(it.inner.Header, &header); err != nil {
- return nil, err
- }
- if err := rlp.Decode(it.inner.Body, &body); err != nil {
- return nil, err
- }
- return types.NewBlockWithHeader(&header).WithBody(body), nil
-}
-
-// Receipts returns the receipts for the iterator's current position.
-func (it *Iterator) Receipts() (types.Receipts, error) {
- if it.inner.Receipts == nil {
- return nil, errors.New("receipts must be non-nil")
- }
- var receipts types.Receipts
- err := rlp.Decode(it.inner.Receipts, &receipts)
- return receipts, err
-}
-
-// BlockAndReceipts returns the block and receipts for the iterator's current
-// position.
-func (it *Iterator) BlockAndReceipts() (*types.Block, types.Receipts, error) {
- b, err := it.Block()
- if err != nil {
- return nil, nil, err
- }
- r, err := it.Receipts()
- if err != nil {
- return nil, nil, err
- }
- return b, r, nil
-}
-
-// TotalDifficulty returns the total difficulty for the iterator's current
-// position.
-func (it *Iterator) TotalDifficulty() (*big.Int, error) {
- td, err := io.ReadAll(it.inner.TotalDifficulty)
- if err != nil {
- return nil, err
- }
- return new(big.Int).SetBytes(reverseOrder(td)), nil
-}
-
-// RawIterator reads an RLP-encode Era1 entries.
-type RawIterator struct {
- e *Era // backing Era1
- next uint64 // next block to read
- err error // last error
-
- Header io.Reader
- Body io.Reader
- Receipts io.Reader
- TotalDifficulty io.Reader
-}
-
-// NewRawIterator returns a new RawIterator instance. Next must be immediately
-// called on new iterators to load the first item.
-func NewRawIterator(e *Era) (*RawIterator, error) {
- return &RawIterator{
- e: e,
- next: e.m.start,
- }, nil
-}
-
-// Next moves the iterator to the next block entry. It returns false when all
-// items have been read or an error has halted its progress. Header, Body,
-// Receipts, TotalDifficulty will be set to nil in the case returning false or
-// finding an error and should therefore no longer be read from.
-func (it *RawIterator) Next() bool {
- // Clear old errors.
- it.err = nil
- if it.e.m.start+it.e.m.count <= it.next {
- it.clear()
- return false
- }
- off, err := it.e.readOffset(it.next)
- if err != nil {
- // Error here means block index is corrupted, so don't
- // continue.
- it.clear()
- it.err = err
- return false
- }
- var n int64
- if it.Header, n, it.err = newSnappyReader(it.e.s, TypeCompressedHeader, off); it.err != nil {
- it.clear()
- return true
- }
- off += n
- if it.Body, n, it.err = newSnappyReader(it.e.s, TypeCompressedBody, off); it.err != nil {
- it.clear()
- return true
- }
- off += n
- if it.Receipts, n, it.err = newSnappyReader(it.e.s, TypeCompressedReceipts, off); it.err != nil {
- it.clear()
- return true
- }
- off += n
- if it.TotalDifficulty, _, it.err = it.e.s.ReaderAt(TypeTotalDifficulty, off); it.err != nil {
- it.clear()
- return true
- }
- it.next += 1
- return true
-}
-
-// Number returns the current number block the iterator will return.
-func (it *RawIterator) Number() uint64 {
- return it.next - 1
-}
-
-// Error returns the error status of the iterator. It should be called before
-// reading from any of the iterator's values.
-func (it *RawIterator) Error() error {
- if it.err == io.EOF {
- return nil
- }
- return it.err
-}
-
-// clear sets all the outputs to nil.
-func (it *RawIterator) clear() {
- it.Header = nil
- it.Body = nil
- it.Receipts = nil
- it.TotalDifficulty = nil
-}