diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index f35b28510c29..d711ae56acce 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -242,6 +242,13 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 { misc.ApplyDAOHardFork(statedb) } + // EIP-7997: insert the deterministic deployment factory at the Amsterdam + // activation block via an irregular state transition. + if pre.Env.Number > 0 && + chainConfig.IsAmsterdam(new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) && + !chainConfig.IsAmsterdam(new(big.Int).SetUint64(pre.Env.Number-1), pre.Env.ParentTimestamp) { + misc.ApplyEIP7997(statedb) + } evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig) if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil { core.ProcessBeaconBlockRoot(*beaconRoot, evm, blockAccessList) diff --git a/core/state_processor.go b/core/state_processor.go index bc565db6f9a1..27009cb49aa2 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -81,10 +81,9 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(block.Number()) == 0 { misc.ApplyDAOHardFork(tracingStateDB) } - // EIP-7997: insert the deterministic deployment factory at the Amsterdam - // activation block via an irregular state transition. - if isEIP7997Transition(config, p.chain, header) { - misc.ApplyEIP7997(tracingStateDB) + parent := p.chain.GetHeader(block.ParentHash(), block.NumberU64()-1) + if parent == nil { + return nil, fmt.Errorf("missing parent %#x", block.ParentHash()) } var ( context = NewEVMBlockContext(header, p.chain, nil) @@ -98,7 +97,7 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated evm.SetJumpDestCache(jumpDestCache) } // Run the pre-execution system calls - blockAccessList.Merge(PreExecution(ctx, block.BeaconRoot(), block.ParentHash(), config, evm, block.Number(), block.Time())) + blockAccessList.Merge(PreExecution(ctx, block.BeaconRoot(), parent, config, evm, block.Number(), block.Time())) // Iterate over and process the individual transactions for i, tx := range block.Transactions() { @@ -142,27 +141,20 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated }, nil } -// isEIP7997Transition reports whether the given header belongs to the first block -// on which the Amsterdam fork is active. -func isEIP7997Transition(config *params.ChainConfig, chain ChainContext, header *types.Header) bool { - if header.Number.Sign() == 0 || !config.IsAmsterdam(header.Number, header.Time) { - return false - } - parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) - if parent == nil { - return false - } - return !config.IsAmsterdam(parent.Number, parent.Time) -} - -// PreExecution processes pre-execution system calls. -func PreExecution(ctx context.Context, beaconRoot *common.Hash, parent common.Hash, config *params.ChainConfig, evm *vm.EVM, number *big.Int, time uint64) *bal.ConstructionBlockAccessList { +// PreExecution processes pre-execution state changes and system calls. +func PreExecution(ctx context.Context, beaconRoot *common.Hash, parent *types.Header, config *params.ChainConfig, evm *vm.EVM, number *big.Int, time uint64) *bal.ConstructionBlockAccessList { _, _, spanEnd := telemetry.StartSpan(ctx, "core.preExecution") defer spanEnd(nil) var blockAccessList *bal.ConstructionBlockAccessList if config.IsAmsterdam(number, time) { blockAccessList = bal.NewConstructionBlockAccessList() + + // EIP-7997: insert the deterministic deployment factory at the Amsterdam + // activation block via an irregular state transition. + if !config.IsAmsterdam(parent.Number, parent.Time) { + misc.ApplyEIP7997(evm.StateDB) + } } // EIP-4788 if beaconRoot != nil { @@ -170,7 +162,7 @@ func PreExecution(ctx context.Context, beaconRoot *common.Hash, parent common.Ha } // EIP-2935 if config.IsPrague(number, time) || config.IsUBT(number, time) { - ProcessParentBlockHash(parent, evm, blockAccessList) + ProcessParentBlockHash(parent.Hash(), evm, blockAccessList) } return blockAccessList } diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 3c3539dbdb64..7bcd83d5811e 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -250,7 +250,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, defer evm.Release() // Run pre-execution system calls - core.PreExecution(ctx, block.BeaconRoot(), block.ParentHash(), eth.blockchain.Config(), evm, block.Number(), block.Time()) + core.PreExecution(ctx, block.BeaconRoot(), parent.Header(), eth.blockchain.Config(), evm, block.Number(), block.Time()) if txIndex == 0 && len(block.Transactions()) == 0 { return nil, context, statedb, release, nil diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 88132b4b6304..d103b8ef350c 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -373,7 +373,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed context := core.NewEVMBlockContext(next.Header(), api.chainContext(ctx), nil) evm := vm.NewEVM(context, statedb, api.backend.ChainConfig(), vm.Config{}) - core.PreExecution(ctx, next.BeaconRoot(), next.ParentHash(), api.backend.ChainConfig(), evm, next.Number(), next.Time()) + core.PreExecution(ctx, next.BeaconRoot(), block.Header(), api.backend.ChainConfig(), evm, next.Number(), next.Time()) evm.Release() // Clean out any pending release functions of trace state. Note this // step must be done after constructing tracing state, because the @@ -523,7 +523,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config ) defer evm.Release() // Run pre-execution system calls - core.PreExecution(ctx, block.BeaconRoot(), block.ParentHash(), chainConfig, evm, block.Number(), block.Time()) + core.PreExecution(ctx, block.BeaconRoot(), parent.Header(), chainConfig, evm, block.Number(), block.Time()) for i, tx := range block.Transactions() { if err := ctx.Err(); err != nil { @@ -582,7 +582,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac defer evm.Release() // Run pre-execution system calls - core.PreExecution(ctx, block.BeaconRoot(), block.ParentHash(), api.backend.ChainConfig(), evm, block.Number(), block.Time()) + core.PreExecution(ctx, block.BeaconRoot(), parent.Header(), api.backend.ChainConfig(), evm, block.Number(), block.Time()) // JS tracers have high overhead. In this case run a parallel // process that generates states in one thread and traces txes @@ -754,7 +754,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block defer evm.Release() // Run pre-execution system calls - core.PreExecution(ctx, block.BeaconRoot(), block.ParentHash(), chainConfig, evm, block.Number(), block.Time()) + core.PreExecution(ctx, block.BeaconRoot(), parent.Header(), chainConfig, evm, block.Number(), block.Time()) for i, tx := range block.Transactions() { // Prepare the transaction for un-traced execution diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 8462194b1d33..0da2672756d9 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -322,7 +322,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, evm.SetPrecompiles(precompiles) } // Run pre-execution system calls - blockAccessList.Merge(core.PreExecution(ctx, header.ParentBeaconRoot, header.ParentHash, sim.chainConfig, evm, header.Number, header.Time)) + blockAccessList.Merge(core.PreExecution(ctx, header.ParentBeaconRoot, parent, sim.chainConfig, evm, header.Number, header.Time)) var allLogs []*types.Log for i, call := range block.Calls { diff --git a/miner/payload_building_test.go b/miner/payload_building_test.go index f8e495cc99fe..734a5605badb 100644 --- a/miner/payload_building_test.go +++ b/miner/payload_building_test.go @@ -17,6 +17,7 @@ package miner import ( + "bytes" "context" "math/big" "reflect" @@ -26,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" @@ -115,6 +117,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes()) e.Authorize(testBankAddress) case *ethash.Ethash: + case *beacon.Beacon: default: t.Fatalf("unexpected consensus engine type: %T", engine) } @@ -194,6 +197,58 @@ func TestBuildPayload(t *testing.T) { } } +// TestBuildPayloadAmsterdamTransition verifies that a locally built payload for +// the first Amsterdam block contains the EIP-7997 deterministic deployment +// factory, i.e. the block-building path applies the same irregular state +// transition as block processing and the resulting block is importable. +func TestBuildPayloadAmsterdamTransition(t *testing.T) { + var ( + db = rawdb.NewMemoryDatabase() + recipient = common.HexToAddress("0xdeadbeef") + ) + config := new(params.ChainConfig) + *config = *params.MergedTestChainConfig + config.AmsterdamTime = new(uint64) + *config.AmsterdamTime = 1 // genesis (t=0) is pre-Amsterdam, the first block crosses the fork + + w, b := newTestWorker(t, config, beacon.New(ethash.NewFaker()), db, 0) + + var ( + beaconRoot = common.Hash{0x01} + slotNum = uint64(1) + ) + payload, err := w.buildPayload(context.Background(), &BuildPayloadArgs{ + Parent: b.chain.CurrentBlock().Hash(), + Timestamp: 1, + FeeRecipient: recipient, + Withdrawals: types.Withdrawals{}, + BeaconRoot: &beaconRoot, + SlotNum: &slotNum, + }, false) + if err != nil { + t.Fatalf("Failed to build payload %v", err) + } + block := payload.empty + if !config.IsAmsterdam(block.Number(), block.Time()) { + t.Fatal("transition block is not an Amsterdam block") + } + // The block must be importable: Process applies EIP-7997 independently, so + // a payload built without the factory would fail the state root check here. + if _, err := b.chain.InsertChain(types.Blocks{block}); err != nil { + t.Fatalf("failed to insert transition block: %v", err) + } + statedb, err := b.chain.StateAt(block.Header()) + if err != nil { + t.Fatalf("failed to open state at transition block: %v", err) + } + if code := statedb.GetCode(params.DeterministicFactoryAddress); !bytes.Equal(code, params.DeterministicFactoryCode) { + t.Fatalf("factory code missing from built payload state:\n got %x\nwant %x", code, params.DeterministicFactoryCode) + } + if nonce := statedb.GetNonce(params.DeterministicFactoryAddress); nonce != 1 { + t.Fatalf("factory nonce = %d, want 1", nonce) + } +} + func TestPayloadId(t *testing.T) { t.Parallel() ids := make(map[string]int) diff --git a/miner/worker.go b/miner/worker.go index 01a14b8a0244..7f0e11f30aaf 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -326,7 +326,7 @@ func (miner *Miner) prepareWork(ctx context.Context, genParams *generateParams, return nil, err } // Run pre-execution system calls - env.bal.Merge(core.PreExecution(ctx, header.ParentBeaconRoot, header.ParentHash, miner.chainConfig, env.evm, header.Number, header.Time)) + env.bal.Merge(core.PreExecution(ctx, header.ParentBeaconRoot, parent, miner.chainConfig, env.evm, header.Number, header.Time)) return env, nil }