1f50aa7631
* all: implement era format, add history importer/export * internal/era/e2store: refactor e2store to provide ReadAt interface * internal/era/e2store: export HeaderSize * internal/era: refactor era to use ReadAt interface * internal/era: elevate anonymous func to named * cmd/utils: don't store entire era file in-memory during import / export * internal/era: better abstraction between era and e2store * cmd/era: properly close era files * cmd/era: don't let defers stack * cmd/geth: add description for import-history * cmd/utils: better bytes buffer * internal/era: error if accumulator has more records than max allowed * internal/era: better doc comment * internal/era/e2store: rm superfluous reader, rm superfluous testcases, add fuzzer * internal/era: avoid some repetition * internal/era: simplify clauses * internal/era: unexport things * internal/era,cmd/utils,cmd/era: change to iterator interface for reading era entries * cmd/utils: better defer handling in history test * internal/era,cmd: add number method to era iterator to get the current block number * internal/era/e2store: avoid double allocation during write * internal/era,cmd/utils: fix lint issues * internal/era: add ReaderAt func so entry value can be read lazily Co-authored-by: lightclient <lightclient@protonmail.com> Co-authored-by: Martin Holst Swende <martin@swende.se> * internal/era: improve iterator interface * internal/era: fix rlp decode of header and correctly read total difficulty * cmd/era: fix rebase errors * cmd/era: clearer comments * cmd,internal: fix comment typos --------- Co-authored-by: Martin Holst Swende <martin@swende.se>
198 lines
5.4 KiB
Go
198 lines
5.4 KiB
Go
// Copyright 2023 The go-ethereum Authors
|
|
// This file is part of go-ethereum.
|
|
//
|
|
// go-ethereum is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// go-ethereum 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 General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package era
|
|
|
|
import (
|
|
"fmt"
|
|
"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
|
|
}
|
|
|
|
// NewRawIterator 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, fmt.Errorf("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.Transactions, body.Uncles), 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, fmt.Errorf("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
|
|
}
|