mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-17 18:30:45 +00:00
cmd/utils: export chain with block-level accessList (#35369)
This commit is contained in:
parent
80b58f649a
commit
dddbaa4bf3
2 changed files with 96 additions and 7 deletions
|
|
@ -40,6 +40,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
|
|
@ -57,6 +58,38 @@ const (
|
|||
importBatchSize = 2500
|
||||
)
|
||||
|
||||
// exportBlock is the on-disk representation used by geth export and import.
|
||||
//
|
||||
// It intentionally differs from types.Block's network encoding: a block access
|
||||
// list is stored as an optional trailing field so that an exported chain retains
|
||||
// EIP-7928 data. Older export files, which end at Withdrawals, remain valid.
|
||||
type exportBlock struct {
|
||||
Header *types.Header
|
||||
Txs []*types.Transaction
|
||||
Uncles []*types.Header
|
||||
Withdrawals []*types.Withdrawal `rlp:"optional"`
|
||||
AccessList *bal.BlockAccessList `rlp:"optional"`
|
||||
}
|
||||
|
||||
func makeExportBlock(block *types.Block) exportBlock {
|
||||
return exportBlock{
|
||||
Header: block.Header(),
|
||||
Txs: block.Transactions(),
|
||||
Uncles: block.Uncles(),
|
||||
Withdrawals: block.Withdrawals(),
|
||||
AccessList: block.AccessList(),
|
||||
}
|
||||
}
|
||||
|
||||
func (b exportBlock) block() *types.Block {
|
||||
block := types.NewBlockWithHeader(b.Header).WithBody(types.Body{
|
||||
Transactions: b.Txs,
|
||||
Uncles: b.Uncles,
|
||||
Withdrawals: b.Withdrawals,
|
||||
})
|
||||
return block.WithAccessListUnsafe(b.AccessList)
|
||||
}
|
||||
|
||||
type EraFileFormat int
|
||||
|
||||
// ErrImportInterrupted is returned when the user interrupts the import process.
|
||||
|
|
@ -202,18 +235,19 @@ func ImportChain(chain *core.BlockChain, fn string) error {
|
|||
}
|
||||
i := 0
|
||||
for ; i < importBatchSize; i++ {
|
||||
var b types.Block
|
||||
if err := stream.Decode(&b); err == io.EOF {
|
||||
var record exportBlock
|
||||
if err := stream.Decode(&record); err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("at block %d: %v", n, err)
|
||||
}
|
||||
b := record.block()
|
||||
// don't import first block
|
||||
if b.NumberU64() == 0 {
|
||||
i--
|
||||
continue
|
||||
}
|
||||
blocks[i] = &b
|
||||
blocks[i] = b
|
||||
n++
|
||||
}
|
||||
if i == 0 {
|
||||
|
|
@ -411,8 +445,7 @@ func ExportChain(blockchain *core.BlockChain, fn string) error {
|
|||
writer = gzip.NewWriter(writer)
|
||||
defer writer.(*gzip.Writer).Close()
|
||||
}
|
||||
// Iterate over the blocks and export them
|
||||
if err := blockchain.Export(writer); err != nil {
|
||||
if err := exportChain(blockchain, writer, 0, blockchain.CurrentBlock().Number.Uint64()); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Exported blockchain", "file", fn)
|
||||
|
|
@ -436,14 +469,34 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
|
|||
writer = gzip.NewWriter(writer)
|
||||
defer writer.(*gzip.Writer).Close()
|
||||
}
|
||||
// Iterate over the blocks and export them
|
||||
if err := blockchain.ExportN(writer, first, last); err != nil {
|
||||
if err := exportChain(blockchain, writer, first, last); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Exported blockchain to", "file", fn)
|
||||
return nil
|
||||
}
|
||||
|
||||
func exportChain(chain *core.BlockChain, writer io.Writer, first, last uint64) error {
|
||||
if first > last {
|
||||
return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
|
||||
}
|
||||
var parentHash common.Hash
|
||||
for number := first; number <= last; number++ {
|
||||
block := chain.GetBlockByNumber(number)
|
||||
if block == nil {
|
||||
return fmt.Errorf("export failed on #%d: not found", number)
|
||||
}
|
||||
if number > first && block.ParentHash() != parentHash {
|
||||
return errors.New("export failed: chain reorg during export")
|
||||
}
|
||||
parentHash = block.Hash()
|
||||
if err := rlp.Encode(writer, makeExportBlock(block)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExportHistory exports blockchain history into the specified directory,
|
||||
// following the Era format.
|
||||
func ExportHistory(bc *core.BlockChain, dir string, first, last uint64, newBuilder func(io.Writer) era.Builder, filename func(network string, epoch int, lastBlockHash common.Hash) string) error {
|
||||
|
|
|
|||
|
|
@ -18,12 +18,17 @@ package utils
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
|
|
@ -38,6 +43,37 @@ func TestExportGzip(t *testing.T) {
|
|||
testExport(t, f)
|
||||
}
|
||||
|
||||
func TestExportBlockAccessList(t *testing.T) {
|
||||
accessList := &bal.BlockAccessList{{Address: common.HexToAddress("0x1234")}}
|
||||
block := types.NewBlockWithHeader(&types.Header{Number: big.NewInt(1)}).WithAccessListUnsafe(accessList)
|
||||
encoded, err := rlp.EncodeToBytes(makeExportBlock(block))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var decoded exportBlock
|
||||
if err := rlp.DecodeBytes(encoded, &decoded); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := decoded.block().AccessList(); !reflect.DeepEqual(got, accessList) {
|
||||
t.Fatalf("access list = %#v, want %#v", got, accessList)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportLegacyBlock(t *testing.T) {
|
||||
block := types.NewBlockWithHeader(&types.Header{Number: big.NewInt(1)})
|
||||
encoded, err := rlp.EncodeToBytes(block)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var decoded exportBlock
|
||||
if err := rlp.DecodeBytes(encoded, &decoded); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := decoded.block().AccessList(); got != nil {
|
||||
t.Fatalf("access list = %#v, want nil", got)
|
||||
}
|
||||
}
|
||||
|
||||
type testIterator struct {
|
||||
index int
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue