From bd8c78a400bc78455bf3a9c7eadad0e0774a40c4 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 17 Jan 2025 15:19:41 +0100 Subject: [PATCH 01/39] feat(core/types): `Body` and `Block` hooks --- consensus/dummy/consensus.go | 4 +- core/blockchain.go | 2 +- core/rawdb/accessors_chain.go | 2 +- core/types/block.go | 301 ------------------------------- core/types/block_ext.go | 145 ++++++++++++--- core/types/block_test.go | 34 ++-- core/types/imports.go | 6 + core/types/libevm.go | 2 +- ethclient/ethclient.go | 2 +- internal/ethapi/api.go | 2 +- plugin/evm/block.go | 2 +- plugin/evm/block_verification.go | 10 +- plugin/evm/syncervm_test.go | 2 +- plugin/evm/vm.go | 2 +- plugin/evm/vm_test.go | 32 ++-- 15 files changed, 176 insertions(+), 372 deletions(-) delete mode 100644 core/types/block.go diff --git a/consensus/dummy/consensus.go b/consensus/dummy/consensus.go index ffbd8b0abe..b93864a9fd 100644 --- a/consensus/dummy/consensus.go +++ b/consensus/dummy/consensus.go @@ -377,12 +377,12 @@ func (eng *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *types if extDataGasUsed == nil { extDataGasUsed = new(big.Int).Set(common.Big0) } - if blockExtDataGasUsed := block.ExtDataGasUsed(); blockExtDataGasUsed == nil || !blockExtDataGasUsed.IsUint64() || blockExtDataGasUsed.Cmp(extDataGasUsed) != 0 { + if blockExtDataGasUsed := types.BlockExtDataGasUsed(block); blockExtDataGasUsed == nil || !blockExtDataGasUsed.IsUint64() || blockExtDataGasUsed.Cmp(extDataGasUsed) != 0 { return fmt.Errorf("invalid extDataGasUsed: have %d, want %d", blockExtDataGasUsed, extDataGasUsed) } // Verify the BlockGasCost set in the header matches the expected value. - blockGasCost := block.BlockGasCost() + blockGasCost := types.BlockGasCost(block) expectedBlockGasCost := customheader.BlockGasCost( configExtra, parent, diff --git a/core/blockchain.go b/core/blockchain.go index 6b2b7d4cf5..81806728a5 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1367,7 +1367,7 @@ func (bc *BlockChain) insertBlock(block *types.Block, writes bool) error { "parentHash", block.ParentHash(), "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(start)), - "root", block.Root(), "baseFeePerGas", block.BaseFee(), "blockGasCost", block.BlockGasCost(), + "root", block.Root(), "baseFeePerGas", block.BaseFee(), "blockGasCost", types.BlockGasCost(block), ) processedBlockGasUsedCounter.Inc(int64(block.GasUsed())) diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index dba05750a7..21eb1ff843 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -522,7 +522,7 @@ func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block { if body == nil { return nil } - return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles).WithExtData(body.Version, body.ExtData) + return types.NewBlockWithHeader(header).WithBody(*body) } // WriteBlock serializes a block into the database, header and body separately. diff --git a/core/types/block.go b/core/types/block.go deleted file mode 100644 index 01c723c475..0000000000 --- a/core/types/block.go +++ /dev/null @@ -1,301 +0,0 @@ -// (c) 2019-2020, Ava Labs, Inc. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2014 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 types contains data types related to Ethereum consensus. -package types - -import ( - "encoding/binary" - "io" - "math/big" - "sync/atomic" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/rlp" -) - -// Body is a simple (mutable, non-safe) data container for storing and moving -// a block's data contents (transactions and uncles) together. -type Body struct { - Transactions []*Transaction - Uncles []*Header - Version uint32 - ExtData *[]byte `rlp:"nil"` -} - -// Block represents an Ethereum block. -// -// Note the Block type tries to be 'immutable', and contains certain caches that rely -// on that. The rules around block immutability are as follows: -// -// - We copy all data when the block is constructed. This makes references held inside -// the block independent of whatever value was passed in. -// -// - We copy all header data on access. This is because any change to the header would mess -// up the cached hash and size values in the block. Calling code is expected to take -// advantage of this to avoid over-allocating! -// -// - When new body data is attached to the block, a shallow copy of the block is returned. -// This ensures block modifications are race-free. -// -// - We do not copy body data on access because it does not affect the caches, and also -// because it would be too expensive. -type Block struct { - header *Header - uncles []*Header - transactions Transactions - - // Coreth specific data structures to support atomic transactions - version uint32 - extdata *[]byte - - // caches - hash atomic.Value - size atomic.Value -} - -// "external" block encoding. used for eth protocol, etc. -type extblock struct { - Header *Header - Txs []*Transaction - Uncles []*Header - Version uint32 - ExtData *[]byte `rlp:"nil"` -} - -// NewBlock creates a new block. The input data is copied, changes to header and to the -// field values will not affect the block. -// -// The values of TxHash, UncleHash, ReceiptHash and Bloom in header -// are ignored and set to values derived from the given txs, uncles -// and receipts. -func NewBlock( - header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher TrieHasher, -) *Block { - b := &Block{header: CopyHeader(header)} - - // TODO: panic if len(txs) != len(receipts) - if len(txs) == 0 { - b.header.TxHash = EmptyTxsHash - } else { - b.header.TxHash = DeriveSha(Transactions(txs), hasher) - b.transactions = make(Transactions, len(txs)) - copy(b.transactions, txs) - } - - if len(receipts) == 0 { - b.header.ReceiptHash = EmptyReceiptsHash - } else { - b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher) - b.header.Bloom = CreateBloom(receipts) - } - - if len(uncles) == 0 { - b.header.UncleHash = EmptyUncleHash - } else { - b.header.UncleHash = CalcUncleHash(uncles) - b.uncles = make([]*Header, len(uncles)) - for i := range uncles { - b.uncles[i] = CopyHeader(uncles[i]) - } - } - - return b -} - -// DecodeRLP decodes a block from RLP. -func (b *Block) DecodeRLP(s *rlp.Stream) error { - var eb extblock - _, size, _ := s.Kind() - if err := s.Decode(&eb); err != nil { - return err - } - b.header, b.uncles, b.transactions, b.version, b.extdata = eb.Header, eb.Uncles, eb.Txs, eb.Version, eb.ExtData - b.size.Store(rlp.ListSize(size)) - return nil -} - -// EncodeRLP serializes a block as RLP. -func (b *Block) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, &extblock{ - Header: b.header, - Txs: b.transactions, - Uncles: b.uncles, - Version: b.version, - ExtData: b.extdata, - }) -} - -// Body returns the non-header content of the block. -// Note the returned data is not an independent copy. -func (b *Block) Body() *Body { - return &Body{b.transactions, b.uncles, b.version, b.extdata} -} - -// Accessors for body data. These do not return a copy because the content -// of the body slices does not affect the cached hash/size in block. - -func (b *Block) Uncles() []*Header { return b.uncles } -func (b *Block) Transactions() Transactions { return b.transactions } - -func (b *Block) Transaction(hash common.Hash) *Transaction { - for _, transaction := range b.transactions { - if transaction.Hash() == hash { - return transaction - } - } - return nil -} - -// Header returns the block header (as a copy). -func (b *Block) Header() *Header { - return CopyHeader(b.header) -} - -// Header value accessors. These do copy! - -func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) } -func (b *Block) GasLimit() uint64 { return b.header.GasLimit } -func (b *Block) GasUsed() uint64 { return b.header.GasUsed } -func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) } -func (b *Block) Time() uint64 { return b.header.Time } - -func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() } -func (b *Block) MixDigest() common.Hash { return b.header.MixDigest } -func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) } -func (b *Block) Bloom() Bloom { return b.header.Bloom } -func (b *Block) Coinbase() common.Address { return b.header.Coinbase } -func (b *Block) Root() common.Hash { return b.header.Root } -func (b *Block) ParentHash() common.Hash { return b.header.ParentHash } -func (b *Block) TxHash() common.Hash { return b.header.TxHash } -func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash } -func (b *Block) UncleHash() common.Hash { return b.header.UncleHash } -func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) } - -func (b *Block) BaseFee() *big.Int { - if b.header.BaseFee == nil { - return nil - } - return new(big.Int).Set(b.header.BaseFee) -} - -func (b *Block) BeaconRoot() *common.Hash { return b.header.ParentBeaconRoot } - -func (b *Block) ExcessBlobGas() *uint64 { - var excessBlobGas *uint64 - if b.header.ExcessBlobGas != nil { - excessBlobGas = new(uint64) - *excessBlobGas = *b.header.ExcessBlobGas - } - return excessBlobGas -} - -func (b *Block) BlobGasUsed() *uint64 { - var blobGasUsed *uint64 - if b.header.BlobGasUsed != nil { - blobGasUsed = new(uint64) - *blobGasUsed = *b.header.BlobGasUsed - } - return blobGasUsed -} - -func (b *Block) BlockGasCost() *big.Int { - cost := GetHeaderExtra(b.header).BlockGasCost - if cost == nil { - return nil - } - return new(big.Int).Set(cost) -} - -// Size returns the true RLP encoded storage size of the block, either by encoding -// and returning it, or returning a previously cached value. -func (b *Block) Size() uint64 { - if size := b.size.Load(); size != nil { - return size.(uint64) - } - c := writeCounter(0) - rlp.Encode(&c, b) - b.size.Store(uint64(c)) - return uint64(c) -} - -type writeCounter uint64 - -func (c *writeCounter) Write(b []byte) (int, error) { - *c += writeCounter(len(b)) - return len(b), nil -} - -func CalcUncleHash(uncles []*Header) common.Hash { - if len(uncles) == 0 { - return EmptyUncleHash - } - return rlpHash(uncles) -} - -// NewBlockWithHeader creates a block with the given header data. The -// header data is copied, changes to header and to the field values -// will not affect the block. -func NewBlockWithHeader(header *Header) *Block { - return &Block{header: CopyHeader(header)} -} - -// WithSeal returns a new block with the data from b but the header replaced with -// the sealed one. -func (b *Block) WithSeal(header *Header) *Block { - return &Block{ - header: CopyHeader(header), - transactions: b.transactions, - uncles: b.uncles, - } -} - -// WithBody returns a copy of the block with the given transaction and uncle contents. -func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { - block := &Block{ - header: b.header, - transactions: make([]*Transaction, len(transactions)), - uncles: make([]*Header, len(uncles)), - } - copy(block.transactions, transactions) - for i := range uncles { - block.uncles[i] = CopyHeader(uncles[i]) - } - return block -} - -// Hash returns the keccak256 hash of b's header. -// The hash is computed on the first call and cached thereafter. -func (b *Block) Hash() common.Hash { - if hash := b.hash.Load(); hash != nil { - return hash.(common.Hash) - } - v := b.header.Hash() - b.hash.Store(v) - return v -} - -type Blocks []*Block diff --git a/core/types/block_ext.go b/core/types/block_ext.go index 2cfb4b2888..61982e405d 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -7,50 +7,139 @@ import ( "math/big" "github.com/ava-labs/libevm/common" + ethtypes "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/rlp" ) -func (b *Block) WithExtData(version uint32, extdata *[]byte) *Block { - b.version = version - b.setExtDataHelper(extdata, false) +// GetBlockExtra returns the [BlockBodyExtra] contained in the [Block] `b`. +func GetBlockExtra(b *Block) *BlockBodyExtra { + return extras.Block.Get(b) +} + +// WithBlockExtra sets the [BlockBodyExtra] `extra` into the [Block] `b`. +func WithBlockExtra(b *Block, extra *BlockBodyExtra) *Block { + extras.Block.Set(b, extra) + return b +} + +// GetBodyExtra returns the [BlockBodyExtra] contained in the [Body] `b`. +func GetBodyExtra(b *Body) *BlockBodyExtra { + return extras.Body.Get(b) +} + +// WithBodyExtra sets the [BlockBodyExtra] `extra` into the [Body] `b`. +func WithBodyExtra(b *Body, extra *BlockBodyExtra) *Body { + extras.Body.Set(b, extra) return b } -func (b *Block) setExtDataHelper(data *[]byte, recalc bool) { - if data == nil { - b.setExtData(nil, recalc) - return +// BlockBodyExtra is a struct containing extra fields used by Avalanche +// in the [Block] and [Body]. +type BlockBodyExtra struct { + Version uint32 + ExtData *[]byte + + // RLP block and body field removed from geth: + // - Withdrawals []*Withdrawal +} + +// Copy deep copies the [BlockBodyExtra] `b` and returns it. +// It is notably used in the following functions: +// - [Block]'s Body() method +// - [Block]'s With*() methods +func (b *BlockBodyExtra) Copy() *BlockBodyExtra { + cpy := *b + if b.ExtData == nil { + data := make([]byte, 0) + cpy.ExtData = &data + } else { + data := make([]byte, len(*b.ExtData)) + copy(data, *b.ExtData) + cpy.ExtData = &data } - b.setExtData(*data, recalc) + return &cpy } -func (b *Block) setExtData(data []byte, recalc bool) { - _data := make([]byte, len(data)) - b.extdata = &_data - copy(*b.extdata, data) - if recalc { - GetHeaderExtra(b.header).ExtDataHash = CalcExtDataHash(*b.extdata) +// BodyRLPFieldPointersForEncoding returns the fields that should be encoded +// for the [Body] and [BlockBodyExtra]. +func (b *BlockBodyExtra) BodyRLPFieldsForEncoding(body *Body) *rlp.Fields { + return &rlp.Fields{ + Required: []any{ + body.Transactions, + body.Uncles, + b.Version, + b.ExtData, + }, + } +} + +// BodyRLPFieldPointersForDecoding returns the fields that should be decoded to +// for the [Body] and [BlockBodyExtra]. +func (b *BlockBodyExtra) BodyRLPFieldPointersForDecoding(body *Body) *rlp.Fields { + return &rlp.Fields{ + Required: []any{ + &body.Transactions, + &body.Uncles, + &b.Version, + &b.ExtData, + }, + } +} + +// BlockRLPFieldPointersForEncoding returns the fields that should be encoded +// for the [Block] and [BlockBodyExtra]. +func (b *BlockBodyExtra) BlockRLPFieldsForEncoding(block *ethtypes.BlockRLPProxy) *rlp.Fields { + return &rlp.Fields{ + Required: []any{ + block.Header, + block.Txs, + block.Uncles, + b.Version, + b.ExtData, + }, + } +} + +// BlockRLPFieldPointersForDecoding returns the fields that should be decoded to +// for the [Block] and [BlockBodyExtra]. +func (b *BlockBodyExtra) BlockRLPFieldPointersForDecoding(block *ethtypes.BlockRLPProxy) *rlp.Fields { + return &rlp.Fields{ + Required: []any{ + &block.Header, + &block.Txs, + &block.Uncles, + &b.Version, + &b.ExtData, + }, } } -func (b *Block) ExtData() []byte { - if b.extdata == nil { +func BlockExtData(b *Block) []byte { + extras := GetBlockExtra(b) + if extras.ExtData == nil { return nil } - return *b.extdata + return *extras.ExtData } -func (b *Block) Version() uint32 { - return b.version +func BlockVersion(b *Block) uint32 { + return GetBlockExtra(b).Version } -func (b *Block) ExtDataGasUsed() *big.Int { - used := GetHeaderExtra(b.header).ExtDataGasUsed +func BlockExtDataGasUsed(b *Block) *big.Int { + used := GetHeaderExtra(b.Header()).ExtDataGasUsed if used == nil { return nil } return new(big.Int).Set(used) } +func BlockGasCost(b *Block) *big.Int { + header := b.Header() // note this deep copies headerExtra.BlockGasCost + headerExtra := GetHeaderExtra(header) + return headerExtra.BlockGasCost +} + func CalcExtDataHash(extdata []byte) common.Hash { if len(extdata) == 0 { return EmptyExtDataHash @@ -62,7 +151,15 @@ func NewBlockWithExtData( header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher TrieHasher, extdata []byte, recalc bool, ) *Block { - b := NewBlock(header, txs, uncles, receipts, hasher) - b.setExtData(extdata, recalc) - return b + if recalc { + headerExtra := GetHeaderExtra(header) + headerExtra.ExtDataHash = CalcExtDataHash(extdata) + } + block := NewBlock(header, txs, uncles, receipts, hasher) + extdataCopy := make([]byte, len(extdata)) + copy(extdataCopy, extdata) + extras := &BlockBodyExtra{ + ExtData: &extdataCopy, + } + return WithBlockExtra(block, extras) } diff --git a/core/types/block_test.go b/core/types/block_test.go index 95546c83c3..9cadaeff76 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -69,10 +69,10 @@ func TestBlockEncoding(t *testing.T) { check("Extra", block.Extra(), common.FromHex("")) check("MixDigest", block.MixDigest(), common.HexToHash("0000000000000000000000000000000000000000000000000000000000000000")) check("Nonce", block.Nonce(), uint64(0)) - check("ExtDataHash", GetHeaderExtra(block.header).ExtDataHash, common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")) + check("ExtDataHash", GetHeaderExtra(block.Header()).ExtDataHash, common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")) check("BaseFee", block.BaseFee(), (*big.Int)(nil)) - check("ExtDataGasUsed", block.ExtDataGasUsed(), (*big.Int)(nil)) - check("BlockGasCost", block.BlockGasCost(), (*big.Int)(nil)) + check("ExtDataGasUsed", BlockExtDataGasUsed(&block), (*big.Int)(nil)) + check("BlockGasCost", BlockGasCost(&block), (*big.Int)(nil)) check("Size", block.Size(), uint64(len(blockEnc))) check("BlockHash", block.Hash(), common.HexToHash("0608e5d5e13c337f226b621a0b08b3d50470f1961329826fd59f5a241d1df49e")) @@ -113,8 +113,8 @@ func TestEIP1559BlockEncoding(t *testing.T) { check("Time", block.Time(), uint64(1426516743)) check("Size", block.Size(), uint64(len(blockEnc))) check("BaseFee", block.BaseFee(), new(big.Int).SetUint64(1_000_000_000)) - check("ExtDataGasUsed", block.ExtDataGasUsed(), (*big.Int)(nil)) - check("BlockGasCost", block.BlockGasCost(), (*big.Int)(nil)) + check("ExtDataGasUsed", BlockExtDataGasUsed(&block), (*big.Int)(nil)) + check("BlockGasCost", BlockGasCost(&block), (*big.Int)(nil)) tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil) tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")) @@ -177,10 +177,10 @@ func TestEIP2718BlockEncoding(t *testing.T) { check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4)) check("Time", block.Time(), uint64(1426516743)) check("Size", block.Size(), uint64(len(blockEnc))) - check("ExtDataHash", GetHeaderExtra(block.header).ExtDataHash, common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")) + check("ExtDataHash", GetHeaderExtra(block.Header()).ExtDataHash, common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")) check("BaseFee", block.BaseFee(), (*big.Int)(nil)) - check("ExtDataGasUsed", block.ExtDataGasUsed(), (*big.Int)(nil)) - check("BlockGasCost", block.BlockGasCost(), (*big.Int)(nil)) + check("ExtDataGasUsed", BlockExtDataGasUsed(&block), (*big.Int)(nil)) + check("BlockGasCost", BlockGasCost(&block), (*big.Int)(nil)) // Create legacy tx. to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87") @@ -212,8 +212,8 @@ func TestEIP2718BlockEncoding(t *testing.T) { check("Transactions[1].Hash", block.Transactions()[1].Hash(), tx2.Hash()) check("Transactions[1].Type()", block.Transactions()[1].Type(), uint8(AccessListTxType)) - if !bytes.Equal(block.ExtData(), []byte{}) { - t.Errorf("Block ExtraData field mismatch, expected empty byte array, but found 0x%x", block.ExtData()) + if !bytes.Equal(BlockExtData(&block), []byte{}) { + t.Errorf("Block ExtraData field mismatch, expected empty byte array, but found 0x%x", BlockExtData(&block)) } ourBlockEnc, err := rlp.EncodeToBytes(&block) @@ -252,10 +252,10 @@ func TestBlockEncodingWithExtraData(t *testing.T) { check("Extra", block.Extra(), common.FromHex("")) check("MixDigest", block.MixDigest(), common.HexToHash("0000000000000000000000000000000000000000000000000000000000000000")) check("Nonce", block.Nonce(), uint64(0)) - check("ExtDataHash", GetHeaderExtra(block.header).ExtDataHash, common.HexToHash("296ff3bfdebf7c4b1fb71f589d69ed03b1c59b278d1780d54dc86ea7cb87cf17")) + check("ExtDataHash", GetHeaderExtra(block.Header()).ExtDataHash, common.HexToHash("296ff3bfdebf7c4b1fb71f589d69ed03b1c59b278d1780d54dc86ea7cb87cf17")) check("BaseFee", block.BaseFee(), (*big.Int)(nil)) - check("ExtDataGasUsed", block.ExtDataGasUsed(), (*big.Int)(nil)) - check("BlockGasCost", block.BlockGasCost(), (*big.Int)(nil)) + check("ExtDataGasUsed", BlockExtDataGasUsed(&block), (*big.Int)(nil)) + check("BlockGasCost", BlockGasCost(&block), (*big.Int)(nil)) check("Size", block.Size(), uint64(len(blockEnc))) check("BlockHash", block.Hash(), common.HexToHash("4504ee98a94d16dbd70a35370501a3cb00c2965b012672085fbd328a72962902")) @@ -263,8 +263,8 @@ func TestBlockEncodingWithExtraData(t *testing.T) { check("len(Transactions)", len(block.Transactions()), 0) expectedBlockExtraData := common.FromHex("00000000000000003039c85fc1980a77c5da78fe5486233fc09a769bb812bcb2cc548cf9495d046b3f1bd891ad56056d9c01f18f43f58b5c784ad07a4a49cf3d1f11623804b5cba2c6bf000000028a0f7c3e4d840143671a4c4ecacccb4d60fb97dce97a7aa5d60dfd072a7509cf00000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000500002d79883d20000000000100000000e0d5c4edc78f594b79025a56c44933c28e8ba3e51e6e23318727eeaac10eb27d00000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000500002d79883d20000000000100000000000000016dc8ea73dd39ab12fa2ecbc3427abaeb87d56fd800005af3107a4000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000200000009000000010d9f115cd63c3ab78b5b82cfbe4339cd6be87f21cda14cf192b269c7a6cb2d03666aa8f8b23ca0a2ceee4050e75c9b05525a17aa1dd0e9ea391a185ce395943f0000000009000000010d9f115cd63c3ab78b5b82cfbe4339cd6be87f21cda14cf192b269c7a6cb2d03666aa8f8b23ca0a2ceee4050e75c9b05525a17aa1dd0e9ea391a185ce395943f00") - if !bytes.Equal(block.ExtData(), expectedBlockExtraData) { - t.Errorf("Block ExtraData field mismatch, expected 0x%x, but found 0x%x", block.ExtData(), expectedBlockExtraData) + if !bytes.Equal(BlockExtData(&block), expectedBlockExtraData) { + t.Errorf("Block ExtraData field mismatch, expected 0x%x, but found 0x%x", BlockExtData(&block), expectedBlockExtraData) } ourBlockEnc, err := rlp.EncodeToBytes(&block) @@ -365,8 +365,8 @@ func TestAP4BlockEncoding(t *testing.T) { check("Time", block.Time(), uint64(1426516743)) check("Size", block.Size(), uint64(len(blockEnc))) check("BaseFee", block.BaseFee(), big.NewInt(1_000_000_000)) - check("ExtDataGasUsed", block.ExtDataGasUsed(), big.NewInt(25_000)) - check("BlockGasCost", block.BlockGasCost(), big.NewInt(1_000_000)) + check("ExtDataGasUsed", BlockExtDataGasUsed(&block), big.NewInt(25_000)) + check("BlockGasCost", BlockGasCost(&block), big.NewInt(1_000_000)) tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil) tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")) diff --git a/core/types/imports.go b/core/types/imports.go index e630ea5366..2a0ad279bf 100644 --- a/core/types/imports.go +++ b/core/types/imports.go @@ -15,8 +15,11 @@ type ( AccessTuple = ethtypes.AccessTuple BlobTx = ethtypes.BlobTx BlobTxSidecar = ethtypes.BlobTxSidecar + Block = ethtypes.Block BlockNonce = ethtypes.BlockNonce + Blocks = ethtypes.Blocks Bloom = ethtypes.Bloom + Body = ethtypes.Body DynamicFeeTx = ethtypes.DynamicFeeTx Header = ethtypes.Header HomesteadSigner = ethtypes.HomesteadSigner @@ -51,11 +54,14 @@ const ( var ( BloomLookup = ethtypes.BloomLookup BytesToBloom = ethtypes.BytesToBloom + CalcUncleHash = ethtypes.CalcUncleHash CopyHeader = ethtypes.CopyHeader CreateBloom = ethtypes.CreateBloom EncodeNonce = ethtypes.EncodeNonce FullAccount = ethtypes.FullAccount FullAccountRLP = ethtypes.FullAccountRLP + NewBlock = ethtypes.NewBlock + NewBlockWithHeader = ethtypes.NewBlockWithHeader NewContractCreation = ethtypes.NewContractCreation NewEmptyStateAccount = ethtypes.NewEmptyStateAccount NewReceipt = ethtypes.NewReceipt diff --git a/core/types/libevm.go b/core/types/libevm.go index a5cc469bca..32d81467e7 100644 --- a/core/types/libevm.go +++ b/core/types/libevm.go @@ -9,6 +9,6 @@ import ( var extras = ethtypes.RegisterExtras[ HeaderExtra, *HeaderExtra, - ethtypes.NOOPBlockBodyHooks, *ethtypes.NOOPBlockBodyHooks, + BlockBodyExtra, *BlockBodyExtra, isMultiCoin, ]() diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index fda588e45b..7cb018b998 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -265,7 +265,7 @@ func (ec *client) getBlock(ctx context.Context, method string, args ...interface } txs[i] = tx.tx } - return types.NewBlockWithHeader(head).WithBody(txs, uncles).WithExtData(body.Version, (*[]byte)(body.BlockExtraData)), nil + return types.NewBlockWithHeader(head).WithBody(types.Body{Transactions: txs, Uncles: uncles}), nil } // HeaderByHash returns the block header with the given hash. diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 05bc4ab4e1..4f883f457b 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1279,7 +1279,7 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} { func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *params.ChainConfig) map[string]interface{} { fields := RPCMarshalHeader(block.Header()) fields["size"] = hexutil.Uint64(block.Size()) - fields["blockExtraData"] = hexutil.Bytes(block.ExtData()) + fields["blockExtraData"] = hexutil.Bytes(types.BlockExtData(block)) if inclTx { formatTx := func(idx int, tx *types.Transaction) interface{} { diff --git a/plugin/evm/block.go b/plugin/evm/block.go index 766167e21f..274928cf8d 100644 --- a/plugin/evm/block.go +++ b/plugin/evm/block.go @@ -120,7 +120,7 @@ type Block struct { // newBlock returns a new Block wrapping the ethBlock type and implementing the snowman.Block interface func (vm *VM) newBlock(ethBlock *types.Block) (*Block, error) { isApricotPhase5 := vm.chainConfigExtra().IsApricotPhase5(ethBlock.Time()) - atomicTxs, err := atomic.ExtractAtomicTxs(ethBlock.ExtData(), isApricotPhase5, atomic.Codec) + atomicTxs, err := atomic.ExtractAtomicTxs(types.BlockExtData(ethBlock), isApricotPhase5, atomic.Codec) if err != nil { return nil, err } diff --git a/plugin/evm/block_verification.go b/plugin/evm/block_verification.go index 7d73e3eece..34fdbb393f 100644 --- a/plugin/evm/block_verification.go +++ b/plugin/evm/block_verification.go @@ -54,7 +54,7 @@ func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error { if !rulesExtra.IsApricotPhase1 { if v.extDataHashes != nil { - extData := b.ethBlock.ExtData() + extData := types.BlockExtData(b.ethBlock) extDataHash := types.CalcExtDataHash(extData) // If there is no extra data, check that there is no extra data in the hash map either to ensure we do not // have a block that is unexpectedly missing extra data. @@ -81,7 +81,9 @@ func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error { // Verify the ExtDataHash field headerExtra := types.GetHeaderExtra(ethHeader) if rulesExtra.IsApricotPhase1 { - if hash := types.CalcExtDataHash(b.ethBlock.ExtData()); headerExtra.ExtDataHash != hash { + extraData := types.BlockExtData(b.ethBlock) + hash := types.CalcExtDataHash(extraData) + if headerExtra.ExtDataHash != hash { return fmt.Errorf("extra data hash mismatch: have %x, want %x", headerExtra.ExtDataHash, hash) } } else { @@ -133,8 +135,8 @@ func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error { return err } - if b.ethBlock.Version() != 0 { - return fmt.Errorf("invalid version: %d", b.ethBlock.Version()) + if version := types.BlockVersion(b.ethBlock); version != 0 { + return fmt.Errorf("invalid version: %d", version) } // Check that the tx hash in the header matches the body diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index 931a123f03..aab53572a0 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -605,7 +605,7 @@ func patchBlock(blk *types.Block, root common.Hash, db ethdb.Database) *types.Bl header.Root = root receipts := rawdb.ReadRawReceipts(db, blk.Hash(), blk.NumberU64()) newBlk := types.NewBlockWithExtData( - header, blk.Transactions(), blk.Uncles(), receipts, trie.NewStackTrie(nil), blk.ExtData(), true, + header, blk.Transactions(), blk.Uncles(), receipts, trie.NewStackTrie(nil), types.BlockExtData(blk), true, ) rawdb.WriteBlock(db, newBlk) rawdb.WriteCanonicalHash(db, newBlk.Hash(), newBlk.NumberU64()) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index b6111d1e4c..904dc5a582 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -935,7 +935,7 @@ func (vm *VM) onExtraStateChange(block *types.Block, state *state.StateDB) (*big rulesExtra = *params.GetRulesExtra(rules) ) - txs, err := atomic.ExtractAtomicTxs(block.ExtData(), rulesExtra.IsApricotPhase5, atomic.Codec) + txs, err := atomic.ExtractAtomicTxs(types.BlockExtData(block), rulesExtra.IsApricotPhase5, atomic.Codec) if err != nil { return nil, nil, err } diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index d79c966d1c..0224f9e2ca 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -2389,7 +2389,7 @@ func TestUncleBlock(t *testing.T) { uncles, nil, trie.NewStackTrie(nil), - blkDEthBlock.ExtData(), + types.BlockExtData(blkDEthBlock), false, ) uncleBlock, err := vm2.newBlock(uncleEthBlock) @@ -2450,7 +2450,7 @@ func TestEmptyBlock(t *testing.T) { false, ) - if len(emptyEthBlock.ExtData()) != 0 || types.GetHeaderExtra(emptyEthBlock.Header()).ExtDataHash != (common.Hash{}) { + if len(types.BlockExtData(emptyEthBlock)) != 0 || types.GetHeaderExtra(emptyEthBlock.Header()).ExtDataHash != (common.Hash{}) { t.Fatalf("emptyEthBlock should not have any extra data") } @@ -2716,7 +2716,7 @@ func TestFutureBlock(t *testing.T) { nil, nil, new(trie.Trie), - internalBlkA.ethBlock.ExtData(), + types.BlockExtData(internalBlkA.ethBlock), false, ) @@ -3271,10 +3271,10 @@ func TestBuildApricotPhase4Block(t *testing.T) { } ethBlk := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock - if eBlockGasCost := ethBlk.BlockGasCost(); eBlockGasCost == nil || eBlockGasCost.Cmp(common.Big0) != 0 { + if eBlockGasCost := types.BlockGasCost(ethBlk); eBlockGasCost == nil || eBlockGasCost.Cmp(common.Big0) != 0 { t.Fatalf("expected blockGasCost to be 0 but got %d", eBlockGasCost) } - if eExtDataGasUsed := ethBlk.ExtDataGasUsed(); eExtDataGasUsed == nil || eExtDataGasUsed.Cmp(big.NewInt(1230)) != 0 { + if eExtDataGasUsed := types.BlockExtDataGasUsed(ethBlk); eExtDataGasUsed == nil || eExtDataGasUsed.Cmp(big.NewInt(1230)) != 0 { t.Fatalf("expected extDataGasUsed to be 1000 but got %d", eExtDataGasUsed) } minRequiredTip, err := header.EstimateRequiredTip(vm.chainConfigExtra(), ethBlk.Header()) @@ -3330,11 +3330,11 @@ func TestBuildApricotPhase4Block(t *testing.T) { } ethBlk = blk.(*chain.BlockWrapper).Block.(*Block).ethBlock - if ethBlk.BlockGasCost() == nil || ethBlk.BlockGasCost().Cmp(big.NewInt(100)) < 0 { - t.Fatalf("expected blockGasCost to be at least 100 but got %d", ethBlk.BlockGasCost()) + if types.BlockGasCost(ethBlk) == nil || types.BlockGasCost(ethBlk).Cmp(big.NewInt(100)) < 0 { + t.Fatalf("expected blockGasCost to be at least 100 but got %d", types.BlockGasCost(ethBlk)) } - if ethBlk.ExtDataGasUsed() == nil || ethBlk.ExtDataGasUsed().Cmp(common.Big0) != 0 { - t.Fatalf("expected extDataGasUsed to be 0 but got %d", ethBlk.ExtDataGasUsed()) + if types.BlockExtDataGasUsed(ethBlk) == nil || types.BlockExtDataGasUsed(ethBlk).Cmp(common.Big0) != 0 { + t.Fatalf("expected extDataGasUsed to be 0 but got %d", types.BlockExtDataGasUsed(ethBlk)) } minRequiredTip, err = header.EstimateRequiredTip(vm.chainConfigExtra(), ethBlk.Header()) if err != nil { @@ -3441,10 +3441,10 @@ func TestBuildApricotPhase5Block(t *testing.T) { } ethBlk := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock - if eBlockGasCost := ethBlk.BlockGasCost(); eBlockGasCost == nil || eBlockGasCost.Cmp(common.Big0) != 0 { + if eBlockGasCost := types.BlockGasCost(ethBlk); eBlockGasCost == nil || eBlockGasCost.Cmp(common.Big0) != 0 { t.Fatalf("expected blockGasCost to be 0 but got %d", eBlockGasCost) } - if eExtDataGasUsed := ethBlk.ExtDataGasUsed(); eExtDataGasUsed == nil || eExtDataGasUsed.Cmp(big.NewInt(11230)) != 0 { + if eExtDataGasUsed := types.BlockExtDataGasUsed(ethBlk); eExtDataGasUsed == nil || eExtDataGasUsed.Cmp(big.NewInt(11230)) != 0 { t.Fatalf("expected extDataGasUsed to be 11230 but got %d", eExtDataGasUsed) } minRequiredTip, err := header.EstimateRequiredTip(vm.chainConfigExtra(), ethBlk.Header()) @@ -3492,11 +3492,11 @@ func TestBuildApricotPhase5Block(t *testing.T) { } ethBlk = blk.(*chain.BlockWrapper).Block.(*Block).ethBlock - if ethBlk.BlockGasCost() == nil || ethBlk.BlockGasCost().Cmp(big.NewInt(100)) < 0 { - t.Fatalf("expected blockGasCost to be at least 100 but got %d", ethBlk.BlockGasCost()) + if types.BlockGasCost(ethBlk) == nil || types.BlockGasCost(ethBlk).Cmp(big.NewInt(100)) < 0 { + t.Fatalf("expected blockGasCost to be at least 100 but got %d", types.BlockGasCost(ethBlk)) } - if ethBlk.ExtDataGasUsed() == nil || ethBlk.ExtDataGasUsed().Cmp(common.Big0) != 0 { - t.Fatalf("expected extDataGasUsed to be 0 but got %d", ethBlk.ExtDataGasUsed()) + if types.BlockExtDataGasUsed(ethBlk) == nil || types.BlockExtDataGasUsed(ethBlk).Cmp(common.Big0) != 0 { + t.Fatalf("expected extDataGasUsed to be 0 but got %d", types.BlockExtDataGasUsed(ethBlk)) } minRequiredTip, err = header.EstimateRequiredTip(vm.chainConfigExtra(), ethBlk.Header()) if err != nil { @@ -3909,7 +3909,7 @@ func TestParentBeaconRootBlock(t *testing.T) { nil, nil, new(trie.Trie), - ethBlock.ExtData(), + types.BlockExtData(ethBlock), false, ) From cdd8775fd46bfe1361791c1d1894779beb284d9f Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 14 Feb 2025 14:28:58 +0100 Subject: [PATCH 02/39] Add tests --- core/types/block_ext_test.go | 414 +++++++++++++++++++++++++++++++++- core/types/header_ext_test.go | 10 +- 2 files changed, 419 insertions(+), 5 deletions(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index df7d988e0c..36ba4cf2bd 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -10,7 +10,10 @@ import ( "unsafe" "github.com/ava-labs/libevm/common" + ethtypes "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/rlp" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestCopyHeader(t *testing.T) { @@ -55,7 +58,7 @@ func TestCopyHeader(t *testing.T) { } func exportedFieldsPointToDifferentMemory[T interface { - Header | HeaderExtra + Header | HeaderExtra | BlockBodyExtra }](t *testing.T, original, cpy *T) { t.Helper() @@ -68,7 +71,7 @@ func exportedFieldsPointToDifferentMemory[T interface { continue } switch field.Type.Kind() { - case reflect.Array, reflect.Uint64: + case reflect.Array, reflect.Uint64, reflect.Uint32: // Not pointers, but using explicit Kinds for safety continue } @@ -82,6 +85,8 @@ func exportedFieldsPointToDifferentMemory[T interface { assertDifferentPointers(t, f, fieldCp) case *uint64: assertDifferentPointers(t, f, fieldCp) + case *[]uint8: + assertDifferentPointers(t, f, fieldCp) case []uint8: assertDifferentPointers(t, unsafe.SliceData(f), unsafe.SliceData(fieldCp.([]uint8))) default: @@ -106,3 +111,408 @@ func assertDifferentPointers[T any](t *testing.T, a *T, b any) { // Note: no need to check `b` is of the same type as `a`, otherwise // the memory address would be different as well. } + +func TestBlockExtraGetWith(t *testing.T) { + t.Parallel() + + b := &Block{} + + extra := GetBlockExtra(b) + require.NotNil(t, extra) + assert.Equal(t, &BlockBodyExtra{}, extra) + + extra = &BlockBodyExtra{ + Version: 1, + } + WithBlockExtra(b, extra) + + extra = GetBlockExtra(b) + wantExtra := &BlockBodyExtra{ + Version: 1, + } + assert.Equal(t, wantExtra, extra) +} + +func TestBodyExtraGetWith(t *testing.T) { + t.Parallel() + + b := &Body{} + + extra := GetBodyExtra(b) + require.NotNil(t, extra) + assert.Equal(t, &BlockBodyExtra{}, extra) + + extra = &BlockBodyExtra{ + Version: 1, + } + WithBodyExtra(b, extra) + + extra = GetBodyExtra(b) + wantExtra := &BlockBodyExtra{ + Version: 1, + } + assert.Equal(t, wantExtra, extra) +} + +func TestBodyExtraRLP(t *testing.T) { + t.Parallel() + + testTx := NewTransaction(0, common.Address{1}, big.NewInt(2), 3, big.NewInt(4), []byte{5}) + + uncle := &Header{ParentHash: common.Hash{6}} + uncleExtra := &HeaderExtra{ExtDataHash: common.Hash{7}} + SetHeaderExtra(uncle, uncleExtra) + + body := &Body{ + Transactions: []*Transaction{testTx}, + Uncles: []*Header{uncle}, + Withdrawals: []*ethtypes.Withdrawal{{Index: 7}}, // ignored + } + extra := &BlockBodyExtra{ + Version: 1, + ExtData: ptrTo([]byte{2}), + } + _ = WithBodyExtra(body, extra) + + b, err := rlp.EncodeToBytes(body) + require.NoError(t, err) + + gotBody := new(Body) + err = rlp.DecodeBytes(b, gotBody) + require.NoError(t, err) + + // Check transactions in the following because unexported fields + // size and time are set by the RLP decoder. + require.Len(t, gotBody.Transactions, 1) + gotTx := gotBody.Transactions[0] + gotTxJSON, err := gotTx.MarshalJSON() + require.NoError(t, err) + wantTxJSON, err := testTx.MarshalJSON() + require.NoError(t, err) + assert.Equal(t, string(wantTxJSON), string(gotTxJSON)) + gotBody.Transactions = nil + + wantUncle := &Header{ + ParentHash: common.Hash{6}, + Difficulty: new(big.Int), + Number: new(big.Int), + Extra: []byte{}, + } + wantUncleExtra := &HeaderExtra{ExtDataHash: common.Hash{7}} + SetHeaderExtra(wantUncle, wantUncleExtra) + + wantBody := &Body{ + Transactions: nil, // checked above + Uncles: []*Header{wantUncle}, + } + wantExtra := &BlockBodyExtra{ + Version: 1, + ExtData: ptrTo([]byte{2}), + } + _ = WithBodyExtra(wantBody, wantExtra) + + assert.Equal(t, wantBody, gotBody) + assert.Equal(t, wantExtra, GetBodyExtra(gotBody)) +} + +func TestBlockExtraRLP(t *testing.T) { + t.Parallel() + + header := &Header{ParentHash: common.Hash{1}} + headerExtras := &HeaderExtra{ExtDataHash: common.Hash{2}} + SetHeaderExtra(header, headerExtras) + + testTx := NewTransaction(3, common.Address{4}, big.NewInt(2), 3, big.NewInt(4), []byte{5}) + txs := []*Transaction{testTx} + + uncle := &Header{ParentHash: common.Hash{6}} + uncleExtra := &HeaderExtra{ExtDataHash: common.Hash{7}} + SetHeaderExtra(uncle, uncleExtra) + uncles := []*Header{uncle} + + receipts := []*Receipt{{PostState: []byte{8}}} + + block := NewBlock(header, txs, uncles, receipts, stubHasher{}) + + withdrawals := []*ethtypes.Withdrawal{{Index: 9}} // ignored + block = block.WithWithdrawals(withdrawals) + + extra := &BlockBodyExtra{ + Version: 10, + ExtData: ptrTo([]byte{11}), + } + _ = WithBlockExtra(block, extra) + + b, err := rlp.EncodeToBytes(block) + require.NoError(t, err) + + gotBlock := new(Block) + err = rlp.DecodeBytes(b, gotBlock) + require.NoError(t, err) + + // Check transactions in the following because unexported fields + // size and time are set by the RLP decoder. + gotTxs := gotBlock.Transactions() + require.Len(t, gotTxs, 1) + gotTx := gotTxs[0] + gotTxBin, err := gotTx.MarshalBinary() + require.NoError(t, err) + wantTxBin, err := testTx.MarshalBinary() + require.NoError(t, err) + assert.Equal(t, wantTxBin, gotTxBin) + + wantHeader := &Header{ + ParentHash: common.Hash{1}, + Extra: []byte{}, // field set by RLP decoder + } + wantHeaderExtras := &HeaderExtra{ExtDataHash: common.Hash{2}} + SetHeaderExtra(wantHeader, wantHeaderExtras) + + wantUncle := &Header{ + ParentHash: common.Hash{6}, + Difficulty: new(big.Int), // field set by RLP decoder + Number: new(big.Int), // field set by RLP decoder + Extra: []byte{}, // field set by RLP decoder + } + wantUncleExtra := &HeaderExtra{ExtDataHash: common.Hash{7}} + SetHeaderExtra(wantUncle, wantUncleExtra) + wantUncles := []*Header{wantUncle} + + wantBlock := NewBlock(wantHeader, gotTxs /* checked above */, wantUncles, receipts, stubHasher{}) + wantExtra := &BlockBodyExtra{ + Version: 10, + ExtData: ptrTo([]byte{11}), + } + _ = WithBlockExtra(wantBlock, wantExtra) + _ = wantBlock.Size() // set block cached unexported size field + + assert.Equal(t, wantBlock, gotBlock) + assert.Equal(t, wantExtra, GetBlockExtra(gotBlock)) +} + +// TestBlockBody tests the [BlockBodyExtra.Copy] method is implemented correctly. +func TestBlockBody(t *testing.T) { + t.Parallel() + + header := &Header{ParentHash: common.Hash{1}} + tx := NewTransaction(0, common.Address{1}, big.NewInt(2), 3, big.NewInt(4), []byte{5}) + txs := []*Transaction{tx} + uncles := []*Header{{ParentHash: common.Hash{6}}} + receipts := []*Receipt{{PostState: []byte{7}}} + block := NewBlock(header, txs, uncles, receipts, stubHasher{}) + extras := &BlockBodyExtra{ + Version: 8, + ExtData: ptrTo([]byte{9}), + } + allExportedFieldsSet(t, extras) // make sure each field is checked + _ = WithBlockExtra(block, extras) + + wantUncle := &Header{ + ParentHash: common.Hash{6}, + Difficulty: new(big.Int), + Number: new(big.Int), + } + // uncle returned from Body() has its extra set, even if the block uncle did not have it set + SetHeaderExtra(wantUncle, &HeaderExtra{}) + wantBody := &Body{ + Transactions: []*Transaction{tx}, + Uncles: []*Header{wantUncle}, + } + wantBodyExtra := &BlockBodyExtra{ + Version: 8, + ExtData: ptrTo([]byte{9}), + } + _ = WithBodyExtra(wantBody, wantBodyExtra) + + body := block.Body() + assert.Equal(t, wantBody, body) + bodyExtra := GetBodyExtra(body) + assert.Equal(t, wantBodyExtra, bodyExtra) + + exportedFieldsPointToDifferentMemory(t, extras, bodyExtra) +} + +func TestBlockGetters(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + headerExtra *HeaderExtra + blockExtra *BlockBodyExtra + wantExtDataGasUsed *big.Int + wantBlockGasCost *big.Int + wantVersion uint32 + wantExtData []byte + }{ + { + name: "empty", + headerExtra: &HeaderExtra{}, + blockExtra: &BlockBodyExtra{}, + }, + { + name: "fields_set", + headerExtra: &HeaderExtra{ + ExtDataGasUsed: big.NewInt(1), + BlockGasCost: big.NewInt(2), + }, + blockExtra: &BlockBodyExtra{ + Version: 3, + ExtData: ptrTo([]byte{4}), + }, + wantExtDataGasUsed: big.NewInt(1), + wantBlockGasCost: big.NewInt(2), + wantVersion: 3, + wantExtData: []byte{4}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + header := &Header{} + SetHeaderExtra(header, test.headerExtra) + + block := NewBlock(header, nil, nil, nil, stubHasher{}) + _ = WithBlockExtra(block, test.blockExtra) + + extData := BlockExtData(block) + assert.Equal(t, test.wantExtData, extData) + + version := BlockVersion(block) + assert.Equal(t, test.wantVersion, version) + + extDataGasUsed := BlockExtDataGasUsed(block) + assert.Equal(t, test.wantExtDataGasUsed, extDataGasUsed) + + blockGasCost := BlockGasCost(block) + assert.Equal(t, test.wantBlockGasCost, blockGasCost) + }) + } +} + +func TestNewBlockWithExtData(t *testing.T) { + t.Parallel() + + // This transaction is generated beforehand because of its unexported time field being set + // on creation. + testTx := NewTransaction(0, common.Address{1}, big.NewInt(2), 3, big.NewInt(4), []byte{5}) + + tests := []struct { + name string + header func() *Header + txs []*Transaction + uncles func() []*Header + receipts []*Receipt + extdata []byte + recalc bool + wantBlock func() *Block + }{ + { + name: "empty", + header: func() *Header { + header := &Header{} + SetHeaderExtra(header, &HeaderExtra{}) + return header + }, + wantBlock: func() *Block { + header := &Header{} + SetHeaderExtra(header, &HeaderExtra{}) + block := NewBlock(header, nil, nil, nil, stubHasher{}) + blockExtra := &BlockBodyExtra{ExtData: ptrTo([]byte{})} + _ = WithBlockExtra(block, blockExtra) + return block + }, + }, + { + name: "with_recalc", + header: func() *Header { + header := &Header{} + extra := &HeaderExtra{ + ExtDataHash: common.Hash{1}, // should be overwritten + } + SetHeaderExtra(header, extra) + return header + }, + extdata: []byte{2}, + recalc: true, + wantBlock: func() *Block { + header := &Header{} + extra := &HeaderExtra{ + ExtDataHash: common.HexToHash("0xf2ee15ea639b73fa3db9b34a245bdfa015c260c598b211bf05a1ecc4b3e3b4f2"), + } + SetHeaderExtra(header, extra) + block := NewBlock(header, nil, nil, nil, stubHasher{}) + blockExtra := &BlockBodyExtra{ExtData: ptrTo([]byte{2})} + _ = WithBlockExtra(block, blockExtra) + return block + }, + }, + { + name: "filled_no_recalc", + header: func() *Header { + header := &Header{GasLimit: 1} + extra := &HeaderExtra{ + ExtDataHash: common.Hash{2}, + ExtDataGasUsed: big.NewInt(3), + BlockGasCost: big.NewInt(4), + } + SetHeaderExtra(header, extra) + return header + }, + txs: []*Transaction{testTx}, + uncles: func() []*Header { + uncle := &Header{GasLimit: 5} + SetHeaderExtra(uncle, &HeaderExtra{BlockGasCost: big.NewInt(6)}) + return []*Header{uncle} + }, + receipts: []*Receipt{{PostState: []byte{7}}}, + extdata: []byte{8}, + wantBlock: func() *Block { + header := &Header{GasLimit: 1} + extra := &HeaderExtra{ + ExtDataHash: common.Hash{2}, + ExtDataGasUsed: big.NewInt(3), + BlockGasCost: big.NewInt(4), + } + SetHeaderExtra(header, extra) + uncle := &Header{GasLimit: 5} + SetHeaderExtra(uncle, &HeaderExtra{BlockGasCost: big.NewInt(6)}) + uncles := []*Header{uncle} + block := NewBlock(header, []*Transaction{testTx}, uncles, []*Receipt{{PostState: []byte{7}}}, stubHasher{}) + blockExtra := &BlockBodyExtra{ExtData: ptrTo([]byte{8})} + _ = WithBlockExtra(block, blockExtra) + return block + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + var uncles []*Header + if test.uncles != nil { + uncles = test.uncles() + } + + block := NewBlockWithExtData( + test.header(), + test.txs, + uncles, + test.receipts, + stubHasher{}, + test.extdata, + test.recalc, + ) + + assert.Equal(t, test.wantBlock(), block) + }) + } +} + +type stubHasher struct{} + +func (h stubHasher) Reset() {} +func (h stubHasher) Update(key, value []byte) error { return nil } +func (h stubHasher) Hash() common.Hash { return common.Hash{} } diff --git a/core/types/header_ext_test.go b/core/types/header_ext_test.go index 769052a552..f05d0cc485 100644 --- a/core/types/header_ext_test.go +++ b/core/types/header_ext_test.go @@ -117,7 +117,7 @@ func headerWithNonZeroFields() (*Header, *HeaderExtra) { } func allExportedFieldsSet[T interface { - ethtypes.Header | HeaderExtra + ethtypes.Header | HeaderExtra | BlockBodyExtra }](t *testing.T, x *T) { // We don't test for nil pointers because we're only confirming that // test-input data is well-formed. A panic due to a dereference will be @@ -140,6 +140,8 @@ func allExportedFieldsSet[T interface { assertNonZero(t, f) case Bloom: assertNonZero(t, f) + case uint32: + assertNonZero(t, f) case uint64: assertNonZero(t, f) case *big.Int: @@ -148,6 +150,8 @@ func allExportedFieldsSet[T interface { assertNonZero(t, f) case *uint64: assertNonZero(t, f) + case *[]uint8: + assertNonZero(t, f) case []uint8: assert.NotEmpty(t, f) default: @@ -158,8 +162,8 @@ func allExportedFieldsSet[T interface { } func assertNonZero[T interface { - common.Hash | common.Address | BlockNonce | uint64 | Bloom | - *big.Int | *common.Hash | *uint64 + common.Hash | common.Address | BlockNonce | uint32 | uint64 | Bloom | + *big.Int | *common.Hash | *uint64 | *[]uint8 }](t *testing.T, v T) { t.Helper() var zero T From 1deea08d6ceea36757e6750827de97e8d7d71a16 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 14 Feb 2025 15:16:33 +0100 Subject: [PATCH 03/39] Fix sync/client test TestGetBlocks --- sync/client/client_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sync/client/client_test.go b/sync/client/client_test.go index 70c7f6f118..559157e76c 100644 --- a/sync/client/client_test.go +++ b/sync/client/client_test.go @@ -266,7 +266,7 @@ func TestGetBlocks(t *testing.T) { return responseBytes }, - expectedErr: "failed to unmarshal response: rlp: expected input list for types.extblock", + expectedErr: "failed to unmarshal response: rlp: expected List and ctx error context canceled", }, "incorrect starting point": { request: message.BlockRequest{ @@ -381,7 +381,7 @@ func TestGetBlocks(t *testing.T) { if err == nil { t.Fatalf("Expected error: %s, but found no error", test.expectedErr) } - assert.True(t, strings.Contains(err.Error(), test.expectedErr), "expected error to contain [%s], but found [%s]", test.expectedErr, err) + assert.ErrorContains(t, err, test.expectedErr) return } if err != nil { From 122356d1302fde773738c4926683b3dc97e1384b Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 21 Feb 2025 17:38:58 +0100 Subject: [PATCH 04/39] Remove exported but only local functions --- core/types/block_ext.go | 31 ++++-------------------- core/types/block_ext_test.go | 46 +++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 48 deletions(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index 61982e405d..e77983c730 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -11,28 +11,6 @@ import ( "github.com/ava-labs/libevm/rlp" ) -// GetBlockExtra returns the [BlockBodyExtra] contained in the [Block] `b`. -func GetBlockExtra(b *Block) *BlockBodyExtra { - return extras.Block.Get(b) -} - -// WithBlockExtra sets the [BlockBodyExtra] `extra` into the [Block] `b`. -func WithBlockExtra(b *Block, extra *BlockBodyExtra) *Block { - extras.Block.Set(b, extra) - return b -} - -// GetBodyExtra returns the [BlockBodyExtra] contained in the [Body] `b`. -func GetBodyExtra(b *Body) *BlockBodyExtra { - return extras.Body.Get(b) -} - -// WithBodyExtra sets the [BlockBodyExtra] `extra` into the [Body] `b`. -func WithBodyExtra(b *Body, extra *BlockBodyExtra) *Body { - extras.Body.Set(b, extra) - return b -} - // BlockBodyExtra is a struct containing extra fields used by Avalanche // in the [Block] and [Body]. type BlockBodyExtra struct { @@ -115,7 +93,7 @@ func (b *BlockBodyExtra) BlockRLPFieldPointersForDecoding(block *ethtypes.BlockR } func BlockExtData(b *Block) []byte { - extras := GetBlockExtra(b) + extras := extras.Block.Get(b) if extras.ExtData == nil { return nil } @@ -123,7 +101,7 @@ func BlockExtData(b *Block) []byte { } func BlockVersion(b *Block) uint32 { - return GetBlockExtra(b).Version + return extras.Block.Get(b).Version } func BlockExtDataGasUsed(b *Block) *big.Int { @@ -158,8 +136,9 @@ func NewBlockWithExtData( block := NewBlock(header, txs, uncles, receipts, hasher) extdataCopy := make([]byte, len(extdata)) copy(extdataCopy, extdata) - extras := &BlockBodyExtra{ + extra := &BlockBodyExtra{ ExtData: &extdataCopy, } - return WithBlockExtra(block, extras) + extras.Block.Set(block, extra) + return block } diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 36ba4cf2bd..0424dec201 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -117,16 +117,16 @@ func TestBlockExtraGetWith(t *testing.T) { b := &Block{} - extra := GetBlockExtra(b) + extra := extras.Block.Get(b) require.NotNil(t, extra) assert.Equal(t, &BlockBodyExtra{}, extra) extra = &BlockBodyExtra{ Version: 1, } - WithBlockExtra(b, extra) + extras.Block.Set(b, extra) - extra = GetBlockExtra(b) + extra = extras.Block.Get(b) wantExtra := &BlockBodyExtra{ Version: 1, } @@ -138,16 +138,16 @@ func TestBodyExtraGetWith(t *testing.T) { b := &Body{} - extra := GetBodyExtra(b) + extra := extras.Body.Get(b) require.NotNil(t, extra) assert.Equal(t, &BlockBodyExtra{}, extra) extra = &BlockBodyExtra{ Version: 1, } - WithBodyExtra(b, extra) + extras.Body.Set(b, extra) - extra = GetBodyExtra(b) + extra = extras.Body.Get(b) wantExtra := &BlockBodyExtra{ Version: 1, } @@ -172,7 +172,7 @@ func TestBodyExtraRLP(t *testing.T) { Version: 1, ExtData: ptrTo([]byte{2}), } - _ = WithBodyExtra(body, extra) + extras.Body.Set(body, extra) b, err := rlp.EncodeToBytes(body) require.NoError(t, err) @@ -209,10 +209,11 @@ func TestBodyExtraRLP(t *testing.T) { Version: 1, ExtData: ptrTo([]byte{2}), } - _ = WithBodyExtra(wantBody, wantExtra) + extras.Body.Set(wantBody, wantExtra) assert.Equal(t, wantBody, gotBody) - assert.Equal(t, wantExtra, GetBodyExtra(gotBody)) + gotExtra := extras.Body.Get(gotBody) + assert.Equal(t, wantExtra, gotExtra) } func TestBlockExtraRLP(t *testing.T) { @@ -241,7 +242,7 @@ func TestBlockExtraRLP(t *testing.T) { Version: 10, ExtData: ptrTo([]byte{11}), } - _ = WithBlockExtra(block, extra) + extras.Block.Set(block, extra) b, err := rlp.EncodeToBytes(block) require.NoError(t, err) @@ -283,11 +284,12 @@ func TestBlockExtraRLP(t *testing.T) { Version: 10, ExtData: ptrTo([]byte{11}), } - _ = WithBlockExtra(wantBlock, wantExtra) + extras.Block.Set(wantBlock, wantExtra) _ = wantBlock.Size() // set block cached unexported size field assert.Equal(t, wantBlock, gotBlock) - assert.Equal(t, wantExtra, GetBlockExtra(gotBlock)) + gotExtra := extras.Block.Get(gotBlock) + assert.Equal(t, wantExtra, gotExtra) } // TestBlockBody tests the [BlockBodyExtra.Copy] method is implemented correctly. @@ -300,12 +302,12 @@ func TestBlockBody(t *testing.T) { uncles := []*Header{{ParentHash: common.Hash{6}}} receipts := []*Receipt{{PostState: []byte{7}}} block := NewBlock(header, txs, uncles, receipts, stubHasher{}) - extras := &BlockBodyExtra{ + blockExtras := &BlockBodyExtra{ Version: 8, ExtData: ptrTo([]byte{9}), } - allExportedFieldsSet(t, extras) // make sure each field is checked - _ = WithBlockExtra(block, extras) + allExportedFieldsSet(t, blockExtras) // make sure each field is checked + extras.Block.Set(block, blockExtras) wantUncle := &Header{ ParentHash: common.Hash{6}, @@ -322,14 +324,14 @@ func TestBlockBody(t *testing.T) { Version: 8, ExtData: ptrTo([]byte{9}), } - _ = WithBodyExtra(wantBody, wantBodyExtra) + extras.Body.Set(wantBody, wantBodyExtra) body := block.Body() assert.Equal(t, wantBody, body) - bodyExtra := GetBodyExtra(body) + bodyExtra := extras.Body.Get(body) assert.Equal(t, wantBodyExtra, bodyExtra) - exportedFieldsPointToDifferentMemory(t, extras, bodyExtra) + exportedFieldsPointToDifferentMemory(t, blockExtras, bodyExtra) } func TestBlockGetters(t *testing.T) { @@ -374,7 +376,7 @@ func TestBlockGetters(t *testing.T) { SetHeaderExtra(header, test.headerExtra) block := NewBlock(header, nil, nil, nil, stubHasher{}) - _ = WithBlockExtra(block, test.blockExtra) + extras.Block.Set(block, test.blockExtra) extData := BlockExtData(block) assert.Equal(t, test.wantExtData, extData) @@ -420,7 +422,7 @@ func TestNewBlockWithExtData(t *testing.T) { SetHeaderExtra(header, &HeaderExtra{}) block := NewBlock(header, nil, nil, nil, stubHasher{}) blockExtra := &BlockBodyExtra{ExtData: ptrTo([]byte{})} - _ = WithBlockExtra(block, blockExtra) + extras.Block.Set(block, blockExtra) return block }, }, @@ -444,7 +446,7 @@ func TestNewBlockWithExtData(t *testing.T) { SetHeaderExtra(header, extra) block := NewBlock(header, nil, nil, nil, stubHasher{}) blockExtra := &BlockBodyExtra{ExtData: ptrTo([]byte{2})} - _ = WithBlockExtra(block, blockExtra) + extras.Block.Set(block, blockExtra) return block }, }, @@ -481,7 +483,7 @@ func TestNewBlockWithExtData(t *testing.T) { uncles := []*Header{uncle} block := NewBlock(header, []*Transaction{testTx}, uncles, []*Receipt{{PostState: []byte{7}}}, stubHasher{}) blockExtra := &BlockBodyExtra{ExtData: ptrTo([]byte{8})} - _ = WithBlockExtra(block, blockExtra) + extras.Block.Set(block, blockExtra) return block }, }, From e1154a14511083fa930e6de8a36c5a27aeca6dee Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 26 Feb 2025 11:29:55 +0100 Subject: [PATCH 05/39] sync/client: has its expectation closer to how it was --- sync/client/client_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sync/client/client_test.go b/sync/client/client_test.go index 559157e76c..a45438bab6 100644 --- a/sync/client/client_test.go +++ b/sync/client/client_test.go @@ -266,7 +266,7 @@ func TestGetBlocks(t *testing.T) { return responseBytes }, - expectedErr: "failed to unmarshal response: rlp: expected List and ctx error context canceled", + expectedErr: "failed to unmarshal response: rlp: expected List", }, "incorrect starting point": { request: message.BlockRequest{ From d5715e304384b978d0158facac137edfafadf2c5 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 26 Feb 2025 11:37:13 +0100 Subject: [PATCH 06/39] ethclient: fix setting extras on block in `getBlock` --- core/types/block_ext.go | 5 +++++ ethclient/ethclient.go | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index e77983c730..f258e5f2b3 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -11,6 +11,11 @@ import ( "github.com/ava-labs/libevm/rlp" ) +// SetBlockExtra sets the [BlockBodyExtra] `extra` in the [Block] `b`. +func SetBlockExtra(b *Block, extra *BlockBodyExtra) { + extras.Block.Set(b, extra) +} + // BlockBodyExtra is a struct containing extra fields used by Avalanche // in the [Block] and [Body]. type BlockBodyExtra struct { diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 7cb018b998..27079c8ee3 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -265,7 +265,14 @@ func (ec *client) getBlock(ctx context.Context, method string, args ...interface } txs[i] = tx.tx } - return types.NewBlockWithHeader(head).WithBody(types.Body{Transactions: txs, Uncles: uncles}), nil + block := types.NewBlockWithHeader(head) + block = block.WithBody(types.Body{Transactions: txs, Uncles: uncles}) + extra := &types.BlockBodyExtra{ + Version: body.Version, + ExtData: (*[]byte)(body.BlockExtraData), + } + types.SetBlockExtra(block, extra) + return block, nil } // HeaderByHash returns the block header with the given hash. From 2c9f9e4fbf4f1a7773690c28b04ecf258ddd9a76 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 26 Feb 2025 11:42:32 +0100 Subject: [PATCH 07/39] core/types: move comment on RLP field differences with geth to rlp encoding methods --- core/types/block_ext.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index f258e5f2b3..f46a968c10 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -21,9 +21,6 @@ func SetBlockExtra(b *Block, extra *BlockBodyExtra) { type BlockBodyExtra struct { Version uint32 ExtData *[]byte - - // RLP block and body field removed from geth: - // - Withdrawals []*Withdrawal } // Copy deep copies the [BlockBodyExtra] `b` and returns it. @@ -45,6 +42,10 @@ func (b *BlockBodyExtra) Copy() *BlockBodyExtra { // BodyRLPFieldPointersForEncoding returns the fields that should be encoded // for the [Body] and [BlockBodyExtra]. +// Note the following fields are added (+) and removed (-) compared to geth: +// - (-) [ethtypes.Body] `Withdrawals` field +// - (+) [BlockBodyExtra] `Version` field +// - (+) [BlockBodyExtra] `ExtData` field func (b *BlockBodyExtra) BodyRLPFieldsForEncoding(body *Body) *rlp.Fields { return &rlp.Fields{ Required: []any{ @@ -71,6 +72,10 @@ func (b *BlockBodyExtra) BodyRLPFieldPointersForDecoding(body *Body) *rlp.Fields // BlockRLPFieldPointersForEncoding returns the fields that should be encoded // for the [Block] and [BlockBodyExtra]. +// Note the following fields are added (+) and removed (-) compared to geth: +// - (-) [ethtypes.Body] `Withdrawals` field +// - (+) [BlockBodyExtra] `Version` field +// - (+) [BlockBodyExtra] `ExtData` field func (b *BlockBodyExtra) BlockRLPFieldsForEncoding(block *ethtypes.BlockRLPProxy) *rlp.Fields { return &rlp.Fields{ Required: []any{ From 7ec7bccdd305e960b3043836bffdc6286145a697 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 26 Feb 2025 11:44:59 +0100 Subject: [PATCH 08/39] core/types: improve comment on Copy method to have godoc links to methods --- core/types/block_ext.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index f46a968c10..1316b5f67b 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -25,8 +25,10 @@ type BlockBodyExtra struct { // Copy deep copies the [BlockBodyExtra] `b` and returns it. // It is notably used in the following functions: -// - [Block]'s Body() method -// - [Block]'s With*() methods +// - [ethtypes.Block.Body] +// - [ethtypes.Block.WithSeal] +// - [ethtypes.Block.WithBody] +// - [ethtypes.Block.WithWithdrawals] func (b *BlockBodyExtra) Copy() *BlockBodyExtra { cpy := *b if b.ExtData == nil { From b1b7b8a4c472ab0c8fa4691c280577f5cf64676a Mon Sep 17 00:00:00 2001 From: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Date: Wed, 26 Feb 2025 11:45:30 +0100 Subject: [PATCH 09/39] core/types: small simplifications in BlockBodyExtra.Copy method --- core/types/block_ext.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index 1316b5f67b..bf27c35a0c 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -5,6 +5,7 @@ package types import ( "math/big" + "slices" "github.com/ava-labs/libevm/common" ethtypes "github.com/ava-labs/libevm/core/types" @@ -32,11 +33,9 @@ type BlockBodyExtra struct { func (b *BlockBodyExtra) Copy() *BlockBodyExtra { cpy := *b if b.ExtData == nil { - data := make([]byte, 0) - cpy.ExtData = &data + cpy.ExtData = &[]byte{} } else { - data := make([]byte, len(*b.ExtData)) - copy(data, *b.ExtData) + data := slices.Clone(*b.ExtData) cpy.ExtData = &data } return &cpy From 6e3a2d54fc77f36e50d63f650ab353c6c2093a5a Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Wed, 26 Feb 2025 11:57:54 +0100 Subject: [PATCH 10/39] core/types: small simplification for `BlockExtData` Co-authored-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Signed-off-by: Quentin McGaw --- core/types/block_ext.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index bf27c35a0c..2c1e578523 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -104,8 +104,10 @@ func (b *BlockBodyExtra) BlockRLPFieldPointersForDecoding(block *ethtypes.BlockR } func BlockExtData(b *Block) []byte { - extras := extras.Block.Get(b) - if extras.ExtData == nil { + if data := extras.Block.Get(b).ExtData; data != nil { + return *data + } + return nil return nil } return *extras.ExtData From a8d090cf023b52be372c8464c52bca535aca6009 Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Wed, 26 Feb 2025 11:58:53 +0100 Subject: [PATCH 11/39] core/types: fix comment of BlockRLPFieldsForEncoding Signed-off-by: Quentin McGaw --- core/types/block_ext.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index 2c1e578523..7833adfb24 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -74,7 +74,7 @@ func (b *BlockBodyExtra) BodyRLPFieldPointersForDecoding(body *Body) *rlp.Fields // BlockRLPFieldPointersForEncoding returns the fields that should be encoded // for the [Block] and [BlockBodyExtra]. // Note the following fields are added (+) and removed (-) compared to geth: -// - (-) [ethtypes.Body] `Withdrawals` field +// - (-) [ethtypes.Block] `Withdrawals` field // - (+) [BlockBodyExtra] `Version` field // - (+) [BlockBodyExtra] `ExtData` field func (b *BlockBodyExtra) BlockRLPFieldsForEncoding(block *ethtypes.BlockRLPProxy) *rlp.Fields { From 0568fb4af95cca9bb86bd7dcd9d71005b2117838 Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Wed, 26 Feb 2025 12:03:10 +0100 Subject: [PATCH 12/39] core/types: safer and more concise BlockGasCost function - do not rely on Block.Header method call deep copying BlockGasCost - improved style Co-authored-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Signed-off-by: Quentin McGaw --- core/types/block_ext.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index 7833adfb24..79af8a0d06 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -126,9 +126,8 @@ func BlockExtDataGasUsed(b *Block) *big.Int { } func BlockGasCost(b *Block) *big.Int { - header := b.Header() // note this deep copies headerExtra.BlockGasCost - headerExtra := GetHeaderExtra(header) - return headerExtra.BlockGasCost + cost := GetHeaderExtra(b.Header()).BlockGasCost + return new(big.Int).Set(cost) } func CalcExtDataHash(extdata []byte) common.Hash { From 7e16659a5f78c4a358c5d24f1d6fea3960fe3078 Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Wed, 26 Feb 2025 12:07:09 +0100 Subject: [PATCH 13/39] core/types: use slices.Clone in NewBlockWithExtData Co-authored-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Signed-off-by: Quentin McGaw --- core/types/block_ext.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index 79af8a0d06..662416ca23 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -146,8 +146,7 @@ func NewBlockWithExtData( headerExtra.ExtDataHash = CalcExtDataHash(extdata) } block := NewBlock(header, txs, uncles, receipts, hasher) - extdataCopy := make([]byte, len(extdata)) - copy(extdataCopy, extdata) + extdataCopy := slices.Clone(extdata) extra := &BlockBodyExtra{ ExtData: &extdataCopy, } From 817bede141ba77aac7d2a3336bccd1b0caf7e104 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 26 Feb 2025 13:32:00 +0100 Subject: [PATCH 14/39] core/types: fix bad commit suggestion --- core/types/block_ext.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index 662416ca23..0dd2f29dff 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -108,9 +108,6 @@ func BlockExtData(b *Block) []byte { return *data } return nil - return nil - } - return *extras.ExtData } func BlockVersion(b *Block) uint32 { From 64ca33d182b1c69f0848e7465a55242086e0ee3d Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 26 Feb 2025 13:43:12 +0100 Subject: [PATCH 15/39] core/types: reverting slices.Clone in NewBlockWithExtData --- core/types/block_ext.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index 0dd2f29dff..dd9be60b3a 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -143,7 +143,8 @@ func NewBlockWithExtData( headerExtra.ExtDataHash = CalcExtDataHash(extdata) } block := NewBlock(header, txs, uncles, receipts, hasher) - extdataCopy := slices.Clone(extdata) + extdataCopy := make([]byte, len(extdata)) + copy(extdataCopy, extdata) extra := &BlockBodyExtra{ ExtData: &extdataCopy, } From 6c32a6a835efe8074036076985b8f93c2ddc9cd3 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 26 Feb 2025 14:21:14 +0100 Subject: [PATCH 16/39] core/types: fix bad suggestion in BlockGasCost --- core/types/block_ext.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index dd9be60b3a..cc35146e7e 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -124,6 +124,9 @@ func BlockExtDataGasUsed(b *Block) *big.Int { func BlockGasCost(b *Block) *big.Int { cost := GetHeaderExtra(b.Header()).BlockGasCost + if cost == nil { + return nil + } return new(big.Int).Set(cost) } From d271e391eec16567a369e0aad95fa0a59768135c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 26 Feb 2025 14:25:47 +0100 Subject: [PATCH 17/39] core/types: revet to use slices.Clone in NewBlockWithExtData --- core/types/block_ext.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index cc35146e7e..949fdd99ef 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -146,8 +146,7 @@ func NewBlockWithExtData( headerExtra.ExtDataHash = CalcExtDataHash(extdata) } block := NewBlock(header, txs, uncles, receipts, hasher) - extdataCopy := make([]byte, len(extdata)) - copy(extdataCopy, extdata) + extdataCopy := slices.Clone(extdata) extra := &BlockBodyExtra{ ExtData: &extdataCopy, } From 0332484bc986421278a7fb45452ef044aea354de Mon Sep 17 00:00:00 2001 From: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Date: Wed, 26 Feb 2025 13:55:10 +0000 Subject: [PATCH 18/39] refactor(core/types): body and block hooks tests (#841) Targeting #760 --- core/types/block_ext_test.go | 209 ++++++++++------------------------- go.mod | 1 + 2 files changed, 62 insertions(+), 148 deletions(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 0424dec201..68b147eee7 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -12,6 +12,8 @@ import ( "github.com/ava-labs/libevm/common" ethtypes "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/rlp" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -112,108 +114,62 @@ func assertDifferentPointers[T any](t *testing.T, a *T, b any) { // the memory address would be different as well. } -func TestBlockExtraGetWith(t *testing.T) { - t.Parallel() - - b := &Block{} - - extra := extras.Block.Get(b) - require.NotNil(t, extra) - assert.Equal(t, &BlockBodyExtra{}, extra) - - extra = &BlockBodyExtra{ - Version: 1, - } - extras.Block.Set(b, extra) - - extra = extras.Block.Get(b) - wantExtra := &BlockBodyExtra{ - Version: 1, - } - assert.Equal(t, wantExtra, extra) +func txHashComparer() cmp.Option { + return cmp.Comparer(func(a, b *Transaction) bool { + return a.Hash() == b.Hash() + }) } -func TestBodyExtraGetWith(t *testing.T) { - t.Parallel() - - b := &Body{} - - extra := extras.Body.Get(b) - require.NotNil(t, extra) - assert.Equal(t, &BlockBodyExtra{}, extra) - - extra = &BlockBodyExtra{ - Version: 1, - } - extras.Body.Set(b, extra) - - extra = extras.Body.Get(b) - wantExtra := &BlockBodyExtra{ - Version: 1, - } - assert.Equal(t, wantExtra, extra) +func headerHashComparer() cmp.Option { + return cmp.Comparer(func(a, b *Header) bool { + return a.Hash() == b.Hash() + }) } func TestBodyExtraRLP(t *testing.T) { t.Parallel() - testTx := NewTransaction(0, common.Address{1}, big.NewInt(2), 3, big.NewInt(4), []byte{5}) + tx := NewTransaction(0, common.Address{1}, big.NewInt(2), 3, big.NewInt(4), []byte{5}) uncle := &Header{ParentHash: common.Hash{6}} uncleExtra := &HeaderExtra{ExtDataHash: common.Hash{7}} SetHeaderExtra(uncle, uncleExtra) body := &Body{ - Transactions: []*Transaction{testTx}, + Transactions: []*Transaction{tx}, Uncles: []*Header{uncle}, Withdrawals: []*ethtypes.Withdrawal{{Index: 7}}, // ignored } extra := &BlockBodyExtra{ Version: 1, - ExtData: ptrTo([]byte{2}), + ExtData: &[]byte{2}, } extras.Body.Set(body, extra) - b, err := rlp.EncodeToBytes(body) + encoded, err := rlp.EncodeToBytes(body) require.NoError(t, err) gotBody := new(Body) - err = rlp.DecodeBytes(b, gotBody) + err = rlp.DecodeBytes(encoded, gotBody) require.NoError(t, err) - // Check transactions in the following because unexported fields - // size and time are set by the RLP decoder. - require.Len(t, gotBody.Transactions, 1) - gotTx := gotBody.Transactions[0] - gotTxJSON, err := gotTx.MarshalJSON() - require.NoError(t, err) - wantTxJSON, err := testTx.MarshalJSON() - require.NoError(t, err) - assert.Equal(t, string(wantTxJSON), string(gotTxJSON)) - gotBody.Transactions = nil - - wantUncle := &Header{ - ParentHash: common.Hash{6}, - Difficulty: new(big.Int), - Number: new(big.Int), - Extra: []byte{}, - } - wantUncleExtra := &HeaderExtra{ExtDataHash: common.Hash{7}} - SetHeaderExtra(wantUncle, wantUncleExtra) + wantBody := body + wantBody.Withdrawals = nil - wantBody := &Body{ - Transactions: nil, // checked above - Uncles: []*Header{wantUncle}, + opts := cmp.Options{ + txHashComparer(), + headerHashComparer(), + cmpopts.IgnoreUnexported(Body{}), } - wantExtra := &BlockBodyExtra{ - Version: 1, - ExtData: ptrTo([]byte{2}), + if diff := cmp.Diff(wantBody, gotBody, opts); diff != "" { + t.Errorf("%T diff after RLP round-trip (-want +got):\n%s", wantBody, diff) } - extras.Body.Set(wantBody, wantExtra) - assert.Equal(t, wantBody, gotBody) gotExtra := extras.Body.Get(gotBody) - assert.Equal(t, wantExtra, gotExtra) + wantExtra := extra + if diff := cmp.Diff(wantExtra, gotExtra); diff != "" { + t.Errorf("%T diff after RLP round-trip of %T (-want +got):\n%s", wantExtra, wantBody, diff) + } } func TestBlockExtraRLP(t *testing.T) { @@ -223,8 +179,8 @@ func TestBlockExtraRLP(t *testing.T) { headerExtras := &HeaderExtra{ExtDataHash: common.Hash{2}} SetHeaderExtra(header, headerExtras) - testTx := NewTransaction(3, common.Address{4}, big.NewInt(2), 3, big.NewInt(4), []byte{5}) - txs := []*Transaction{testTx} + tx := NewTransaction(3, common.Address{4}, big.NewInt(2), 3, big.NewInt(4), []byte{5}) + txs := []*Transaction{tx} uncle := &Header{ParentHash: common.Hash{6}} uncleExtra := &HeaderExtra{ExtDataHash: common.Hash{7}} @@ -240,98 +196,55 @@ func TestBlockExtraRLP(t *testing.T) { extra := &BlockBodyExtra{ Version: 10, - ExtData: ptrTo([]byte{11}), + ExtData: &[]byte{11}, } extras.Block.Set(block, extra) - b, err := rlp.EncodeToBytes(block) + encoded, err := rlp.EncodeToBytes(block) require.NoError(t, err) gotBlock := new(Block) - err = rlp.DecodeBytes(b, gotBlock) + err = rlp.DecodeBytes(encoded, gotBlock) require.NoError(t, err) - // Check transactions in the following because unexported fields - // size and time are set by the RLP decoder. - gotTxs := gotBlock.Transactions() - require.Len(t, gotTxs, 1) - gotTx := gotTxs[0] - gotTxBin, err := gotTx.MarshalBinary() - require.NoError(t, err) - wantTxBin, err := testTx.MarshalBinary() - require.NoError(t, err) - assert.Equal(t, wantTxBin, gotTxBin) + wantBlock := block.WithWithdrawals(nil) - wantHeader := &Header{ - ParentHash: common.Hash{1}, - Extra: []byte{}, // field set by RLP decoder - } - wantHeaderExtras := &HeaderExtra{ExtDataHash: common.Hash{2}} - SetHeaderExtra(wantHeader, wantHeaderExtras) - - wantUncle := &Header{ - ParentHash: common.Hash{6}, - Difficulty: new(big.Int), // field set by RLP decoder - Number: new(big.Int), // field set by RLP decoder - Extra: []byte{}, // field set by RLP decoder + opts := cmp.Options{ + txHashComparer(), + headerHashComparer(), + cmpopts.IgnoreUnexported(Block{}), } - wantUncleExtra := &HeaderExtra{ExtDataHash: common.Hash{7}} - SetHeaderExtra(wantUncle, wantUncleExtra) - wantUncles := []*Header{wantUncle} - - wantBlock := NewBlock(wantHeader, gotTxs /* checked above */, wantUncles, receipts, stubHasher{}) - wantExtra := &BlockBodyExtra{ - Version: 10, - ExtData: ptrTo([]byte{11}), + if diff := cmp.Diff(wantBlock, gotBlock, opts); diff != "" { + t.Errorf("%T diff after RLP round-trip (-want +got):\n%s", gotBlock, diff) } - extras.Block.Set(wantBlock, wantExtra) - _ = wantBlock.Size() // set block cached unexported size field - assert.Equal(t, wantBlock, gotBlock) gotExtra := extras.Block.Get(gotBlock) - assert.Equal(t, wantExtra, gotExtra) + wantExtra := extra + if diff := cmp.Diff(wantExtra, gotExtra); diff != "" { + t.Errorf("%T diff after RLP round-trip of %T (-want +got):\n%s", wantExtra, wantBlock, diff) + } } // TestBlockBody tests the [BlockBodyExtra.Copy] method is implemented correctly. func TestBlockBody(t *testing.T) { t.Parallel() - header := &Header{ParentHash: common.Hash{1}} - tx := NewTransaction(0, common.Address{1}, big.NewInt(2), 3, big.NewInt(4), []byte{5}) - txs := []*Transaction{tx} - uncles := []*Header{{ParentHash: common.Hash{6}}} - receipts := []*Receipt{{PostState: []byte{7}}} - block := NewBlock(header, txs, uncles, receipts, stubHasher{}) blockExtras := &BlockBodyExtra{ - Version: 8, - ExtData: ptrTo([]byte{9}), + Version: 1, + ExtData: &[]byte{2}, } allExportedFieldsSet(t, blockExtras) // make sure each field is checked + block := NewBlock(&Header{}, nil, nil, nil, stubHasher{}) extras.Block.Set(block, blockExtras) - wantUncle := &Header{ - ParentHash: common.Hash{6}, - Difficulty: new(big.Int), - Number: new(big.Int), - } - // uncle returned from Body() has its extra set, even if the block uncle did not have it set - SetHeaderExtra(wantUncle, &HeaderExtra{}) - wantBody := &Body{ - Transactions: []*Transaction{tx}, - Uncles: []*Header{wantUncle}, - } - wantBodyExtra := &BlockBodyExtra{ - Version: 8, - ExtData: ptrTo([]byte{9}), + wantExtra := &BlockBodyExtra{ + Version: 1, + ExtData: &[]byte{2}, } - extras.Body.Set(wantBody, wantBodyExtra) - - body := block.Body() - assert.Equal(t, wantBody, body) - bodyExtra := extras.Body.Get(body) - assert.Equal(t, wantBodyExtra, bodyExtra) + gotExtra := extras.Body.Get(block.Body()) // [types.Block.Body] invokes [BlockBodyExtra.Copy] + assert.Equal(t, wantExtra, gotExtra) - exportedFieldsPointToDifferentMemory(t, blockExtras, bodyExtra) + exportedFieldsPointToDifferentMemory(t, blockExtras, gotExtra) } func TestBlockGetters(t *testing.T) { @@ -359,7 +272,7 @@ func TestBlockGetters(t *testing.T) { }, blockExtra: &BlockBodyExtra{ Version: 3, - ExtData: ptrTo([]byte{4}), + ExtData: &[]byte{4}, }, wantExtDataGasUsed: big.NewInt(1), wantBlockGasCost: big.NewInt(2), @@ -379,16 +292,16 @@ func TestBlockGetters(t *testing.T) { extras.Block.Set(block, test.blockExtra) extData := BlockExtData(block) - assert.Equal(t, test.wantExtData, extData) + assert.Equal(t, test.wantExtData, extData, "BlockExtData()") version := BlockVersion(block) - assert.Equal(t, test.wantVersion, version) + assert.Equal(t, test.wantVersion, version, "BlockVersion()") extDataGasUsed := BlockExtDataGasUsed(block) - assert.Equal(t, test.wantExtDataGasUsed, extDataGasUsed) + assert.Equal(t, test.wantExtDataGasUsed, extDataGasUsed, "BlockExtDataGasUsed()") blockGasCost := BlockGasCost(block) - assert.Equal(t, test.wantBlockGasCost, blockGasCost) + assert.Equal(t, test.wantBlockGasCost, blockGasCost, "BlockGasCost()") }) } } @@ -421,7 +334,7 @@ func TestNewBlockWithExtData(t *testing.T) { header := &Header{} SetHeaderExtra(header, &HeaderExtra{}) block := NewBlock(header, nil, nil, nil, stubHasher{}) - blockExtra := &BlockBodyExtra{ExtData: ptrTo([]byte{})} + blockExtra := &BlockBodyExtra{ExtData: &[]byte{}} extras.Block.Set(block, blockExtra) return block }, @@ -441,11 +354,11 @@ func TestNewBlockWithExtData(t *testing.T) { wantBlock: func() *Block { header := &Header{} extra := &HeaderExtra{ - ExtDataHash: common.HexToHash("0xf2ee15ea639b73fa3db9b34a245bdfa015c260c598b211bf05a1ecc4b3e3b4f2"), + ExtDataHash: CalcExtDataHash([]byte{2}), } SetHeaderExtra(header, extra) block := NewBlock(header, nil, nil, nil, stubHasher{}) - blockExtra := &BlockBodyExtra{ExtData: ptrTo([]byte{2})} + blockExtra := &BlockBodyExtra{ExtData: &[]byte{2}} extras.Block.Set(block, blockExtra) return block }, @@ -482,7 +395,7 @@ func TestNewBlockWithExtData(t *testing.T) { SetHeaderExtra(uncle, &HeaderExtra{BlockGasCost: big.NewInt(6)}) uncles := []*Header{uncle} block := NewBlock(header, []*Transaction{testTx}, uncles, []*Receipt{{PostState: []byte{7}}}, stubHasher{}) - blockExtra := &BlockBodyExtra{ExtData: ptrTo([]byte{8})} + blockExtra := &BlockBodyExtra{ExtData: &[]byte{8}} extras.Block.Set(block, blockExtra) return block }, diff --git a/go.mod b/go.mod index 9e25296e41..d6b71d4458 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set/v2 v2.1.0 + github.com/google/go-cmp v0.6.0 github.com/gorilla/rpc v1.2.0 github.com/gorilla/websocket v1.5.0 github.com/hashicorp/go-bexpr v0.1.10 From 5b0f05cf88352762f7a8b412bcc00483383bbd93 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 26 Feb 2025 14:59:29 +0100 Subject: [PATCH 19/39] core/types: revert again to not use slices.Clone in NewBlockWithExtData to keep it safe --- core/types/block_ext.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index 949fdd99ef..cc35146e7e 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -146,7 +146,8 @@ func NewBlockWithExtData( headerExtra.ExtDataHash = CalcExtDataHash(extdata) } block := NewBlock(header, txs, uncles, receipts, hasher) - extdataCopy := slices.Clone(extdata) + extdataCopy := make([]byte, len(extdata)) + copy(extdataCopy, extdata) extra := &BlockBodyExtra{ ExtData: &extdataCopy, } From 6c81d6d5a02045b9ce00945d631c2bea14237ea9 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 26 Feb 2025 16:39:52 +0100 Subject: [PATCH 20/39] core/types: add golden data test TestBodyRLP --- core/types/block_ext_test.go | 71 +++++++++++++++++++++++++++++++++++ core/types/header_ext_test.go | 2 +- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 68b147eee7..bfc6c10989 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -4,6 +4,7 @@ package types import ( + "encoding/hex" "math/big" "reflect" "testing" @@ -114,6 +115,76 @@ func assertDifferentPointers[T any](t *testing.T, a *T, b any) { // the memory address would be different as well. } +func TestBodyRLP(t *testing.T) { + t.Parallel() + + input, _ := bodyWithNonZeroFields() // the [Body] carries the [BlockBodyExtra] so we can ignore it + encoded, err := rlp.EncodeToBytes(input) + require.NoError(t, err, "encode") + + gotBody := new(Body) + err = rlp.DecodeBytes(encoded, gotBody) + require.NoError(t, err, "decode") + gotExtra := extras.Body.Get(gotBody) + + wantBody, wantExtra := bodyWithNonZeroFields() + + wantBody.Withdrawals = nil // this should be ignored + opts := cmp.Options{ + txHashComparer(), + headerHashComparer(), + cmpopts.IgnoreUnexported(Body{}), + } + if diff := cmp.Diff(wantBody, gotBody, opts); diff != "" { + t.Errorf("%T diff after RLP round-trip (-want +got):\n%s", wantBody, diff) + } + assert.Equal(t, wantExtra, gotExtra, "extra") + + // Golden data from original coreth implementation, before integration of + // libevm. WARNING: changing these values can break backwards compatibility + // with extreme consequences. + const wantHex = "f90235dedd0105049402000000000000000000000000000000000000000306808080f90211f9020ea00900000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070880808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00a000000000000000000000000000000000000000000000000000000000000000c0d" + + assert.Equal(t, wantHex, hex.EncodeToString(encoded), "golden data") +} + +// bodyWithNonZeroFields returns a [Body] and a [BlockBodyExtra], +// each with all fields set to non-zero values. +// The [BlockBodyExtra] extra payload is set in the [Body] via `extras.Body.Set`. +// +// NOTE: They can be used to demonstrate that RLP round-trip encoding +// can recover all fields, but not that the encoded format is correct. This is +// very important as the RLP encoding of a [Body] defines its hash. +func bodyWithNonZeroFields() (*Body, *BlockBodyExtra) { + tx := NewTransaction(1, common.Address{2}, big.NewInt(3), 4, big.NewInt(5), []byte{6}) + uncle := &Header{ + Difficulty: big.NewInt(7), + Number: big.NewInt(8), + ParentHash: common.Hash{9}, + } + uncleExtra := &HeaderExtra{ExtDataHash: common.Hash{10}} + SetHeaderExtra(uncle, uncleExtra) + body := &Body{ + Transactions: []*Transaction{tx}, + Uncles: []*Header{uncle}, + Withdrawals: []*ethtypes.Withdrawal{{Index: 11}}, + } + extra := &BlockBodyExtra{ + Version: 12, + ExtData: &[]byte{13}, + } + extras.Body.Set(body, extra) + return body, extra +} + +func TestBodyWithNonZeroFields(t *testing.T) { + t.Parallel() + + body, extra := bodyWithNonZeroFields() + t.Run("Body", func(t *testing.T) { allExportedFieldsSet(t, body) }) + t.Run("BodyExtra", func(t *testing.T) { allExportedFieldsSet(t, extra) }) +} + func txHashComparer() cmp.Option { return cmp.Comparer(func(a, b *Transaction) bool { return a.Hash() == b.Hash() diff --git a/core/types/header_ext_test.go b/core/types/header_ext_test.go index f05d0cc485..3c228727f6 100644 --- a/core/types/header_ext_test.go +++ b/core/types/header_ext_test.go @@ -117,7 +117,7 @@ func headerWithNonZeroFields() (*Header, *HeaderExtra) { } func allExportedFieldsSet[T interface { - ethtypes.Header | HeaderExtra | BlockBodyExtra + Header | HeaderExtra | Body | BlockBodyExtra }](t *testing.T, x *T) { // We don't test for nil pointers because we're only confirming that // test-input data is well-formed. A panic due to a dereference will be From 876aaf358f91a40003a8b88ed44641d1f05047c2 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 26 Feb 2025 17:51:37 +0100 Subject: [PATCH 21/39] core/types: add golden data test TestBlockRLP --- core/types/block_ext_test.go | 62 +++++++++++++++++++++++++++++++++++ core/types/header_ext_test.go | 2 +- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index bfc6c10989..0063bfdb7d 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -148,6 +148,68 @@ func TestBodyRLP(t *testing.T) { assert.Equal(t, wantHex, hex.EncodeToString(encoded), "golden data") } +// blockWithNonZeroFields returns a [Block] and a [BlockBodyExtra], +// each with all fields set to non-zero values. +// The [BlockBodyExtra] extra payload is set in the [Block] via `extras.Block.Set`. +// +// NOTE: They can be used to demonstrate that RLP round-trip encoding +// can recover all fields, but not that the encoded format is correct. This is +// very important as the RLP encoding of a [Block] defines its hash. +func blockWithNonZeroFields() (*Block, *BlockBodyExtra) { + header := &Header{ + ParentHash: common.Hash{1}, + } + headerExtra := &HeaderExtra{ + ExtDataHash: common.Hash{2}, + } + SetHeaderExtra(header, headerExtra) + + tx := NewTransaction(1, common.Address{2}, big.NewInt(3), 4, big.NewInt(5), []byte{6}) + txs := []*Transaction{tx} + + uncle := &Header{ + Difficulty: big.NewInt(7), + Number: big.NewInt(8), + ParentHash: common.Hash{9}, + } + uncleExtra := &HeaderExtra{ExtDataHash: common.Hash{10}} + SetHeaderExtra(uncle, uncleExtra) + uncles := []*Header{uncle} + + receipts := []*Receipt{{PostState: []byte{11}}} + + block := NewBlock(header, txs, uncles, receipts, stubHasher{}) + extra := &BlockBodyExtra{ + Version: 12, + ExtData: &[]byte{13}, + } + extras.Block.Set(block, extra) + return block, extra +} + +func TestBlockWithNonZeroFields(t *testing.T) { + t.Parallel() + + block, extra := blockWithNonZeroFields() + t.Run("Block", func(t *testing.T) { allExportedFieldsSet(t, block) }) + t.Run("BlockExtra", func(t *testing.T) { allExportedFieldsSet(t, extra) }) +} + +func TestBlockRLP(t *testing.T) { + t.Parallel() + + input, _ := blockWithNonZeroFields() // the [Block] carries the [BlockBodyExtra] so we can ignore it + encoded, err := rlp.EncodeToBytes(input) + require.NoError(t, err, "encode") + + // Golden data from original coreth implementation, before integration of + // libevm. WARNING: changing these values can break backwards compatibility + // with extreme consequences. + const wantHex = "f90446f9020ea00100000000000000000000000000000000000000000000000000000000000000a008539331084089cedbaf7771d0f5f69847f246e0676e4d96091a49c53c89360b940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808080808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00200000000000000000000000000000000000000000000000000000000000000dedd0105049402000000000000000000000000000000000000000306808080f90211f9020ea00900000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070880808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00a000000000000000000000000000000000000000000000000000000000000000c0d" + + assert.Equal(t, wantHex, hex.EncodeToString(encoded), "golden data") +} + // bodyWithNonZeroFields returns a [Body] and a [BlockBodyExtra], // each with all fields set to non-zero values. // The [BlockBodyExtra] extra payload is set in the [Body] via `extras.Body.Set`. diff --git a/core/types/header_ext_test.go b/core/types/header_ext_test.go index 3c228727f6..540bcd81a8 100644 --- a/core/types/header_ext_test.go +++ b/core/types/header_ext_test.go @@ -117,7 +117,7 @@ func headerWithNonZeroFields() (*Header, *HeaderExtra) { } func allExportedFieldsSet[T interface { - Header | HeaderExtra | Body | BlockBodyExtra + Header | HeaderExtra | Block | Body | BlockBodyExtra }](t *testing.T, x *T) { // We don't test for nil pointers because we're only confirming that // test-input data is well-formed. A panic due to a dereference will be From aed154c65d1dfbaa673eb8263f0cdf58ef624e82 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 26 Feb 2025 18:18:25 +0100 Subject: [PATCH 22/39] Fix `TestBlockWithNonZeroFields` by ignoring block fields that cannot be set --- core/types/block_ext_test.go | 2 +- core/types/header_ext_test.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 0063bfdb7d..e1f368607c 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -191,7 +191,7 @@ func TestBlockWithNonZeroFields(t *testing.T) { t.Parallel() block, extra := blockWithNonZeroFields() - t.Run("Block", func(t *testing.T) { allExportedFieldsSet(t, block) }) + t.Run("Block", func(t *testing.T) { allExportedFieldsSet(t, block, "ReceivedAt", "ReceivedFrom") }) t.Run("BlockExtra", func(t *testing.T) { allExportedFieldsSet(t, extra) }) } diff --git a/core/types/header_ext_test.go b/core/types/header_ext_test.go index 540bcd81a8..2693b0da2e 100644 --- a/core/types/header_ext_test.go +++ b/core/types/header_ext_test.go @@ -8,6 +8,7 @@ import ( "encoding/json" "math/big" "reflect" + "slices" "testing" "github.com/ava-labs/libevm/common" @@ -118,7 +119,7 @@ func headerWithNonZeroFields() (*Header, *HeaderExtra) { func allExportedFieldsSet[T interface { Header | HeaderExtra | Block | Body | BlockBodyExtra -}](t *testing.T, x *T) { +}](t *testing.T, x *T, ignoredFields ...string) { // We don't test for nil pointers because we're only confirming that // test-input data is well-formed. A panic due to a dereference will be // reported anyway. @@ -126,7 +127,7 @@ func allExportedFieldsSet[T interface { v := reflect.ValueOf(*x) for i := range v.Type().NumField() { field := v.Type().Field(i) - if !field.IsExported() { + if !field.IsExported() || slices.Contains(ignoredFields, field.Name) { continue } @@ -152,7 +153,7 @@ func allExportedFieldsSet[T interface { assertNonZero(t, f) case *[]uint8: assertNonZero(t, f) - case []uint8: + case []uint8, []*Transaction, []*Header, []*ethtypes.Withdrawal: assert.NotEmpty(t, f) default: t.Errorf("Field %q has unsupported type %T", field.Name, f) From c973e568d88367ecf1a6af127cd5ce12f5e63c5a Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Thu, 27 Feb 2025 13:52:44 +0100 Subject: [PATCH 23/39] core/types: improve comment on body withdrawals field drop Co-authored-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Signed-off-by: Quentin McGaw --- core/types/block_ext_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index e1f368607c..7d40e994b5 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -129,7 +129,7 @@ func TestBodyRLP(t *testing.T) { wantBody, wantExtra := bodyWithNonZeroFields() - wantBody.Withdrawals = nil // this should be ignored + wantBody.Withdrawals = nil // this should be dropped by the RLP round trip opts := cmp.Options{ txHashComparer(), headerHashComparer(), From 50e3adba6a17ce797b3ceed8ad04c949774ab6e2 Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Thu, 27 Feb 2025 13:53:30 +0100 Subject: [PATCH 24/39] core/types: blockWithNonZeroFields uses SetBlockExtra Co-authored-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Signed-off-by: Quentin McGaw --- core/types/block_ext_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 7d40e994b5..296f1ecb93 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -183,7 +183,7 @@ func blockWithNonZeroFields() (*Block, *BlockBodyExtra) { Version: 12, ExtData: &[]byte{13}, } - extras.Block.Set(block, extra) + SetBlockExtra(block, extra) return block, extra } From 9701624ed64aab0fd070cc0a25360622bccc9399 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 27 Feb 2025 10:20:02 +0100 Subject: [PATCH 25/39] ethclient: make return statement closer to geth --- ethclient/ethclient.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 27079c8ee3..5ffb722096 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -265,8 +265,12 @@ func (ec *client) getBlock(ctx context.Context, method string, args ...interface } txs[i] = tx.tx } - block := types.NewBlockWithHeader(head) - block = block.WithBody(types.Body{Transactions: txs, Uncles: uncles}) + + block := types.NewBlockWithHeader(head).WithBody( + types.Body{ + Transactions: txs, + Uncles: uncles, + }) extra := &types.BlockBodyExtra{ Version: body.Version, ExtData: (*[]byte)(body.BlockExtraData), From dd6e438bc569ae486d976ec1fb0263c7eb556e12 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 27 Feb 2025 10:23:28 +0100 Subject: [PATCH 26/39] core/types: name ignoreFields var in TestBlockWithNonZeroFields --- core/types/block_ext_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 296f1ecb93..6dbdc1e0ae 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -191,7 +191,10 @@ func TestBlockWithNonZeroFields(t *testing.T) { t.Parallel() block, extra := blockWithNonZeroFields() - t.Run("Block", func(t *testing.T) { allExportedFieldsSet(t, block, "ReceivedAt", "ReceivedFrom") }) + t.Run("Block", func(t *testing.T) { + ignoreFields := []string{"ReceivedAt", "ReceivedFrom"} + allExportedFieldsSet(t, block, ignoreFields...) + }) t.Run("BlockExtra", func(t *testing.T) { allExportedFieldsSet(t, extra) }) } From 1c3d8c20a93cd22674f06cda9c6575cddf501b87 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 27 Feb 2025 15:35:47 +0100 Subject: [PATCH 27/39] to also check unexported fields (for Block) --- core/types/block_ext_test.go | 14 ++++++++------ core/types/header_ext_test.go | 26 ++++++++++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 6dbdc1e0ae..a8dbd1cbc4 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -179,6 +179,8 @@ func blockWithNonZeroFields() (*Block, *BlockBodyExtra) { receipts := []*Receipt{{PostState: []byte{11}}} block := NewBlock(header, txs, uncles, receipts, stubHasher{}) + withdrawals := []*ethtypes.Withdrawal{{Index: 12}} + block = block.WithWithdrawals(withdrawals) extra := &BlockBodyExtra{ Version: 12, ExtData: &[]byte{13}, @@ -192,10 +194,10 @@ func TestBlockWithNonZeroFields(t *testing.T) { block, extra := blockWithNonZeroFields() t.Run("Block", func(t *testing.T) { - ignoreFields := []string{"ReceivedAt", "ReceivedFrom"} - allExportedFieldsSet(t, block, ignoreFields...) + ignoreFields := []string{"hash", "size", "ReceivedAt", "ReceivedFrom"} + allFieldsSet(t, block, ignoreFields...) }) - t.Run("BlockExtra", func(t *testing.T) { allExportedFieldsSet(t, extra) }) + t.Run("BlockExtra", func(t *testing.T) { allFieldsSet(t, extra) }) } func TestBlockRLP(t *testing.T) { @@ -246,8 +248,8 @@ func TestBodyWithNonZeroFields(t *testing.T) { t.Parallel() body, extra := bodyWithNonZeroFields() - t.Run("Body", func(t *testing.T) { allExportedFieldsSet(t, body) }) - t.Run("BodyExtra", func(t *testing.T) { allExportedFieldsSet(t, extra) }) + t.Run("Body", func(t *testing.T) { allFieldsSet(t, body) }) + t.Run("BodyExtra", func(t *testing.T) { allFieldsSet(t, extra) }) } func txHashComparer() cmp.Option { @@ -369,7 +371,7 @@ func TestBlockBody(t *testing.T) { Version: 1, ExtData: &[]byte{2}, } - allExportedFieldsSet(t, blockExtras) // make sure each field is checked + allFieldsSet(t, blockExtras) // make sure each field is checked block := NewBlock(&Header{}, nil, nil, nil, stubHasher{}) extras.Block.Set(block, blockExtras) diff --git a/core/types/header_ext_test.go b/core/types/header_ext_test.go index 2693b0da2e..72de219594 100644 --- a/core/types/header_ext_test.go +++ b/core/types/header_ext_test.go @@ -10,6 +10,7 @@ import ( "reflect" "slices" "testing" + "unsafe" "github.com/ava-labs/libevm/common" ethtypes "github.com/ava-labs/libevm/core/types" @@ -74,8 +75,8 @@ func TestHeaderWithNonZeroFields(t *testing.T) { t.Parallel() header, extra := headerWithNonZeroFields() - t.Run("Header", func(t *testing.T) { allExportedFieldsSet(t, header) }) - t.Run("HeaderExtra", func(t *testing.T) { allExportedFieldsSet(t, extra) }) + t.Run("Header", func(t *testing.T) { allFieldsSet(t, header, "extra") }) + t.Run("HeaderExtra", func(t *testing.T) { allFieldsSet(t, extra) }) } // headerWithNonZeroFields returns a [Header] and a [HeaderExtra], @@ -117,22 +118,35 @@ func headerWithNonZeroFields() (*Header, *HeaderExtra) { return header, extra } -func allExportedFieldsSet[T interface { +func allFieldsSet[T interface { Header | HeaderExtra | Block | Body | BlockBodyExtra }](t *testing.T, x *T, ignoredFields ...string) { + t.Helper() + // We don't test for nil pointers because we're only confirming that // test-input data is well-formed. A panic due to a dereference will be // reported anyway. - v := reflect.ValueOf(*x) + v := reflect.ValueOf(x).Elem() for i := range v.Type().NumField() { field := v.Type().Field(i) - if !field.IsExported() || slices.Contains(ignoredFields, field.Name) { + if slices.Contains(ignoredFields, field.Name) { continue } t.Run(field.Name, func(t *testing.T) { - switch f := v.Field(i).Interface().(type) { + fieldValue := v.Field(i) + if !field.IsExported() { + // Note: we need to check unexported fields especially for [Block]. + if fieldValue.Kind() == reflect.Ptr { + require.Falsef(t, fieldValue.IsNil(), "field %q is nil", field.Name) + } + fieldValue = reflect.NewAt(fieldValue.Type(), unsafe.Pointer(fieldValue.UnsafeAddr())).Elem() //nolint:gosec + assert.NotZero(t, fieldValue.Interface()) + return + } + + switch f := fieldValue.Interface().(type) { case common.Hash: assertNonZero(t, f) case common.Address: From 909d843f711d88ace0ea706bc091a592e65478d7 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 27 Feb 2025 15:38:13 +0100 Subject: [PATCH 28/39] core/types: `bodyWithNonZeroFields` uses `blockWithNonZeroFields` --- core/types/block_ext_test.go | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index a8dbd1cbc4..386a335a38 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -217,31 +217,15 @@ func TestBlockRLP(t *testing.T) { // bodyWithNonZeroFields returns a [Body] and a [BlockBodyExtra], // each with all fields set to non-zero values. -// The [BlockBodyExtra] extra payload is set in the [Body] via `extras.Body.Set`. +// The [BlockBodyExtra] extra payload is set in the [Body] via `extras.Block.Set` +// and the extra copying done in [ethtypes.Block.Body]. // // NOTE: They can be used to demonstrate that RLP round-trip encoding // can recover all fields, but not that the encoded format is correct. This is // very important as the RLP encoding of a [Body] defines its hash. func bodyWithNonZeroFields() (*Body, *BlockBodyExtra) { - tx := NewTransaction(1, common.Address{2}, big.NewInt(3), 4, big.NewInt(5), []byte{6}) - uncle := &Header{ - Difficulty: big.NewInt(7), - Number: big.NewInt(8), - ParentHash: common.Hash{9}, - } - uncleExtra := &HeaderExtra{ExtDataHash: common.Hash{10}} - SetHeaderExtra(uncle, uncleExtra) - body := &Body{ - Transactions: []*Transaction{tx}, - Uncles: []*Header{uncle}, - Withdrawals: []*ethtypes.Withdrawal{{Index: 11}}, - } - extra := &BlockBodyExtra{ - Version: 12, - ExtData: &[]byte{13}, - } - extras.Body.Set(body, extra) - return body, extra + block, extra := blockWithNonZeroFields() + return block.Body(), extra } func TestBodyWithNonZeroFields(t *testing.T) { From 64c22bf578c08620a8af68a829ded7fa3c173acf Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 27 Feb 2025 15:43:54 +0100 Subject: [PATCH 29/39] Merge TestBodyRLP and TestBodyExtraRLP together --- core/types/block_ext_test.go | 58 +++++------------------------------- 1 file changed, 8 insertions(+), 50 deletions(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 386a335a38..868beffa89 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -115,39 +115,6 @@ func assertDifferentPointers[T any](t *testing.T, a *T, b any) { // the memory address would be different as well. } -func TestBodyRLP(t *testing.T) { - t.Parallel() - - input, _ := bodyWithNonZeroFields() // the [Body] carries the [BlockBodyExtra] so we can ignore it - encoded, err := rlp.EncodeToBytes(input) - require.NoError(t, err, "encode") - - gotBody := new(Body) - err = rlp.DecodeBytes(encoded, gotBody) - require.NoError(t, err, "decode") - gotExtra := extras.Body.Get(gotBody) - - wantBody, wantExtra := bodyWithNonZeroFields() - - wantBody.Withdrawals = nil // this should be dropped by the RLP round trip - opts := cmp.Options{ - txHashComparer(), - headerHashComparer(), - cmpopts.IgnoreUnexported(Body{}), - } - if diff := cmp.Diff(wantBody, gotBody, opts); diff != "" { - t.Errorf("%T diff after RLP round-trip (-want +got):\n%s", wantBody, diff) - } - assert.Equal(t, wantExtra, gotExtra, "extra") - - // Golden data from original coreth implementation, before integration of - // libevm. WARNING: changing these values can break backwards compatibility - // with extreme consequences. - const wantHex = "f90235dedd0105049402000000000000000000000000000000000000000306808080f90211f9020ea00900000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070880808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00a000000000000000000000000000000000000000000000000000000000000000c0d" - - assert.Equal(t, wantHex, hex.EncodeToString(encoded), "golden data") -} - // blockWithNonZeroFields returns a [Block] and a [BlockBodyExtra], // each with all fields set to non-zero values. // The [BlockBodyExtra] extra payload is set in the [Block] via `extras.Block.Set`. @@ -251,22 +218,7 @@ func headerHashComparer() cmp.Option { func TestBodyExtraRLP(t *testing.T) { t.Parallel() - tx := NewTransaction(0, common.Address{1}, big.NewInt(2), 3, big.NewInt(4), []byte{5}) - - uncle := &Header{ParentHash: common.Hash{6}} - uncleExtra := &HeaderExtra{ExtDataHash: common.Hash{7}} - SetHeaderExtra(uncle, uncleExtra) - - body := &Body{ - Transactions: []*Transaction{tx}, - Uncles: []*Header{uncle}, - Withdrawals: []*ethtypes.Withdrawal{{Index: 7}}, // ignored - } - extra := &BlockBodyExtra{ - Version: 1, - ExtData: &[]byte{2}, - } - extras.Body.Set(body, extra) + body, wantExtra := bodyWithNonZeroFields() encoded, err := rlp.EncodeToBytes(body) require.NoError(t, err) @@ -288,10 +240,16 @@ func TestBodyExtraRLP(t *testing.T) { } gotExtra := extras.Body.Get(gotBody) - wantExtra := extra if diff := cmp.Diff(wantExtra, gotExtra); diff != "" { t.Errorf("%T diff after RLP round-trip of %T (-want +got):\n%s", wantExtra, wantBody, diff) } + + // Golden data from original coreth implementation, before integration of + // libevm. WARNING: changing these values can break backwards compatibility + // with extreme consequences. + const wantHex = "f90235dedd0105049402000000000000000000000000000000000000000306808080f90211f9020ea00900000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070880808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00a000000000000000000000000000000000000000000000000000000000000000c0d" + + assert.Equal(t, wantHex, hex.EncodeToString(encoded), "golden data") } func TestBlockExtraRLP(t *testing.T) { From 0c5eca89d76a98b7a05547b75da284e730e70493 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 27 Feb 2025 15:46:05 +0100 Subject: [PATCH 30/39] core/types: merge TestBlockRLP into TestBlockExtraRLP --- core/types/block_ext_test.go | 48 ++++++------------------------------ 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 868beffa89..74c7918fce 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -167,21 +167,6 @@ func TestBlockWithNonZeroFields(t *testing.T) { t.Run("BlockExtra", func(t *testing.T) { allFieldsSet(t, extra) }) } -func TestBlockRLP(t *testing.T) { - t.Parallel() - - input, _ := blockWithNonZeroFields() // the [Block] carries the [BlockBodyExtra] so we can ignore it - encoded, err := rlp.EncodeToBytes(input) - require.NoError(t, err, "encode") - - // Golden data from original coreth implementation, before integration of - // libevm. WARNING: changing these values can break backwards compatibility - // with extreme consequences. - const wantHex = "f90446f9020ea00100000000000000000000000000000000000000000000000000000000000000a008539331084089cedbaf7771d0f5f69847f246e0676e4d96091a49c53c89360b940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808080808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00200000000000000000000000000000000000000000000000000000000000000dedd0105049402000000000000000000000000000000000000000306808080f90211f9020ea00900000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070880808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00a000000000000000000000000000000000000000000000000000000000000000c0d" - - assert.Equal(t, wantHex, hex.EncodeToString(encoded), "golden data") -} - // bodyWithNonZeroFields returns a [Body] and a [BlockBodyExtra], // each with all fields set to non-zero values. // The [BlockBodyExtra] extra payload is set in the [Body] via `extras.Block.Set` @@ -255,30 +240,7 @@ func TestBodyExtraRLP(t *testing.T) { func TestBlockExtraRLP(t *testing.T) { t.Parallel() - header := &Header{ParentHash: common.Hash{1}} - headerExtras := &HeaderExtra{ExtDataHash: common.Hash{2}} - SetHeaderExtra(header, headerExtras) - - tx := NewTransaction(3, common.Address{4}, big.NewInt(2), 3, big.NewInt(4), []byte{5}) - txs := []*Transaction{tx} - - uncle := &Header{ParentHash: common.Hash{6}} - uncleExtra := &HeaderExtra{ExtDataHash: common.Hash{7}} - SetHeaderExtra(uncle, uncleExtra) - uncles := []*Header{uncle} - - receipts := []*Receipt{{PostState: []byte{8}}} - - block := NewBlock(header, txs, uncles, receipts, stubHasher{}) - - withdrawals := []*ethtypes.Withdrawal{{Index: 9}} // ignored - block = block.WithWithdrawals(withdrawals) - - extra := &BlockBodyExtra{ - Version: 10, - ExtData: &[]byte{11}, - } - extras.Block.Set(block, extra) + block, wantExtra := blockWithNonZeroFields() encoded, err := rlp.EncodeToBytes(block) require.NoError(t, err) @@ -299,10 +261,16 @@ func TestBlockExtraRLP(t *testing.T) { } gotExtra := extras.Block.Get(gotBlock) - wantExtra := extra if diff := cmp.Diff(wantExtra, gotExtra); diff != "" { t.Errorf("%T diff after RLP round-trip of %T (-want +got):\n%s", wantExtra, wantBlock, diff) } + + // Golden data from original coreth implementation, before integration of + // libevm. WARNING: changing these values can break backwards compatibility + // with extreme consequences. + const wantHex = "f90446f9020ea00100000000000000000000000000000000000000000000000000000000000000a008539331084089cedbaf7771d0f5f69847f246e0676e4d96091a49c53c89360b940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808080808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00200000000000000000000000000000000000000000000000000000000000000dedd0105049402000000000000000000000000000000000000000306808080f90211f9020ea00900000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070880808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00a000000000000000000000000000000000000000000000000000000000000000c0d" + + assert.Equal(t, wantHex, hex.EncodeToString(encoded), "golden data") } // TestBlockBody tests the [BlockBodyExtra.Copy] method is implemented correctly. From 114fc2abda1adfb7fdc4aff169766ff23263ad26 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 27 Feb 2025 15:47:39 +0100 Subject: [PATCH 31/39] core/types: TestBlockBody uses constant version and shared extData variable --- core/types/block_ext_test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 74c7918fce..7911c162df 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -277,17 +277,20 @@ func TestBlockExtraRLP(t *testing.T) { func TestBlockBody(t *testing.T) { t.Parallel() + const version = 1 + extData := &[]byte{2} + blockExtras := &BlockBodyExtra{ - Version: 1, - ExtData: &[]byte{2}, + Version: version, + ExtData: extData, } allFieldsSet(t, blockExtras) // make sure each field is checked block := NewBlock(&Header{}, nil, nil, nil, stubHasher{}) extras.Block.Set(block, blockExtras) wantExtra := &BlockBodyExtra{ - Version: 1, - ExtData: &[]byte{2}, + Version: version, + ExtData: extData, } gotExtra := extras.Body.Get(block.Body()) // [types.Block.Body] invokes [BlockBodyExtra.Copy] assert.Equal(t, wantExtra, gotExtra) From aa0c116301c1cdd48461b26c55ee525192923a89 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 27 Feb 2025 15:53:18 +0100 Subject: [PATCH 32/39] core/types: add comment on nil withdrawals in TestBlockExtraRLP --- core/types/block_ext_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 7911c162df..84141d5e99 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -249,7 +249,7 @@ func TestBlockExtraRLP(t *testing.T) { err = rlp.DecodeBytes(encoded, gotBlock) require.NoError(t, err) - wantBlock := block.WithWithdrawals(nil) + wantBlock := block.WithWithdrawals(nil) // withdrawals are not encoded opts := cmp.Options{ txHashComparer(), From 619a51a7519b21db09a083b5ac338af7dbbf9993 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 28 Feb 2025 14:28:42 +0100 Subject: [PATCH 33/39] core/types: allFieldsSet handles unexported fields in common switch case --- core/types/block_ext_test.go | 7 +++++-- core/types/header_ext_test.go | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 84141d5e99..bc4cd21a27 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -161,7 +161,7 @@ func TestBlockWithNonZeroFields(t *testing.T) { block, extra := blockWithNonZeroFields() t.Run("Block", func(t *testing.T) { - ignoreFields := []string{"hash", "size", "ReceivedAt", "ReceivedFrom"} + ignoreFields := []string{"extra", "hash", "size", "ReceivedAt", "ReceivedFrom"} allFieldsSet(t, block, ignoreFields...) }) t.Run("BlockExtra", func(t *testing.T) { allFieldsSet(t, extra) }) @@ -184,7 +184,10 @@ func TestBodyWithNonZeroFields(t *testing.T) { t.Parallel() body, extra := bodyWithNonZeroFields() - t.Run("Body", func(t *testing.T) { allFieldsSet(t, body) }) + t.Run("Body", func(t *testing.T) { + ignoredFields := []string{"extra"} + allFieldsSet(t, body, ignoredFields...) + }) t.Run("BodyExtra", func(t *testing.T) { allFieldsSet(t, extra) }) } diff --git a/core/types/header_ext_test.go b/core/types/header_ext_test.go index 72de219594..2469cef8b4 100644 --- a/core/types/header_ext_test.go +++ b/core/types/header_ext_test.go @@ -142,8 +142,6 @@ func allFieldsSet[T interface { require.Falsef(t, fieldValue.IsNil(), "field %q is nil", field.Name) } fieldValue = reflect.NewAt(fieldValue.Type(), unsafe.Pointer(fieldValue.UnsafeAddr())).Elem() //nolint:gosec - assert.NotZero(t, fieldValue.Interface()) - return } switch f := fieldValue.Interface().(type) { @@ -167,7 +165,9 @@ func allFieldsSet[T interface { assertNonZero(t, f) case *[]uint8: assertNonZero(t, f) - case []uint8, []*Transaction, []*Header, []*ethtypes.Withdrawal: + case *Header: + assertNonZero(t, f) + case []uint8, []*Header, Transactions, []*Transaction, ethtypes.Withdrawals, []*ethtypes.Withdrawal: assert.NotEmpty(t, f) default: t.Errorf("Field %q has unsupported type %T", field.Name, f) @@ -178,7 +178,7 @@ func allFieldsSet[T interface { func assertNonZero[T interface { common.Hash | common.Address | BlockNonce | uint32 | uint64 | Bloom | - *big.Int | *common.Hash | *uint64 | *[]uint8 + *big.Int | *common.Hash | *uint64 | *[]uint8 | *Header }](t *testing.T, v T) { t.Helper() var zero T From d3354480b01f3b7dee5c253b27fa3f3853e90978 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 28 Feb 2025 14:29:51 +0100 Subject: [PATCH 34/39] core/types: bump values in blockWithNonZeroFields --- core/types/block_ext_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index bc4cd21a27..4b77bb4d95 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -149,8 +149,8 @@ func blockWithNonZeroFields() (*Block, *BlockBodyExtra) { withdrawals := []*ethtypes.Withdrawal{{Index: 12}} block = block.WithWithdrawals(withdrawals) extra := &BlockBodyExtra{ - Version: 12, - ExtData: &[]byte{13}, + Version: 13, + ExtData: &[]byte{14}, } SetBlockExtra(block, extra) return block, extra @@ -235,7 +235,7 @@ func TestBodyExtraRLP(t *testing.T) { // Golden data from original coreth implementation, before integration of // libevm. WARNING: changing these values can break backwards compatibility // with extreme consequences. - const wantHex = "f90235dedd0105049402000000000000000000000000000000000000000306808080f90211f9020ea00900000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070880808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00a000000000000000000000000000000000000000000000000000000000000000c0d" + const wantHex = "f90235dedd0105049402000000000000000000000000000000000000000306808080f90211f9020ea00900000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070880808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00a000000000000000000000000000000000000000000000000000000000000000d0e" assert.Equal(t, wantHex, hex.EncodeToString(encoded), "golden data") } @@ -271,7 +271,7 @@ func TestBlockExtraRLP(t *testing.T) { // Golden data from original coreth implementation, before integration of // libevm. WARNING: changing these values can break backwards compatibility // with extreme consequences. - const wantHex = "f90446f9020ea00100000000000000000000000000000000000000000000000000000000000000a008539331084089cedbaf7771d0f5f69847f246e0676e4d96091a49c53c89360b940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808080808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00200000000000000000000000000000000000000000000000000000000000000dedd0105049402000000000000000000000000000000000000000306808080f90211f9020ea00900000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070880808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00a000000000000000000000000000000000000000000000000000000000000000c0d" + const wantHex = "f90446f9020ea00100000000000000000000000000000000000000000000000000000000000000a008539331084089cedbaf7771d0f5f69847f246e0676e4d96091a49c53c89360b940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808080808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00200000000000000000000000000000000000000000000000000000000000000dedd0105049402000000000000000000000000000000000000000306808080f90211f9020ea00900000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070880808080a00000000000000000000000000000000000000000000000000000000000000000880000000000000000a00a000000000000000000000000000000000000000000000000000000000000000d0e" assert.Equal(t, wantHex, hex.EncodeToString(encoded), "golden data") } From 39c516aa293160dec5386ab3892cbb825ce50625 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 28 Feb 2025 14:43:16 +0100 Subject: [PATCH 35/39] core/types: TestBodyExtraRLP re-generates copy of wanted body and extra --- core/types/block_ext_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 4b77bb4d95..1425130d77 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -206,7 +206,7 @@ func headerHashComparer() cmp.Option { func TestBodyExtraRLP(t *testing.T) { t.Parallel() - body, wantExtra := bodyWithNonZeroFields() + body, _ := bodyWithNonZeroFields() // the body carries the [BlockBodyExtra] so we can ignore it encoded, err := rlp.EncodeToBytes(body) require.NoError(t, err) @@ -215,7 +215,7 @@ func TestBodyExtraRLP(t *testing.T) { err = rlp.DecodeBytes(encoded, gotBody) require.NoError(t, err) - wantBody := body + wantBody, wantExtra := bodyWithNonZeroFields() wantBody.Withdrawals = nil opts := cmp.Options{ From cc11dba84184119b3301de83701ce81dbd14fcb1 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 28 Feb 2025 14:43:28 +0100 Subject: [PATCH 36/39] core/types: TestBlockExtraRLP re-generates copy of wanted block and extra --- core/types/block_ext_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/types/block_ext_test.go b/core/types/block_ext_test.go index 1425130d77..49c68d8af8 100644 --- a/core/types/block_ext_test.go +++ b/core/types/block_ext_test.go @@ -243,7 +243,7 @@ func TestBodyExtraRLP(t *testing.T) { func TestBlockExtraRLP(t *testing.T) { t.Parallel() - block, wantExtra := blockWithNonZeroFields() + block, _ := blockWithNonZeroFields() // the block carries the [BlockBodyExtra] so we can ignore it encoded, err := rlp.EncodeToBytes(block) require.NoError(t, err) @@ -252,7 +252,8 @@ func TestBlockExtraRLP(t *testing.T) { err = rlp.DecodeBytes(encoded, gotBlock) require.NoError(t, err) - wantBlock := block.WithWithdrawals(nil) // withdrawals are not encoded + wantBlock, wantExtra := blockWithNonZeroFields() + wantBlock = wantBlock.WithWithdrawals(nil) // withdrawals are not encoded opts := cmp.Options{ txHashComparer(), From 4b6ff48560bbe488e94def442ce1bd20008455e2 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 28 Feb 2025 14:46:24 +0100 Subject: [PATCH 37/39] core/types: allFieldsSet remove ineffective t.Helper() --- core/types/header_ext_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/types/header_ext_test.go b/core/types/header_ext_test.go index 2469cef8b4..299746f88a 100644 --- a/core/types/header_ext_test.go +++ b/core/types/header_ext_test.go @@ -121,8 +121,6 @@ func headerWithNonZeroFields() (*Header, *HeaderExtra) { func allFieldsSet[T interface { Header | HeaderExtra | Block | Body | BlockBodyExtra }](t *testing.T, x *T, ignoredFields ...string) { - t.Helper() - // We don't test for nil pointers because we're only confirming that // test-input data is well-formed. A panic due to a dereference will be // reported anyway. From e99a77cc0dd45c2a43345b01e2a91b4ab27bc115 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 28 Feb 2025 14:50:56 +0100 Subject: [PATCH 38/39] core/types: BlockBodyExtra Copy leaves ExtData to nil if it's nil previously --- core/types/block_ext.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/types/block_ext.go b/core/types/block_ext.go index cc35146e7e..bf2b1ecd90 100644 --- a/core/types/block_ext.go +++ b/core/types/block_ext.go @@ -32,9 +32,7 @@ type BlockBodyExtra struct { // - [ethtypes.Block.WithWithdrawals] func (b *BlockBodyExtra) Copy() *BlockBodyExtra { cpy := *b - if b.ExtData == nil { - cpy.ExtData = &[]byte{} - } else { + if b.ExtData != nil { data := slices.Clone(*b.ExtData) cpy.ExtData = &data } From 37797f0a5f79f028def67a1be952ee32711838a4 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 28 Feb 2025 15:15:28 +0100 Subject: [PATCH 39/39] deps: bump go-cmp to v0.7.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d6b71d4458..3da197357e 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set/v2 v2.1.0 - github.com/google/go-cmp v0.6.0 + github.com/google/go-cmp v0.7.0 github.com/gorilla/rpc v1.2.0 github.com/gorilla/websocket v1.5.0 github.com/hashicorp/go-bexpr v0.1.10 diff --git a/go.sum b/go.sum index 40ed39bb9d..d8f3ed41d8 100644 --- a/go.sum +++ b/go.sum @@ -268,8 +268,8 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=