From 599c461cad727174ecc4c417bfbe1b2118ee3bc2 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Fri, 29 Mar 2024 12:56:46 -0400 Subject: [PATCH 1/6] The beacon root must be processed before any transactions to have correct state The beacon root when applied in `state_processor.go` is performed right before executing transaction. That means that contract reliying on this value would query the same value found in the block header. In that spirit, it means that any tracing/operation relying on state data which touches transaction must have updated the beacon root before any transaction processing. This PR brings this where I spotted where it was important through `StateAtTransaction` and those using with `StateAtBlock` and transactions. For now I've put the beacon root update outside of `StateAtBlock`. I was reluctant to include it in `StateAtBlock` because technically the state at block doesn't include any state happening "in the block". But everywhere that `StateAtBlock` was used, it felt setting the beacon root was needed, so it would be posssible to change the semantic of the function and run any system pre-hooks. Kinda relevant with PR #29372 --- eth/state_accessor.go | 8 ++++++++ eth/tracers/api.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 770532cbfe73..24d9c6b26a63 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -233,6 +233,14 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, if err != nil { return nil, vm.BlockContext{}, nil, nil, err } + + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { + context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) + vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, eth.blockchain.Config(), vm.Config{}) + + core.ProcessBeaconBlockRoot(*block.BeaconRoot(), vmenv, statedb) + } + if txIndex == 0 && len(block.Transactions()) == 0 { return nil, vm.BlockContext{}, statedb, release, nil } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 7a7c5e48d901..e234851e3705 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -376,6 +376,14 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed failed = err break } + + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { + context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) + + core.ProcessBeaconBlockRoot(*block.BeaconRoot(), vmenv, statedb) + } + // Clean out any pending release functions of trace state. Note this // step must be done after constructing tracing state, because the // tracing state of block next depends on the parent state and construction @@ -518,6 +526,13 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config } defer release() + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { + context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) + + core.ProcessBeaconBlockRoot(*block.BeaconRoot(), vmenv, statedb) + } + var ( roots []common.Hash signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) @@ -585,6 +600,13 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac } defer release() + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { + context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) + + core.ProcessBeaconBlockRoot(*block.BeaconRoot(), vmenv, statedb) + } + // JS tracers have high overhead. In this case run a parallel // process that generates states in one thread and traces txes // in separate worker threads. @@ -728,6 +750,13 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block } defer release() + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { + context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) + + core.ProcessBeaconBlockRoot(*block.BeaconRoot(), vmenv, statedb) + } + // Retrieve the tracing configurations, or use default values var ( logConfig logger.Config @@ -916,6 +945,15 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc } defer release() + if config != nil && config.TxIndex != nil { + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { + context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) + + core.ProcessBeaconBlockRoot(*block.BeaconRoot(), vmenv, statedb) + } + } + vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) // Apply the customization rules if required. if config != nil { From c9c894c07a2638c3b56d542884c802e449cb3506 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Fri, 29 Mar 2024 15:44:50 -0400 Subject: [PATCH 2/6] Inverted wrong logic to apply beacon root in `TraceCall` --- eth/tracers/api.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index e234851e3705..501978055934 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -945,7 +945,8 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc } defer release() - if config != nil && config.TxIndex != nil { + // Apply only when we have used StateAtBlock since `StateAtTransaction` already handles the beacon root + if config == nil || config.TxIndex == nil { if beaconRoot := block.BeaconRoot(); beaconRoot != nil { context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) From 43b91b39f60d9fb565f534e0a26b3786ebabb767 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 16 Apr 2024 11:32:53 +0200 Subject: [PATCH 3/6] fix traceChain beacon root --- eth/tracers/api.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 501978055934..7a4c4b186244 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -376,14 +376,13 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed failed = err break } - - if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + // Insert block's parent beacon block root in the state + // as per EIP-4788. + if beaconRoot := next.BeaconRoot(); beaconRoot != nil { + context := core.NewEVMBlockContext(next.Header(), api.chainContext(ctx), nil) vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) - - core.ProcessBeaconBlockRoot(*block.BeaconRoot(), vmenv, statedb) + core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) } - // Clean out any pending release functions of trace state. Note this // step must be done after constructing tracing state, because the // tracing state of block next depends on the parent state and construction From e1637b32a5429ffe7789ae0c3e8aa9e0ede7fb62 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 16 Apr 2024 12:25:49 +0200 Subject: [PATCH 4/6] minor --- eth/state_accessor.go | 6 ++---- eth/tracers/api.go | 15 +++------------ 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 24d9c6b26a63..372c76f49692 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -233,14 +233,12 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, if err != nil { return nil, vm.BlockContext{}, nil, nil, err } - + // Insert parent beacon block root in the state as per EIP-4788. if beaconRoot := block.BeaconRoot(); beaconRoot != nil { context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, eth.blockchain.Config(), vm.Config{}) - - core.ProcessBeaconBlockRoot(*block.BeaconRoot(), vmenv, statedb) + core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) } - if txIndex == 0 && len(block.Transactions()) == 0 { return nil, vm.BlockContext{}, statedb, release, nil } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 7a4c4b186244..3ef459168f7d 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -524,14 +524,11 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config return nil, err } defer release() - if beaconRoot := block.BeaconRoot(); beaconRoot != nil { context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) - - core.ProcessBeaconBlockRoot(*block.BeaconRoot(), vmenv, statedb) + core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) } - var ( roots []common.Hash signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) @@ -598,14 +595,11 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac return nil, err } defer release() - if beaconRoot := block.BeaconRoot(); beaconRoot != nil { context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) - - core.ProcessBeaconBlockRoot(*block.BeaconRoot(), vmenv, statedb) + core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) } - // JS tracers have high overhead. In this case run a parallel // process that generates states in one thread and traces txes // in separate worker threads. @@ -748,14 +742,11 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block return nil, err } defer release() - if beaconRoot := block.BeaconRoot(); beaconRoot != nil { context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) - - core.ProcessBeaconBlockRoot(*block.BeaconRoot(), vmenv, statedb) + core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) } - // Retrieve the tracing configurations, or use default values var ( logConfig logger.Config From f7e87d1a898d24614d9f39c918a41ea2d615a0d7 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 16 Apr 2024 12:26:16 +0200 Subject: [PATCH 5/6] rm traceCall beacon root insertion --- eth/tracers/api.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 3ef459168f7d..d522030cfc7c 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -935,16 +935,6 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc } defer release() - // Apply only when we have used StateAtBlock since `StateAtTransaction` already handles the beacon root - if config == nil || config.TxIndex == nil { - if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) - - core.ProcessBeaconBlockRoot(*block.BeaconRoot(), vmenv, statedb) - } - } - vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) // Apply the customization rules if required. if config != nil { From c3951c3e1d2f5df1d4358ed1b3c1e6822a0dc4c7 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 16 Apr 2024 12:34:47 +0200 Subject: [PATCH 6/6] move down beacn root processing --- eth/tracers/api.go | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index d522030cfc7c..d99531d48fc9 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -524,11 +524,6 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config return nil, err } defer release() - if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) - core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) - } var ( roots []common.Hash signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) @@ -536,6 +531,10 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) deleteEmptyObjects = chainConfig.IsEIP158(block.Number()) ) + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { + vmenv := vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{}) + core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) + } for i, tx := range block.Transactions() { if err := ctx.Err(); err != nil { return nil, err @@ -595,11 +594,6 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac return nil, err } defer release() - if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) - core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) - } // JS tracers have high overhead. In this case run a parallel // process that generates states in one thread and traces txes // in separate worker threads. @@ -616,6 +610,10 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) results = make([]*txTraceResult, len(txs)) ) + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { + vmenv := vm.NewEVM(blockCtx, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) + core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) + } for i, tx := range txs { // Generate the next state snapshot fast without tracing msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) @@ -742,11 +740,6 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block return nil, err } defer release() - if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - context := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) - core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) - } // Retrieve the tracing configurations, or use default values var ( logConfig logger.Config @@ -775,6 +768,10 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Note: This copies the config, to not screw up the main config chainConfig, canon = overrideConfig(chainConfig, config.Overrides) } + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { + vmenv := vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{}) + core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) + } for i, tx := range block.Transactions() { // Prepare the transaction for un-traced execution var (