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 -}