a bunch of changes. mostly bal persistence and removing bal from block body object. wip...

This commit is contained in:
Jared Wasinger 2026-01-28 17:57:07 -05:00
parent 7036b1cb39
commit 3435849244
18 changed files with 158 additions and 84 deletions

View file

@ -307,11 +307,7 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
var blockAccessListHash *common.Hash var blockAccessListHash *common.Hash
body := types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals} body := types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}
if data.BlockAccessList != nil {
body.AccessList = data.BlockAccessList
balHash := data.BlockAccessList.Hash()
blockAccessListHash = &balHash
}
header := &types.Header{ header := &types.Header{
ParentHash: data.ParentHash, ParentHash: data.ParentHash,
UncleHash: types.EmptyUncleHash, UncleHash: types.EmptyUncleHash,
@ -336,7 +332,16 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
BlockAccessListHash: blockAccessListHash, BlockAccessListHash: blockAccessListHash,
SlotNumber: data.SlotNumber, SlotNumber: data.SlotNumber,
} }
if data.BlockAccessList != nil {
balHash := data.BlockAccessList.Hash()
header.BlockAccessListHash = &balHash
block := types.NewBlockWithHeader(header).WithBody(body).WithAccessList(data.BlockAccessList)
return block, nil
}
return types.NewBlockWithHeader(header).WithBody(body), nil return types.NewBlockWithHeader(header).WithBody(body), nil
} }
// BlockToExecutableData constructs the ExecutableData structure by filling the // BlockToExecutableData constructs the ExecutableData structure by filling the
@ -360,6 +365,10 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
Withdrawals: block.Withdrawals(), Withdrawals: block.Withdrawals(),
BlobGasUsed: block.BlobGasUsed(), BlobGasUsed: block.BlobGasUsed(),
ExcessBlobGas: block.ExcessBlobGas(), ExcessBlobGas: block.ExcessBlobGas(),
<<<<<<< HEAD
=======
BlockAccessList: block.AccessList(),
>>>>>>> 959ade7689 (a bunch of changes. mostly bal persistence and removing bal from block body object. wip...)
SlotNumber: block.SlotNumber(), SlotNumber: block.SlotNumber(),
BlockAccessList: block.Body().AccessList, BlockAccessList: block.Body().AccessList,
} }

View file

@ -5,10 +5,10 @@
# https://github.com/ethereum/execution-spec-tests/releases/download/v5.1.0 # https://github.com/ethereum/execution-spec-tests/releases/download/v5.1.0
a3192784375acec7eaec492799d5c5d0c47a2909a3cc40178898e4ecd20cc416 fixtures_develop.tar.gz a3192784375acec7eaec492799d5c5d0c47a2909a3cc40178898e4ecd20cc416 fixtures_develop.tar.gz
# version:spec-tests-bal v3.0.1 # version:spec-tests-bal v5.0.0
# https://github.com/ethereum/execution-spec-tests/releases # https://github.com/ethereum/execution-spec-tests/releases
# https://github.com/ethereum/execution-spec-tests/releases/download/bal%40v3.0.1 # https://github.com/ethereum/execution-spec-tests/releases/download/bal%40v5.0.0
57d0f109f0557ec33d6ecd6cbd77b55415a658aefe583c4035157e1021ae512a fixtures_bal.tar.gz 17aecf8334da7f5d1025635c763a4be0dfbcdb0e659cc70a36e6a81bd3af06f2 fixtures_bal.tar.gz
# version:golang 1.25.1 # version:golang 1.25.1
# https://go.dev/dl/ # https://go.dev/dl/

View file

@ -19,6 +19,7 @@ package beacon
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/core/types/bal"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -357,9 +358,9 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
// FinalizeAndAssemble implements consensus.Engine, setting the final state and // FinalizeAndAssemble implements consensus.Engine, setting the final state and
// assembling the block. // assembling the block.
func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalization func()) (*types.Block, error) { func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalizeAccessList func() *bal.BlockAccessList) (*types.Block, error) {
if !beacon.IsPoSHeader(header) { if !beacon.IsPoSHeader(header) {
return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts, onFinalization) return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts, nil)
} }
shanghai := chain.Config().IsShanghai(header.Number, header.Time) shanghai := chain.Config().IsShanghai(header.Number, header.Time)
if shanghai { if shanghai {
@ -379,12 +380,15 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
// Assign the final state root to header. // Assign the final state root to header.
header.Root = state.IntermediateRoot(true) header.Root = state.IntermediateRoot(true)
if onFinalization != nil {
onFinalization()
}
// Assemble the final block. // Assemble the final block.
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)), nil if onFinalizeAccessList != nil {
al := onFinalizeAccessList()
alHash := al.Hash()
header.BlockAccessListHash = &alHash
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)).WithAccessList(al), nil
} else {
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)), nil
}
} }
// Seal generates a new sealing request for the given input block and pushes // Seal generates a new sealing request for the given input block and pushes

View file

@ -21,6 +21,7 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/core/types/bal"
"io" "io"
"math/big" "math/big"
"math/rand" "math/rand"
@ -581,7 +582,7 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// nor block rewards given, and returns the final block. // nor block rewards given, and returns the final block.
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalize func()) (*types.Block, error) { func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalizeAccessList func() *bal.BlockAccessList) (*types.Block, error) {
if len(body.Withdrawals) > 0 { if len(body.Withdrawals) > 0 {
return nil, errors.New("clique does not support withdrawals") return nil, errors.New("clique does not support withdrawals")
} }
@ -591,10 +592,9 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *
// Assign the final state root to header. // Assign the final state root to header.
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
if onFinalize != nil { if onFinalizeAccessList != nil {
onFinalize() panic("access list embedding not enabled for clique consensus")
} }
// Assemble and return the final block for sealing. // Assemble and return the final block for sealing.
return types.NewBlock(header, &types.Body{Transactions: body.Transactions}, receipts, trie.NewStackTrie(nil)), nil return types.NewBlock(header, &types.Body{Transactions: body.Transactions}, receipts, trie.NewStackTrie(nil)), nil
} }

View file

@ -18,6 +18,7 @@
package consensus package consensus
import ( import (
"github.com/ethereum/go-ethereum/core/types/bal"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -92,7 +93,7 @@ type Engine interface {
// //
// Note: The block header and state database might be updated to reflect any // Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards). // consensus rules that happen at finalization (e.g. block rewards).
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalization func()) (*types.Block, error) FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalizeAccessList func() *bal.BlockAccessList) (*types.Block, error)
// Seal generates a new sealing request for the given input block and pushes // Seal generates a new sealing request for the given input block and pushes
// the result into the given channel. // the result into the given channel.

View file

@ -19,6 +19,7 @@ package ethash
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/core/types/bal"
"math/big" "math/big"
"time" "time"
@ -513,7 +514,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and // FinalizeAndAssemble implements consensus.Engine, accumulating the block and
// uncle rewards, setting the final state and assembling the block. // uncle rewards, setting the final state and assembling the block.
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalize func()) (*types.Block, error) { func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalizeAccessList func() *bal.BlockAccessList) (*types.Block, error) {
if len(body.Withdrawals) > 0 { if len(body.Withdrawals) > 0 {
return nil, errors.New("ethash does not support withdrawals") return nil, errors.New("ethash does not support withdrawals")
} }
@ -523,8 +524,8 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
// Assign the final state root to header. // Assign the final state root to header.
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
if onFinalize != nil { if onFinalizeAccessList != nil {
onFinalize() panic("access list embedding not supported for ethash consenus")
} }
// Header seems complete, assemble into a block and return // Header seems complete, assemble into a block and return
return types.NewBlock(header, &types.Body{Transactions: body.Transactions, Uncles: body.Uncles}, receipts, trie.NewStackTrie(nil)), nil return types.NewBlock(header, &types.Body{Transactions: body.Transactions, Uncles: body.Uncles}, receipts, trie.NewStackTrie(nil)), nil

View file

@ -116,10 +116,10 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
// TODO: verify that this check isn't also done elsewhere // TODO: verify that this check isn't also done elsewhere
return fmt.Errorf("block access list hash not set in header") return fmt.Errorf("block access list hash not set in header")
} }
if block.Body().AccessList != nil { if block.AccessList() != nil {
if *block.Header().BlockAccessListHash != block.Body().AccessList.Hash() { if *block.Header().BlockAccessListHash != block.AccessList().Hash() {
return fmt.Errorf("access list hash mismatch. local: %x. remote: %x\n", block.Body().AccessList.Hash(), *block.Header().BlockAccessListHash) return fmt.Errorf("access list hash mismatch. local: %x. remote: %x\n", block.AccessList().Hash(), *block.Header().BlockAccessListHash)
} else if err := block.Body().AccessList.Validate(len(block.Transactions())); err != nil { } else if err := block.AccessList().Validate(len(block.Transactions())); err != nil {
return fmt.Errorf("invalid block access list: %v", err) return fmt.Errorf("invalid block access list: %v", err)
} }
} }
@ -133,7 +133,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
} }
} else { } else {
// if experimental.bal is not enabled, block headers cannot have access list hash and bodies cannot have access lists. // if experimental.bal is not enabled, block headers cannot have access list hash and bodies cannot have access lists.
if block.Body().AccessList != nil { if block.AccessList() != nil {
return fmt.Errorf("access list not allowed in block body if not in amsterdam or experimental.bal is set") return fmt.Errorf("access list not allowed in block body if not in amsterdam or experimental.bal is set")
} else if block.Header().BlockAccessListHash != nil { } else if block.Header().BlockAccessListHash != nil {
return fmt.Errorf("access list hash in block header not allowed when experimental.bal is set") return fmt.Errorf("access list hash in block header not allowed when experimental.bal is set")

View file

@ -2201,7 +2201,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
} }
// TODO: need to check that the block is also postcancun if it contained an access list? // TODO: need to check that the block is also postcancun if it contained an access list?
// this should be checked during decoding (?) // this should be checked during decoding (?)
blockHasAccessList := block.Body().AccessList != nil blockHasAccessList := block.AccessList() != nil
// only construct and embed BALs in the block if: // only construct and embed BALs in the block if:
// * it has been enabled for testing purposes (preAmsterdam/postCancun blocks with experimental.bal) // * it has been enabled for testing purposes (preAmsterdam/postCancun blocks with experimental.bal)
// * we are after Amsterdam and the block was provided with bal omitted // * we are after Amsterdam and the block was provided with bal omitted
@ -2276,7 +2276,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
// Disable tracing for prefetcher executions. // Disable tracing for prefetcher executions.
vmCfg := bc.cfg.VmConfig vmCfg := bc.cfg.VmConfig
vmCfg.Tracer = nil vmCfg.Tracer = nil
if block.Body().AccessList == nil { if block.AccessList() == nil {
// only use the state prefetcher for non-BAL blocks. // only use the state prefetcher for non-BAL blocks.
bc.prefetcher.Prefetch(block, throwaway, vmCfg, &interrupt) bc.prefetcher.Prefetch(block, throwaway, vmCfg, &interrupt)
} }
@ -2369,13 +2369,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
return nil, err return nil, err
} }
// very ugly... deepcopy the block body before setting the block access block = block.WithAccessList(balTracer.AccessList().ToEncodingObj())
// list on it to prevent mutating the block instance passed by the caller.
existingBody := block.Body()
block = block.WithBody(*existingBody)
existingBody = block.Body()
existingBody.AccessList = balTracer.AccessList().ToEncodingObj()
block = block.WithBody(*existingBody)
} else if enableBALFork { } else if enableBALFork {
computedAccessList := balTracer.AccessList().ToEncodingObj() computedAccessList := balTracer.AccessList().ToEncodingObj()
@ -2387,16 +2381,12 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
bc.reportBadBlock(block, res, err) bc.reportBadBlock(block, res, err)
return nil, err return nil, err
} }
if block.Body().AccessList == nil { if block.AccessList() == nil {
// very ugly... deep copy the block body before setting the block access // attach the computed access list to the block so it gets persisted
// list on it to prevent mutating the block instance passed by the caller. // when the block is written to disk
existingBody := block.Body() block = block.WithAccessList(computedAccessList)
block = block.WithBody(*existingBody) } else if block.AccessList().Hash() != computedAccessListHash {
existingBody = block.Body() err := fmt.Errorf("block access list hash mismatch (remote=%x computed=%x)", block.AccessList().Hash(), computedAccessListHash)
existingBody.AccessList = computedAccessList
block = block.WithBody(*existingBody)
} else if block.Body().AccessList.Hash() != computedAccessListHash {
err := fmt.Errorf("block access list hash mismatch (remote=%x computed=%x)", block.Body().AccessList.Hash(), computedAccessListHash)
bc.reportBadBlock(block, res, err) bc.reportBadBlock(block, res, err)
return nil, err return nil, err
} }

View file

@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/core/types/bal"
"math/big" "math/big"
"slices" "slices"
@ -421,6 +422,17 @@ func WriteBodyRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp
} }
} }
func WriteAccessListRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp rlp.RawValue) {
if err := db.Put(accessListKey(number, hash), rlp); err != nil {
log.Crit("failed to store block access list", "err", err)
}
}
func ReadAccessListRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
data, _ := db.Get(accessListKey(number, hash))
return data
}
// HasBody verifies the existence of a block body corresponding to the hash. // HasBody verifies the existence of a block body corresponding to the hash.
func HasBody(db ethdb.Reader, hash common.Hash, number uint64) bool { func HasBody(db ethdb.Reader, hash common.Hash, number uint64) bool {
if isCanon(db, number, hash) { if isCanon(db, number, hash) {
@ -455,6 +467,25 @@ func WriteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64, body *t
WriteBodyRLP(db, hash, number, data) WriteBodyRLP(db, hash, number, data)
} }
func ReadAccessList(db ethdb.Reader, hash common.Hash, number uint64) (al *bal.BlockAccessList) {
data := ReadAccessListRLP(db, hash, number)
if data != nil {
err := rlp.DecodeBytes(data, al)
if err != nil {
log.Crit("failed to RLP decode access list", "err", err)
}
}
return al
}
func WriteAccessList(db ethdb.KeyValueWriter, hash common.Hash, number uint64, al *bal.BlockAccessList) {
data, err := rlp.EncodeToBytes(al)
if err != nil {
log.Crit("failed to RLP encode block access list", "err", err)
}
WriteAccessListRLP(db, hash, number, data)
}
// DeleteBody removes all block body data associated with a hash. // DeleteBody removes all block body data associated with a hash.
func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
if err := db.Delete(blockBodyKey(number, hash)); err != nil { if err := db.Delete(blockBodyKey(number, hash)); err != nil {
@ -659,13 +690,26 @@ func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block {
if body == nil { if body == nil {
return nil return nil
} }
return types.NewBlockWithHeader(header).WithBody(*body)
block := types.NewBlockWithHeader(header).WithBody(*body)
// TODO: only read the access list if the bal hash is set in the header.
// I do it here regardless in order to support --experimental.bal which
// doesn't expect bal hash to be set in header in order to be compatible
// with importing mainnet blocks augmented with BALs
accessList := ReadAccessList(db, hash, number)
if accessList != nil {
block = block.WithAccessList(accessList)
}
return block
} }
// WriteBlock serializes a block into the database, header and body separately. // WriteBlock serializes a block into the database, header and body separately.
func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) { func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
WriteBody(db, block.Hash(), block.NumberU64(), block.Body()) WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
WriteHeader(db, block.Header()) WriteHeader(db, block.Header())
panic("TODO write access list if it exists")
} }
// WriteAncientBlocks writes entire block data into ancient store and returns the total written size. // WriteAncientBlocks writes entire block data into ancient store and returns the total written size.

View file

@ -111,6 +111,7 @@ var (
headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian) headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
accessListPrefix = []byte("z")
blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata
@ -209,6 +210,11 @@ func blockBodyKey(number uint64, hash common.Hash) []byte {
return append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...) return append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
} }
// accessListKey = accessListPrefix + num (uint64 big endian) + hash
func accessListKey(number uint64, hash common.Hash) []byte {
return append(append(accessListPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
}
// blockReceiptsKey = blockReceiptsPrefix + num (uint64 big endian) + hash // blockReceiptsKey = blockReceiptsPrefix + num (uint64 big endian) + hash
func blockReceiptsKey(number uint64, hash common.Hash) []byte { func blockReceiptsKey(number uint64, hash common.Hash) []byte {
return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...) return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)

View file

@ -126,7 +126,7 @@ type BALReader struct {
// NewBALReader constructs a new reader from an access list. db is expected to have been instantiated with a reader. // NewBALReader constructs a new reader from an access list. db is expected to have been instantiated with a reader.
func NewBALReader(block *types.Block, reader Reader) *BALReader { func NewBALReader(block *types.Block, reader Reader) *BALReader {
r := &BALReader{accesses: make(map[common.Address]*bal.AccountAccess), block: block} r := &BALReader{accesses: make(map[common.Address]*bal.AccountAccess), block: block}
for _, acctDiff := range *block.Body().AccessList { for _, acctDiff := range *block.AccessList() {
r.accesses[acctDiff.Address] = &acctDiff r.accesses[acctDiff.Address] = &acctDiff
} }
r.prestateReader.schedule(reader, r.ModifiedAccounts()) r.prestateReader.schedule(reader, r.ModifiedAccounts())

View file

@ -462,6 +462,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil { if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, tracing.GasChangeTxIntrinsicGas) t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, tracing.GasChangeTxIntrinsicGas)
} }
fmt.Printf("gas used %d\n", gas)
st.gasRemaining -= gas st.gasRemaining -= gas
if rules.IsEIP4762 { if rules.IsEIP4762 {
@ -568,8 +569,10 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
} }
} }
fmt.Printf("2 used gas is %d, %d\n", st.gasUsed(), peakGasUsed)
return &ExecutionResult{ return &ExecutionResult{
UsedGas: st.gasUsed(), UsedGas: peakGasUsed,
MaxUsedGas: peakGasUsed, MaxUsedGas: peakGasUsed,
Err: vmerr, Err: vmerr,
ReturnData: ret, ReturnData: ret,

View file

@ -93,7 +93,8 @@ func makeTestConstructionBAL() *AccessListBuilder {
// TestBALEncoding tests that a populated access list can be encoded/decoded correctly. // TestBALEncoding tests that a populated access list can be encoded/decoded correctly.
func TestBALEncoding(t *testing.T) { func TestBALEncoding(t *testing.T) {
var buf bytes.Buffer var buf bytes.Buffer
bal := makeTestConstructionBAL() balBuilder := makeTestConstructionBAL()
bal := balBuilder.FinalizedAccesses
err := bal.EncodeRLP(&buf) err := bal.EncodeRLP(&buf)
if err != nil { if err != nil {
t.Fatalf("encoding failed: %v\n", err) t.Fatalf("encoding failed: %v\n", err)
@ -136,7 +137,7 @@ func makeTestAccountAccess(sort bool) AccountAccess {
} }
if sort { if sort {
slices.SortFunc(storageWrites, func(a, b encodingSlotWrites) int { slices.SortFunc(storageWrites, func(a, b encodingSlotWrites) int {
return bytes.Compare(a.Slot[:], b.Slot[:]) return bytes.Compare(a.Slot.inner.Bytes(), b.Slot.inner.Bytes())
}) })
} }
@ -173,7 +174,7 @@ func makeTestAccountAccess(sort bool) AccountAccess {
}) })
} }
var encodedStorageReads []EncodedStorage var encodedStorageReads []*EncodedStorage
for _, slot := range storageReads { for _, slot := range storageReads {
encodedStorageReads = append(encodedStorageReads, newEncodedStorageFromHash(slot)) encodedStorageReads = append(encodedStorageReads, newEncodedStorageFromHash(slot))
} }
@ -220,7 +221,7 @@ func TestBlockAccessListCopy(t *testing.T) {
// Make sure the mutations on copy won't affect the origin // Make sure the mutations on copy won't affect the origin
for _, aa := range cpyCpy { for _, aa := range cpyCpy {
for i := 0; i < len(aa.StorageReads); i++ { for i := 0; i < len(aa.StorageReads); i++ {
aa.StorageReads[i] = testrand.Bytes(32) aa.StorageReads[i] = &EncodedStorage{new(uint256.Int).SetBytes(testrand.Bytes(32))}
} }
} }
if !reflect.DeepEqual(list, cpy) { if !reflect.DeepEqual(list, cpy) {
@ -230,8 +231,9 @@ func TestBlockAccessListCopy(t *testing.T) {
func TestBlockAccessListValidation(t *testing.T) { func TestBlockAccessListValidation(t *testing.T) {
// Validate the block access list after RLP decoding // Validate the block access list after RLP decoding
testBALMaxIndex := 8
enc := makeTestBAL(true) enc := makeTestBAL(true)
if err := enc.Validate(); err != nil { if err := enc.Validate(testBALMaxIndex); err != nil {
t.Fatalf("Unexpected validation error: %v", err) t.Fatalf("Unexpected validation error: %v", err)
} }
var buf bytes.Buffer var buf bytes.Buffer
@ -243,14 +245,14 @@ func TestBlockAccessListValidation(t *testing.T) {
if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 0)); err != nil { if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 0)); err != nil {
t.Fatalf("Unexpected RLP-decode error: %v", err) t.Fatalf("Unexpected RLP-decode error: %v", err)
} }
if err := dec.Validate(); err != nil { if err := dec.Validate(testBALMaxIndex); err != nil {
t.Fatalf("Unexpected validation error: %v", err) t.Fatalf("Unexpected validation error: %v", err)
} }
// Validate the derived block access list // Validate the derived block access list
cBAL := makeTestConstructionBAL() cBAL := makeTestConstructionBAL().FinalizedAccesses
listB := cBAL.ToEncodingObj() listB := cBAL.ToEncodingObj()
if err := listB.Validate(); err != nil { if err := listB.Validate(testBALMaxIndex); err != nil {
t.Fatalf("Unexpected validation error: %v", err) t.Fatalf("Unexpected validation error: %v", err)
} }
} }

View file

@ -182,8 +182,7 @@ func (h *Header) EmptyReceipts() bool {
type Body struct { type Body struct {
Transactions []*Transaction Transactions []*Transaction
Uncles []*Header Uncles []*Header
Withdrawals []*Withdrawal `rlp:"optional"` Withdrawals []*Withdrawal `rlp:"optional"`
AccessList *bal.BlockAccessList `rlp:"optional,nil"`
} }
// Block represents an Ethereum block. // Block represents an Ethereum block.
@ -287,15 +286,17 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher ListHasher
b.withdrawals = slices.Clone(withdrawals) b.withdrawals = slices.Clone(withdrawals)
} }
if body.AccessList != nil {
balHash := body.AccessList.Hash()
b.header.BlockAccessListHash = &balHash
b.accessList = body.AccessList
}
return b return b
} }
func NewBlockWithAccessList(header *Header, body *Body, receipts []*Receipt, accessList *bal.BlockAccessList, hasher ListHasher) *Block {
block := NewBlock(header, body, receipts, hasher)
block.accessList = accessList
balHash := accessList.Hash()
block.header.BlockAccessListHash = &balHash
return block
}
// CopyHeader creates a deep copy of a block header. // CopyHeader creates a deep copy of a block header.
func CopyHeader(h *Header) *Header { func CopyHeader(h *Header) *Header {
cpy := *h cpy := *h
@ -367,15 +368,16 @@ func (b *Block) EncodeRLP(w io.Writer) error {
// Body returns the non-header content of the block. // Body returns the non-header content of the block.
// Note the returned data is not an independent copy. // Note the returned data is not an independent copy.
func (b *Block) Body() *Body { func (b *Block) Body() *Body {
return &Body{b.transactions, b.uncles, b.withdrawals, b.accessList} return &Body{b.transactions, b.uncles, b.withdrawals}
} }
// Accessors for body data. These do not return a copy because the content // 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. // of the body slices does not affect the cached hash/size in block.
func (b *Block) Uncles() []*Header { return b.uncles } func (b *Block) Uncles() []*Header { return b.uncles }
func (b *Block) Transactions() Transactions { return b.transactions } func (b *Block) Transactions() Transactions { return b.transactions }
func (b *Block) Withdrawals() Withdrawals { return b.withdrawals } func (b *Block) Withdrawals() Withdrawals { return b.withdrawals }
func (b *Block) AccessList() *bal.BlockAccessList { return b.accessList }
func (b *Block) Transaction(hash common.Hash) *Transaction { func (b *Block) Transaction(hash common.Hash) *Transaction {
for _, transaction := range b.transactions { for _, transaction := range b.transactions {
@ -522,16 +524,30 @@ func (b *Block) WithBody(body Body) *Block {
uncles: make([]*Header, len(body.Uncles)), uncles: make([]*Header, len(body.Uncles)),
withdrawals: slices.Clone(body.Withdrawals), withdrawals: slices.Clone(body.Withdrawals),
} }
if body.AccessList != nil {
balCopy := body.AccessList.Copy()
block.accessList = &balCopy
}
for i := range body.Uncles { for i := range body.Uncles {
block.uncles[i] = CopyHeader(body.Uncles[i]) block.uncles[i] = CopyHeader(body.Uncles[i])
} }
return block return block
} }
// WithAccessList returns a copy of the block with the access list embedded.
// It does not set the access list hash in the header of the returned block.
// TODO: ^ when support for --experimental.bal is removed, this function should set the access list hash in the header
func (b *Block) WithAccessList(accessList *bal.BlockAccessList) *Block {
alCopy := accessList.Copy()
block := &Block{
header: b.header,
transactions: slices.Clone(b.transactions),
uncles: make([]*Header, len(b.uncles)),
withdrawals: slices.Clone(b.withdrawals),
accessList: &alCopy,
}
for i := range b.uncles {
block.uncles[i] = CopyHeader(b.uncles[i])
}
return block
}
// Hash returns the keccak256 hash of b's header. // Hash returns the keccak256 hash of b's header.
// The hash is computed on the first call and cached thereafter. // The hash is computed on the first call and cached thereafter.
func (b *Block) Hash() common.Hash { func (b *Block) Hash() common.Hash {

View file

@ -514,8 +514,8 @@ func (b *EthAPIBackend) BlockAccessListByNumberOrHash(number rpc.BlockNumberOrHa
if block == nil { if block == nil {
return nil, fmt.Errorf("block not found") return nil, fmt.Errorf("block not found")
} }
if block.Body().AccessList == nil { if block.AccessList() == nil {
return nil, nil return nil, nil
} }
return block.Body().AccessList.StringableRepresentation(), nil return block.AccessList().StringableRepresentation(), nil
} }

View file

@ -102,7 +102,6 @@ func (f *fetchResult) body() types.Body {
Transactions: f.Transactions, Transactions: f.Transactions,
Uncles: f.Uncles, Uncles: f.Uncles,
Withdrawals: f.Withdrawals, Withdrawals: f.Withdrawals,
AccessList: f.AccessList,
} }
} }

View file

@ -970,8 +970,8 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param
if block.Withdrawals() != nil { if block.Withdrawals() != nil {
fields["withdrawals"] = block.Withdrawals() fields["withdrawals"] = block.Withdrawals()
} }
if block.Body().AccessList != nil { if block.AccessList() != nil {
fields["accessList"] = block.Body().AccessList fields["accessList"] = block.AccessList()
} }
return fields return fields
} }

View file

@ -20,6 +20,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types/bal"
"math/big" "math/big"
"sync/atomic" "sync/atomic"
"time" "time"
@ -147,9 +148,6 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay
} }
} }
body := types.Body{Transactions: work.txs, Withdrawals: genParam.withdrawals} body := types.Body{Transactions: work.txs, Withdrawals: genParam.withdrawals}
if work.alTracer != nil {
body.AccessList = work.alTracer.AccessList().ToEncodingObj()
}
allLogs := make([]*types.Log, 0) allLogs := make([]*types.Log, 0)
for _, r := range work.receipts { for _, r := range work.receipts {
@ -184,11 +182,12 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay
// I considered trying to instantiate the beacon consensus engine with a tracer. // I considered trying to instantiate the beacon consensus engine with a tracer.
// however, the BAL tracer instance is used once per block, while the engine object // however, the BAL tracer instance is used once per block, while the engine object
// lives for the entire time the client is running. // lives for the entire time the client is running.
onBlockFinalization := func() { onBlockFinalization := func() *bal.BlockAccessList {
if miner.chainConfig.IsAmsterdam(work.header.Number, work.header.Time) { if miner.chainConfig.IsAmsterdam(work.header.Number, work.header.Time) {
work.alTracer.OnBlockFinalization() work.alTracer.OnBlockFinalization()
body.AccessList = work.alTracer.AccessList().ToEncodingObj() return work.alTracer.AccessList().ToEncodingObj()
} }
return nil
} }
block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts, onBlockFinalization) block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts, onBlockFinalization)