diff --git a/core/state/journal.go b/core/state/journal.go index 89f0cd6ce0..3cc54b93eb 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -105,6 +105,11 @@ type ( createObjectChange struct { account *common.Address } + + createContractChange struct { + account common.Address + } + selfDestructChange struct { account *common.Address prev bool // whether account had already self-destructed @@ -173,6 +178,20 @@ func (ch createObjectChange) copy() journalEntry { } } +func (ch createContractChange) revert(s *StateDB) { + s.stateObjects[ch.account].created = false +} + +func (ch createContractChange) dirtied() *common.Address { + return &ch.account +} + +func (ch createContractChange) copy() journalEntry { + return createContractChange{ + account: ch.account, + } +} + func (ch selfDestructChange) revert(s *StateDB) { obj := s.getStateObject(*ch.account) if obj != nil { diff --git a/core/state/statedb.go b/core/state/statedb.go index be2b23a31f..bb67c69f6d 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -662,6 +662,19 @@ func (s *StateDB) CreateAccount(addr common.Address) { s.createObject(addr) } +// CreateContract is used whenever a contract is created. This may be preceded +// by CreateAccount, but that is not required if it already existed +// in the state due to funds sent beforehand. +// This operation sets the 'created'-flag, which is required in order to +// correctly handle EIP-6780 'delete-in-same-transaction' logic. +func (s *StateDB) CreateContract(addr common.Address) { + obj := s.getStateObject(addr) + if !obj.created { + obj.created = true + s.journal.append(createContractChange{account: addr}) + } +} + // Copy creates a deep, independent copy of the state. // Snapshots of the copied state cannot be applied to the copy. func (s *StateDB) Copy() *StateDB { diff --git a/core/vm/evm.go b/core/vm/evm.go index 07cb8f51bb..e6e9cc64c3 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -462,6 +462,12 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if !evm.StateDB.Exist(address) { evm.StateDB.CreateAccount(address) } + // CreateContract means that regardless of whether the acccount existed + // in the state trie or not, previously, it _now_ becomes created as a + // _contract_ account. This is performed _prior_ to executing the initcode, + // since the initcode acts inside that account. + evm.StateDB.CreateContract(address) + if evm.chainRules.IsEIP158 { evm.StateDB.SetNonce(address, 1) } diff --git a/core/vm/interface.go b/core/vm/interface.go index 30742e96de..774360a08e 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -29,6 +29,7 @@ import ( // StateDB is an EVM database for full state querying. type StateDB interface { CreateAccount(common.Address) + CreateContract(common.Address) SubBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) AddBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason)