diff --git a/core/state/intra_block_state.go b/core/state/intra_block_state.go index e331b657a71..68225e687a1 100644 --- a/core/state/intra_block_state.go +++ b/core/state/intra_block_state.go @@ -288,7 +288,7 @@ func (sdb *IntraBlockState) HasSelfdestructed(addr libcommon.Address) bool { if stateObject.deleted { return false } - if stateObject.created { + if stateObject.createdContract { return false } return stateObject.selfdestructed @@ -415,12 +415,23 @@ func (sdb *IntraBlockState) Selfdestruct(addr libcommon.Address) bool { prevbalance: *stateObject.Balance(), }) stateObject.markSelfdestructed() - stateObject.created = false + stateObject.createdContract = false stateObject.data.Balance.Clear() return true } +func (sdb *IntraBlockState) Selfdestruct6780(addr libcommon.Address) { + stateObject := sdb.getStateObject(addr) + if stateObject == nil { + return + } + + if stateObject.newlyCreated { + sdb.Selfdestruct(addr) + } +} + // SetTransientState sets transient storage for a given account. It // adds the change to the journal so that it can be rolled back // to its previous value if there is a revert. @@ -518,6 +529,7 @@ func (sdb *IntraBlockState) createObject(addr libcommon.Address, previous *state } else { sdb.journal.append(resetObjectChange{account: &addr, prev: previous}) } + newobj.newlyCreated = true sdb.setStateObject(addr, newobj) return newobj } @@ -554,7 +566,7 @@ func (sdb *IntraBlockState) CreateAccount(addr libcommon.Address, contractCreati newObj.data.Initialised = true if contractCreation { - newObj.created = true + newObj.createdContract = true newObj.data.Incarnation = prevInc + 1 } else { newObj.selfdestructed = false @@ -598,7 +610,7 @@ func updateAccount(EIP161Enabled bool, isAura bool, stateWriter StateWriter, add } stateObject.deleted = true } - if isDirty && (stateObject.created || !stateObject.selfdestructed) && !emptyRemoval { + if isDirty && (stateObject.createdContract || !stateObject.selfdestructed) && !emptyRemoval { stateObject.deleted = false // Write any contract code associated with the state object if stateObject.code != nil && stateObject.dirtyCode { @@ -606,7 +618,7 @@ func updateAccount(EIP161Enabled bool, isAura bool, stateWriter StateWriter, add return err } } - if stateObject.created { + if stateObject.createdContract { if err := stateWriter.CreateContract(addr); err != nil { return err } @@ -626,12 +638,12 @@ func printAccount(EIP161Enabled bool, addr libcommon.Address, stateObject *state if stateObject.selfdestructed || (isDirty && emptyRemoval) { fmt.Printf("delete: %x\n", addr) } - if isDirty && (stateObject.created || !stateObject.selfdestructed) && !emptyRemoval { + if isDirty && (stateObject.createdContract || !stateObject.selfdestructed) && !emptyRemoval { // Write any contract code associated with the state object if stateObject.code != nil && stateObject.dirtyCode { fmt.Printf("UpdateCode: %x,%x\n", addr, stateObject.CodeHash()) } - if stateObject.created { + if stateObject.createdContract { fmt.Printf("CreateContract: %x\n", addr) } stateObject.printTrie() @@ -666,7 +678,7 @@ func (sdb *IntraBlockState) FinalizeTx(chainRules *chain.Rules, stateWriter Stat if err := updateAccount(chainRules.IsSpuriousDragon, chainRules.IsAura, stateWriter, addr, so, true); err != nil { return err } - + so.newlyCreated = false sdb.stateObjectsDirty[addr] = struct{}{} } // Invalidate journal because reverting across transactions is not allowed. diff --git a/core/state/state_object.go b/core/state/state_object.go index e4302856ff1..9d681b6749e 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -85,10 +85,11 @@ type stateObject struct { // Cache flags. // When an object is marked selfdestructed it will be delete from the trie // during the "update" phase of the state transition. - dirtyCode bool // true if the code was updated - selfdestructed bool - deleted bool // true if account was deleted during the lifetime of this object - created bool // true if this object represents a newly created contract + dirtyCode bool // true if the code was updated + selfdestructed bool + deleted bool // true if account was deleted during the lifetime of this object + newlyCreated bool // true if this object was created in the current transaction + createdContract bool // true if this object represents a newly created contract } // empty returns whether the account is considered empty. @@ -179,7 +180,7 @@ func (so *stateObject) GetCommittedState(key *libcommon.Hash, out *uint256.Int) return } } - if so.created { + if so.createdContract { out.Clear() return } diff --git a/core/vm/eips.go b/core/vm/eips.go index f1b1fb2bf5b..4b2a825f40f 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -28,6 +28,7 @@ import ( ) var activators = map[int]func(*JumpTable){ + 6780: enable6780, 5656: enable5656, 4844: enable4844, 3860: enable3860, @@ -291,3 +292,14 @@ func opMcopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by scope.Memory.Copy(dst.Uint64(), src.Uint64(), length.Uint64()) return nil, nil } + +// enable6780 applies EIP-6780 (deactivate SELFDESTRUCT) +func enable6780(jt *JumpTable) { + jt[SELFDESTRUCT] = &operation{ + execute: opSelfdestruct6780, + dynamicGas: gasSelfdestructEIP3529, + constantGas: params.SelfdestructGasEIP150, + numPop: 1, + numPush: 0, + } +} diff --git a/core/vm/evmtypes/evmtypes.go b/core/vm/evmtypes/evmtypes.go index a94e502789a..ac2012a8158 100644 --- a/core/vm/evmtypes/evmtypes.go +++ b/core/vm/evmtypes/evmtypes.go @@ -84,6 +84,7 @@ type IntraBlockState interface { Selfdestruct(common.Address) bool HasSelfdestructed(common.Address) bool + Selfdestruct6780(common.Address) // Exist reports whether the given account exists in state. // Notably this should also return true for suicided accounts. diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 691620a4b5a..fa6d937a605 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -885,6 +885,26 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext return nil, errStopToken } +func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } + beneficiary := scope.Stack.Pop() + callerAddr := scope.Contract.Address() + beneficiaryAddr := libcommon.Address(beneficiary.Bytes20()) + balance := interpreter.evm.IntraBlockState().GetBalance(callerAddr) + if interpreter.evm.Config().Debug { + if interpreter.cfg.Debug { + interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, callerAddr, beneficiaryAddr, false /* precompile */, false /* create */, []byte{}, 0, balance, nil /* code */) + interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil) + } + } + interpreter.evm.IntraBlockState().SubBalance(callerAddr, balance) + interpreter.evm.IntraBlockState().AddBalance(beneficiaryAddr, balance) + interpreter.evm.IntraBlockState().Selfdestruct6780(callerAddr) + return nil, errStopToken +} + // following functions are used by the instruction jump table // make log instruction function diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 5e2350ceb28..67e636f14f2 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -103,6 +103,7 @@ func newCancunInstructionSet() JumpTable { enable1153(&instructionSet) // Transient storage opcodes enable4844(&instructionSet) // BLOBHASH opcode enable5656(&instructionSet) // MCOPY opcode + enable6780(&instructionSet) // SELFDESTRUCT only in same transaction validateAndFillMaxStack(&instructionSet) return instructionSet }