From 0d57b76253d252126fc4cb9d9b73bc2c48acf6c7 Mon Sep 17 00:00:00 2001 From: hyeonLewis Date: Thu, 2 Jan 2025 13:15:52 +0900 Subject: [PATCH] Bring EIP-7610 --- blockchain/state/state_object.go | 7 +++++++ blockchain/state/statedb.go | 8 ++++++++ blockchain/vm/evm.go | 11 +++++++++-- blockchain/vm/interface.go | 1 + blockchain/vm/mocks/statedb_mock.go | 14 ++++++++++++++ 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/blockchain/state/state_object.go b/blockchain/state/state_object.go index ee321cdc6..ed1ea1ea2 100644 --- a/blockchain/state/state_object.go +++ b/blockchain/state/state_object.go @@ -588,6 +588,13 @@ func (s *stateObject) Nonce() uint64 { return s.account.GetNonce() } +func (s *stateObject) Root() common.Hash { + if acc := account.GetProgramAccount(s.account); acc != nil { + return acc.GetStorageRoot().Unextend() + } + return common.Hash{} +} + // Never called, but must be present to allow stateObject to be used // as a vm.Account interface that also satisfies the vm.ContractRef // interface. Interfaces are awesome. diff --git a/blockchain/state/statedb.go b/blockchain/state/statedb.go index f91611329..7f79dbc96 100644 --- a/blockchain/state/statedb.go +++ b/blockchain/state/statedb.go @@ -291,6 +291,14 @@ func (s *StateDB) GetCode(addr common.Address) []byte { return nil } +func (s *StateDB) GetStorageRoot(addr common.Address) common.Hash { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.Root() + } + return common.Hash{} +} + func (s *StateDB) GetAccount(addr common.Address) account.Account { stateObject := s.getStateObject(addr) if stateObject != nil { diff --git a/blockchain/vm/evm.go b/blockchain/vm/evm.go index 0840a23ed..7e52a56c7 100644 --- a/blockchain/vm/evm.go +++ b/blockchain/vm/evm.go @@ -511,8 +511,13 @@ func (evm *EVM) create(caller types.ContractRef, codeAndHash *codeAndHash, gas u 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.ResolveCodeHash(address) + storageRoot := evm.StateDB.GetStorageRoot(address) // The early Kaia design tried to support the account creation with a user selected address, // so the account overwriting was restricted. @@ -520,7 +525,9 @@ func (evm *EVM) create(caller types.ContractRef, codeAndHash *codeAndHash, gas u // Kaia enables SCA overwriting over EOA like Ethereum after Shanghai compatible hardfork. // NOTE: The following code should be re-considered when Kaia enables TxTypeAccountCreation if evm.chainRules.IsShanghai { - if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { + if evm.StateDB.GetNonce(address) != 0 || + (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code + (storageRoot != (common.Hash{}) && storageRoot != types.EmptyRootHash) { // non-empty storage return nil, common.Address{}, 0, ErrContractAddressCollision } } else if evm.StateDB.Exist(address) { diff --git a/blockchain/vm/interface.go b/blockchain/vm/interface.go index abb5f4462..dca5f4a6c 100644 --- a/blockchain/vm/interface.go +++ b/blockchain/vm/interface.go @@ -53,6 +53,7 @@ type StateDB interface { SetCodeToEOA(common.Address, []byte, params.Rules) error GetCodeSize(common.Address) int GetVmVersion(common.Address) (params.VmVersion, bool) + GetStorageRoot(common.Address) common.Hash ResolveCodeHash(common.Address) common.Hash ResolveCode(common.Address) []byte diff --git a/blockchain/vm/mocks/statedb_mock.go b/blockchain/vm/mocks/statedb_mock.go index b1f884797..67f72f2cb 100644 --- a/blockchain/vm/mocks/statedb_mock.go +++ b/blockchain/vm/mocks/statedb_mock.go @@ -365,6 +365,20 @@ func (mr *MockStateDBMockRecorder) GetState(arg0, arg1 interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetState", reflect.TypeOf((*MockStateDB)(nil).GetState), arg0, arg1) } +// GetStorageRoot mocks base method. +func (m *MockStateDB) GetStorageRoot(arg0 common.Address) common.Hash { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetStorageRoot", arg0) + ret0, _ := ret[0].(common.Hash) + return ret0 +} + +// GetStorageRoot indicates an expected call of GetStorageRoot. +func (mr *MockStateDBMockRecorder) GetStorageRoot(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStorageRoot", reflect.TypeOf((*MockStateDB)(nil).GetStorageRoot), arg0) +} + // GetTransientState mocks base method. func (m *MockStateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash { m.ctrl.T.Helper()