mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 02:40: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/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
|
@ -57,6 +58,38 @@ const (
|
||||||
importBatchSize = 2500
|
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
|
type EraFileFormat int
|
||||||
|
|
||||||
// ErrImportInterrupted is returned when the user interrupts the import process.
|
// ErrImportInterrupted is returned when the user interrupts the import process.
|
||||||
|
|
@ -202,18 +235,19 @@ func ImportChain(chain *core.BlockChain, fn string) error {
|
||||||
}
|
}
|
||||||
i := 0
|
i := 0
|
||||||
for ; i < importBatchSize; i++ {
|
for ; i < importBatchSize; i++ {
|
||||||
var b types.Block
|
var record exportBlock
|
||||||
if err := stream.Decode(&b); err == io.EOF {
|
if err := stream.Decode(&record); err == io.EOF {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return fmt.Errorf("at block %d: %v", n, err)
|
return fmt.Errorf("at block %d: %v", n, err)
|
||||||
}
|
}
|
||||||
|
b := record.block()
|
||||||
// don't import first block
|
// don't import first block
|
||||||
if b.NumberU64() == 0 {
|
if b.NumberU64() == 0 {
|
||||||
i--
|
i--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
blocks[i] = &b
|
blocks[i] = b
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
|
|
@ -411,8 +445,7 @@ func ExportChain(blockchain *core.BlockChain, fn string) error {
|
||||||
writer = gzip.NewWriter(writer)
|
writer = gzip.NewWriter(writer)
|
||||||
defer writer.(*gzip.Writer).Close()
|
defer writer.(*gzip.Writer).Close()
|
||||||
}
|
}
|
||||||
// Iterate over the blocks and export them
|
if err := exportChain(blockchain, writer, 0, blockchain.CurrentBlock().Number.Uint64()); err != nil {
|
||||||
if err := blockchain.Export(writer); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info("Exported blockchain", "file", fn)
|
log.Info("Exported blockchain", "file", fn)
|
||||||
|
|
@ -436,14 +469,34 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
|
||||||
writer = gzip.NewWriter(writer)
|
writer = gzip.NewWriter(writer)
|
||||||
defer writer.(*gzip.Writer).Close()
|
defer writer.(*gzip.Writer).Close()
|
||||||
}
|
}
|
||||||
// Iterate over the blocks and export them
|
if err := exportChain(blockchain, writer, first, last); err != nil {
|
||||||
if err := blockchain.ExportN(writer, first, last); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info("Exported blockchain to", "file", fn)
|
log.Info("Exported blockchain to", "file", fn)
|
||||||
return nil
|
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,
|
// ExportHistory exports blockchain history into the specified directory,
|
||||||
// following the Era format.
|
// 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 {
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"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"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,6 +43,37 @@ func TestExportGzip(t *testing.T) {
|
||||||
testExport(t, f)
|
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 {
|
type testIterator struct {
|
||||||
index int
|
index int
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue