From c170cc0ab0a1f60adcde80d0af8e3050ee19da93 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 8 Apr 2024 21:48:37 +0800 Subject: [PATCH] core/vm: reject contract creation if the storage is non-empty (#28912) This change implements EIP-7610, which rejects the contract deployment if the destination has non-empty storage. --- core/vm/evm.go | 12 +++++++++--- core/vm/interface.go | 1 + tests/block_test.go | 8 ++++++++ tests/state_test.go | 13 +++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 25b5bc84e8..36bbf0d3da 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -439,13 +439,19 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.chainRules.IsBerlin { evm.StateDB.AddAddressToAccessList(address) } - // Ensure there's no existing contract already at the designated address + // Ensure there's no existing contract already at the designated address. + // Account is regarded as existent if any of these three conditions is met: + // - the nonce is nonzero + // - the code is non-empty + // - the storage is non-empty contractHash := evm.StateDB.GetCodeHash(address) - if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) { + storageRoot := evm.StateDB.GetStorageRoot(address) + if evm.StateDB.GetNonce(address) != 0 || + (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code + (storageRoot != (common.Hash{}) && storageRoot != types.EmptyRootHash) { // non-empty storage if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution) } - return nil, common.Address{}, 0, ErrContractAddressCollision } // Create a new account on the state diff --git a/core/vm/interface.go b/core/vm/interface.go index d7028cc7c7..30742e96de 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -49,6 +49,7 @@ type StateDB interface { GetCommittedState(common.Address, common.Hash) common.Hash GetState(common.Address, common.Hash) common.Hash SetState(common.Address, common.Hash, common.Hash) + GetStorageRoot(addr common.Address) common.Hash GetTransientState(addr common.Address, key common.Hash) common.Hash SetTransientState(addr common.Address, key, value common.Hash) diff --git a/tests/block_test.go b/tests/block_test.go index 1ba84f5f24..43e3d99b3e 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -64,6 +64,14 @@ func TestExecutionSpecBlocktests(t *testing.T) { } bt := new(testMatcher) + // These tests fail as of https://github.com/ethereum/go-ethereum/pull/28666, since we + // no longer delete "leftover storage" when deploying a contract. + bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/self_destructing_initcode_create_tx.json`) + bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/self_destructing_initcode.json`) + bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/recreate_self_destructed_contract_different_txs.json`) + bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/delegatecall_from_new_contract_to_pre_existing_contract.json`) + bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/create_selfdestruct_same_tx.json`) + bt.walk(t, executionSpecBlockchainTestDir, func(t *testing.T, name string, test *BlockTest) { execBlockTest(t, bt, test) }) diff --git a/tests/state_test.go b/tests/state_test.go index 6ec5c9d857..6f53b88722 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -54,9 +54,22 @@ func initMatcher(st *testMatcher) { // Uses 1GB RAM per tested fork st.skipLoad(`^stStaticCall/static_Call1MB`) + // These tests fail as of https://github.com/ethereum/go-ethereum/pull/28666, since we + // no longer delete "leftover storage" when deploying a contract. + st.skipLoad(`^stSStoreTest/InitCollision\.json`) + st.skipLoad(`^stRevertTest/RevertInCreateInInit\.json`) + st.skipLoad(`^stExtCodeHash/dynamicAccountOverwriteEmpty\.json`) + st.skipLoad(`^stCreate2/create2collisionStorage\.json`) + st.skipLoad(`^stCreate2/RevertInCreateInInitCreate2\.json`) + // Broken tests: // EOF is not part of cancun st.skipLoad(`^stEOF/`) + + // The tests under Pyspecs are the ones that are published as execution-spec tests. + // We run these tests separately, no need to _also_ run them as part of the + // reference tests. + st.skipLoad(`^Pyspecs/`) } func TestState(t *testing.T) {