Skip to content

Commit

Permalink
Implement EIP-6780: SELFDESTRUCT only in same transaction (#7976)
Browse files Browse the repository at this point in the history
Cherry pick ethereum/go-ethereum#27189

---------

Co-authored-by: jwasinger <j-wasinger@hotmail.com>
  • Loading branch information
2 people authored and AskAlexSharov committed Sep 6, 2023
1 parent de95211 commit 48d805a
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 13 deletions.
28 changes: 20 additions & 8 deletions core/state/intra_block_state.go
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -598,15 +610,15 @@ 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 {
if err := stateWriter.UpdateAccountCode(addr, stateObject.data.Incarnation, stateObject.data.CodeHash, stateObject.code); err != nil {
return err
}
}
if stateObject.created {
if stateObject.createdContract {
if err := stateWriter.CreateContract(addr); err != nil {
return err
}
Expand All @@ -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()
Expand Down Expand Up @@ -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.
Expand Down
11 changes: 6 additions & 5 deletions core/state/state_object.go
Expand Up @@ -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.
Expand Down Expand Up @@ -179,7 +180,7 @@ func (so *stateObject) GetCommittedState(key *libcommon.Hash, out *uint256.Int)
return
}
}
if so.created {
if so.createdContract {
out.Clear()
return
}
Expand Down
12 changes: 12 additions & 0 deletions core/vm/eips.go
Expand Up @@ -28,6 +28,7 @@ import (
)

var activators = map[int]func(*JumpTable){
6780: enable6780,
5656: enable5656,
4844: enable4844,
3860: enable3860,
Expand Down Expand Up @@ -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,
}
}
1 change: 1 addition & 0 deletions core/vm/evmtypes/evmtypes.go
Expand Up @@ -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.
Expand Down
20 changes: 20 additions & 0 deletions core/vm/instructions.go
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions core/vm/jump_table.go
Expand Up @@ -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
}
Expand Down

0 comments on commit 48d805a

Please sign in to comment.