From 867efad997f71428bb90f0ec80c42fd196fbec88 Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Thu, 2 Jul 2026 14:19:57 +0800 Subject: [PATCH] core, consensus: update irregular state transition --- consensus/misc/eip7997.go | 19 ++++++++++--------- core/eip7997_test.go | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/consensus/misc/eip7997.go b/consensus/misc/eip7997.go index e01c099638..8564d5283a 100644 --- a/consensus/misc/eip7997.go +++ b/consensus/misc/eip7997.go @@ -17,10 +17,9 @@ package misc 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/vm" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) @@ -29,12 +28,10 @@ import ( // CREATE2 factory that, once present at the canonical address on every EVM chain, // allows contracts to be deployed at identical addresses across chains. func ApplyEIP7997(statedb vm.StateDB) { - contractHash := statedb.GetCodeHash(params.DeterministicFactoryAddress) - nonce := statedb.GetNonce(params.DeterministicFactoryAddress) - - // Reject the irregular state transition if the destination doesn't - // satisfy the deployment condition. - if nonce != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) { + // The account must hold the canonical factory runtime code. If its code hash + // already matches, the chain satisfies EIP-7997 and nothing needs to change. + wantHash := crypto.Keccak256Hash(params.DeterministicFactoryCode) + if statedb.GetCodeHash(params.DeterministicFactoryAddress) == wantHash { return } if !statedb.Exist(params.DeterministicFactoryAddress) { @@ -42,5 +39,9 @@ func ApplyEIP7997(statedb vm.StateDB) { } statedb.CreateContract(params.DeterministicFactoryAddress) statedb.SetCode(params.DeterministicFactoryAddress, params.DeterministicFactoryCode, tracing.CodeChangeUnspecified) - statedb.SetNonce(params.DeterministicFactoryAddress, 1, tracing.NonceChangeNewContract) + + // Preserve a pre-existing nonce; only bump the default zero nonce to 1. + if statedb.GetNonce(params.DeterministicFactoryAddress) == 0 { + statedb.SetNonce(params.DeterministicFactoryAddress, 1, tracing.NonceChangeNewContract) + } } diff --git a/core/eip7997_test.go b/core/eip7997_test.go index 3922d78b1c..4c4d26507b 100644 --- a/core/eip7997_test.go +++ b/core/eip7997_test.go @@ -60,6 +60,23 @@ func TestApplyEIP7997Existing(t *testing.T) { } } +// TestApplyEIP7997WrongCode checks that an account occupying the factory address +// with the wrong code is force-overwritten with the canonical runtime code, while +// a pre-existing non-zero nonce is preserved. +func TestApplyEIP7997WrongCode(t *testing.T) { + sdb := mkState(types.GenesisAlloc{ + params.DeterministicFactoryAddress: {Code: []byte{0x60, 0x00}, Nonce: 7}, + }) + misc.ApplyEIP7997(sdb) + + if got := sdb.GetCode(params.DeterministicFactoryAddress); !bytes.Equal(got, params.DeterministicFactoryCode) { + t.Fatalf("factory code not overwritten:\n got %x\nwant %x", got, params.DeterministicFactoryCode) + } + if got := sdb.GetNonce(params.DeterministicFactoryAddress); got != 7 { + t.Fatalf("factory nonce = %d, want %d (existing nonce must be preserved)", got, 7) + } +} + // TestEIP7997FactoryDeploys exercises the inserted factory bytecode: calling it // with a salt followed by init code must CREATE2-deploy the contract at the // canonical deterministic address and return that address (20 bytes, unpadded).