Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/evm/blockrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func runBlockTest(ctx *cli.Context, fname string) ([]testResult, error) {
continue
}
result := &testResult{Name: name, Pass: true}
if err := tests[name].Run(false, rawdb.PathScheme, ctx.Bool(WitnessCrossCheckFlag.Name), tracer, func(res error, chain *core.BlockChain) {
if err := tests[name].Run(false, rawdb.PathScheme, ctx.Bool(WitnessCrossCheckFlag.Name), false, tracer, func(res error, chain *core.BlockChain) {
if ctx.Bool(DumpFlag.Name) {
if s, _ := chain.State(); s != nil {
result.State = dump(s)
Expand Down
1 change: 1 addition & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ var (
utils.BeaconGenesisTimeFlag,
utils.BeaconCheckpointFlag,
utils.BeaconCheckpointFileFlag,
utils.ExperimentalBALFlag,
}, utils.NetworkFlags, utils.DatabaseFlags)

rpcFlags = []cli.Flag{
Expand Down
11 changes: 11 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,14 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
Value: metrics.DefaultConfig.InfluxDBOrganization,
Category: flags.MetricsCategory,
}

// Block Access List flags

ExperimentalBALFlag = &cli.BoolFlag{
Name: "experimental.bal",
Usage: "Enable generation of EIP-7928 block access lists when importing post-Cancun blocks which lack them. When this flag is specified, importing blocks containing access lists triggers validation of their correctness and execution based off them. The header block access list field is not set with blocks created when this flag is specified, nor is it validated when importing blocks that contain access lists. This is used for development purposes only. Do not enable it otherwise.",
Category: flags.MiscCategory,
}
)

var (
Expand Down Expand Up @@ -1917,6 +1925,8 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
cfg.VMTraceJsonConfig = ctx.String(VMTraceJsonConfigFlag.Name)
}
}

cfg.ExperimentalBAL = ctx.Bool(ExperimentalBALFlag.Name)
}

// MakeBeaconLightConfig constructs a beacon light client config based on the
Expand Down Expand Up @@ -2319,6 +2329,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
}
options.VmConfig = vmcfg

options.EnableBALForTesting = ctx.Bool(ExperimentalBALFlag.Name)
chain, err := core.NewBlockChain(chainDb, gspec, engine, options)
if err != nil {
Fatalf("Can't create BlockChain: %v", err)
Expand Down
8 changes: 6 additions & 2 deletions consensus/beacon/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,9 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.

// FinalizeAndAssemble implements consensus.Engine, setting the final state and
// assembling the block.
func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalization func()) (*types.Block, error) {
if !beacon.IsPoSHeader(header) {
return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts)
return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts, onFinalization)
}
shanghai := chain.Config().IsShanghai(header.Number, header.Time)
if shanghai {
Expand All @@ -364,6 +364,10 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
// Assign the final state root to header.
header.Root = state.IntermediateRoot(true)

if onFinalization != nil {
onFinalization()
}

// Assemble the final block.
block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil))

Expand Down
6 changes: 5 additions & 1 deletion consensus/clique/clique.go
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade

// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// 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) (*types.Block, error) {
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalize func()) (*types.Block, error) {
if len(body.Withdrawals) > 0 {
return nil, errors.New("clique does not support withdrawals")
}
Expand All @@ -589,6 +589,10 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *
// Assign the final state root to header.
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))

if onFinalize != nil {
onFinalize()
}

// Assemble and return the final block for sealing.
return types.NewBlock(header, &types.Body{Transactions: body.Transactions}, receipts, trie.NewStackTrie(nil)), nil
}
Expand Down
2 changes: 1 addition & 1 deletion consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ type Engine interface {
//
// Note: The block header and state database might be updated to reflect any
// 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) (*types.Block, error)
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalization func()) (*types.Block, error)

// Seal generates a new sealing request for the given input block and pushes
// the result into the given channel.
Expand Down
5 changes: 4 additions & 1 deletion consensus/ethash/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.

// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
// 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) (*types.Block, error) {
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, onFinalize func()) (*types.Block, error) {
if len(body.Withdrawals) > 0 {
return nil, errors.New("ethash does not support withdrawals")
}
Expand All @@ -521,6 +521,9 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
// Assign the final state root to header.
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))

if onFinalize != nil {
onFinalize()
}
// 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
}
Expand Down
2 changes: 2 additions & 0 deletions consensus/misc/eip4844/eip4844.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ func latestBlobConfig(cfg *params.ChainConfig, time uint64) *BlobConfig {
bc = s.BPO2
case cfg.IsBPO1(london, time) && s.BPO1 != nil:
bc = s.BPO1
case cfg.IsAmsterdam(london, time) && s.Amsterdam != nil:
bc = s.Amsterdam
case cfg.IsOsaka(london, time) && s.Osaka != nil:
bc = s.Osaka
case cfg.IsPrague(london, time) && s.Prague != nil:
Expand Down
102 changes: 102 additions & 0 deletions core/block_access_list_tracer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package core

import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types/bal"
"github.com/holiman/uint256"
"math/big"
)

// BlockAccessListTracer is a tracer which gathers state accesses/mutations
// from the execution of a block. It is used for constructing and verifying
// EIP-7928 block access lists.
type BlockAccessListTracer struct {
builder *bal.AccessListBuilder

// the access list index that changes are currently being recorded into
balIdx uint16
}

// NewBlockAccessListTracer returns an BlockAccessListTracer and a set of hooks
func NewBlockAccessListTracer() (*BlockAccessListTracer, *tracing.Hooks) {
balTracer := &BlockAccessListTracer{
builder: bal.NewAccessListBuilder(),
}
hooks := &tracing.Hooks{
OnBlockFinalization: balTracer.OnBlockFinalization,
OnPreTxExecutionDone: balTracer.OnPreTxExecutionDone,
OnTxEnd: balTracer.TxEndHook,
OnEnter: balTracer.OnEnter,
OnExit: balTracer.OnExit,
OnCodeChangeV2: balTracer.OnCodeChange,
OnBalanceChange: balTracer.OnBalanceChange,
OnNonceChangeV2: balTracer.OnNonceChange,
OnStorageChange: balTracer.OnStorageChange,
OnStorageRead: balTracer.OnStorageRead,
OnAccountRead: balTracer.OnAcountRead,
OnSelfDestructChange: balTracer.OnSelfDestruct,
}
wrappedHooks, _ := tracing.WrapWithJournal(hooks)
return balTracer, wrappedHooks
}

// AccessList returns the constructed access list.
// It is assumed that this is only called after all the block state changes
// have been executed and the block has been finalized.
func (a *BlockAccessListTracer) AccessList() *bal.AccessListBuilder {
return a.builder
}

func (a *BlockAccessListTracer) OnPreTxExecutionDone() {
a.builder.FinaliseIdxChanges(0)
a.balIdx++
}

func (a *BlockAccessListTracer) TxEndHook(receipt *types.Receipt, err error) {
a.builder.FinaliseIdxChanges(a.balIdx)
a.balIdx++
}

func (a *BlockAccessListTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
a.builder.EnterScope()
}

func (a *BlockAccessListTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
a.builder.ExitScope(reverted)
}

func (a *BlockAccessListTracer) OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason tracing.CodeChangeReason) {
a.builder.CodeChange(addr, prevCode, code)
}

func (a *BlockAccessListTracer) OnSelfDestruct(addr common.Address) {
a.builder.SelfDestruct(addr)
}

func (a *BlockAccessListTracer) OnBlockFinalization() {
a.builder.FinaliseIdxChanges(a.balIdx)
}

func (a *BlockAccessListTracer) OnBalanceChange(addr common.Address, prevBalance, newBalance *big.Int, _ tracing.BalanceChangeReason) {
newU256 := new(uint256.Int).SetBytes(newBalance.Bytes())
prevU256 := new(uint256.Int).SetBytes(prevBalance.Bytes())
a.builder.BalanceChange(addr, prevU256, newU256)
}

func (a *BlockAccessListTracer) OnNonceChange(addr common.Address, prev uint64, new uint64, reason tracing.NonceChangeReason) {
a.builder.NonceChange(addr, prev, new)
}

func (a *BlockAccessListTracer) OnStorageRead(addr common.Address, key common.Hash) {
a.builder.StorageRead(addr, key)
}

func (a *BlockAccessListTracer) OnAcountRead(addr common.Address) {
a.builder.AccountRead(addr)
}

func (a *BlockAccessListTracer) OnStorageChange(addr common.Address, slot common.Hash, prev common.Hash, new common.Hash) {
a.builder.StorageWrite(addr, slot, prev, new)
}
25 changes: 25 additions & 0 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,31 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
}
}

// block access lists must be present after the Amsterdam hard fork
if v.config.IsAmsterdam(block.Number(), block.Time()) {
if block.Body().AccessList == nil {
return fmt.Errorf("access list not present in block body")
} else if *block.Header().BlockAccessListHash != block.Body().AccessList.Hash() {
return fmt.Errorf("access list hash mismatch. local: %x. remote: %x\n", block.Body().AccessList.Hash(), *block.Header().BlockAccessListHash)
} else if err := block.Body().AccessList.Validate(); err != nil {
return fmt.Errorf("invalid block access list: %v", err)
}
} else if !v.bc.cfg.EnableBALForTesting {
// if --experimental.bal is not enabled, block headers cannot have access list hash and bodies cannot have access lists.
if block.Body().AccessList != nil {
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 {
return fmt.Errorf("access list hash in block header not allowed when --experimental.bal is set")
}
} else {
// if --experimental.bal is enabled, the BAL hash is not allowed in the header.
// this is in order that Geth can import pre-existing chains augmented with BALs
// and not have a hash mismatch.
if block.Header().BlockAccessListHash != nil {
return fmt.Errorf("access list hash in block header not allowed pre-amsterdam")
}
}

// Ancestor block must be known.
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
Expand Down
34 changes: 32 additions & 2 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ type BlockChainConfig struct {
// If the value is -1, indexing is disabled.
TxLookupLimit int64

// If EnableBALForTesting is enabled, block access lists will be created
// from block execution and embedded in the body. The block access list
// hash will not be set in the header.
EnableBALForTesting bool

// StateSizeTracking indicates whether the state size tracking is enabled.
StateSizeTracking bool
}
Expand Down Expand Up @@ -1911,7 +1916,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
}
// The traced section of block import.
start := time.Now()
res, err := bc.ProcessBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1)
enableBAL := (bc.cfg.EnableBALForTesting && bc.chainConfig.IsCancun(block.Number(), block.Time())) || bc.chainConfig.IsAmsterdam(block.Number(), block.Time())
blockHasAccessList := block.Body().AccessList != nil
constructBAL := enableBAL && !blockHasAccessList

res, err := bc.ProcessBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1, constructBAL, false)
if err != nil {
return nil, it.index, err
}
Expand Down Expand Up @@ -1983,7 +1992,7 @@ func (bpr *blockProcessingResult) Witness() *stateless.Witness {

// ProcessBlock executes and validates the given block. If there was no error
// it writes the block and associated state to database.
func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) {
func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool, constructBALForTesting bool, validateBAL bool) (_ *blockProcessingResult, blockEndErr error) {
var (
err error
startTime = time.Now()
Expand Down Expand Up @@ -2079,6 +2088,14 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
}()
}

var balTracer *BlockAccessListTracer
// Process block using the parent state as reference point
if constructBALForTesting {
balTracer, bc.cfg.VmConfig.Tracer = NewBlockAccessListTracer()
defer func() {
bc.cfg.VmConfig.Tracer = nil
}()
}
// Process block using the parent state as reference point
pstart := time.Now()
res, err := bc.processor.Process(block, statedb, bc.cfg.VmConfig)
Expand All @@ -2088,11 +2105,24 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
}
ptime := time.Since(pstart)

if constructBALForTesting {
balTracer.OnBlockFinalization()
}

vstart := time.Now()
if err := bc.validator.ValidateState(block, statedb, res, false); err != nil {
bc.reportBlock(block, res, err)
return nil, err
}
if constructBALForTesting {
// very ugly... deep-copy the block body before setting the block access
// 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)
}
vtime := time.Since(vstart)

// If witnesses was generated and stateless self-validation requested, do
Expand Down
4 changes: 2 additions & 2 deletions core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
}

body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals}
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts)
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts, nil)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -520,7 +520,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
Uncles: b.uncles,
Withdrawals: b.withdrawals,
}
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, body, b.receipts)
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, body, b.receipts, nil)
if err != nil {
panic(err)
}
Expand Down
Loading
Loading