Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,13 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
return false
}

// ExistBeforeCurTx returns true if a contract exists and was not created
// in the current transaction.
func (s *StateDB) ExistBeforeCurTx(addr common.Address) bool {
obj := s.getStateObject(addr)
return obj != nil && !obj.newContract
}

/*
* SETTERS
*/
Expand Down
77 changes: 30 additions & 47 deletions core/state/statedb_hooked.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,55 +216,21 @@ func (s *hookedStateDB) SetState(address common.Address, key common.Hash, value
}

func (s *hookedStateDB) SelfDestruct(address common.Address) uint256.Int {
var prevCode []byte
var prevCodeHash common.Hash

if s.hooks.OnCodeChange != nil {
prevCode = s.inner.GetCode(address)
prevCodeHash = s.inner.GetCodeHash(address)
}

prev := s.inner.SelfDestruct(address)

if s.hooks.OnBalanceChange != nil && !prev.IsZero() {
s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct)
}

if len(prevCode) > 0 {
if s.hooks.OnCodeChangeV2 != nil {
s.hooks.OnCodeChangeV2(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil, tracing.CodeChangeSelfDestruct)
} else if s.hooks.OnCodeChange != nil {
s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil)
}
}

return prev
}

func (s *hookedStateDB) SelfDestruct6780(address common.Address) (uint256.Int, bool) {
var prevCode []byte
var prevCodeHash common.Hash

if s.hooks.OnCodeChange != nil {
prevCodeHash = s.inner.GetCodeHash(address)
prevCode = s.inner.GetCode(address)
}

prev, changed := s.inner.SelfDestruct6780(address)

if s.hooks.OnBalanceChange != nil && !prev.IsZero() {
s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct)
}

if changed && len(prevCode) > 0 {
if s.hooks.OnCodeChangeV2 != nil {
s.hooks.OnCodeChangeV2(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil, tracing.CodeChangeSelfDestruct)
} else if s.hooks.OnCodeChange != nil {
s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil)
}
}
return s.inner.SelfDestruct6780(address)
}

return prev, changed
func (s *hookedStateDB) ExistBeforeCurTx(address common.Address) bool {
return s.inner.ExistBeforeCurTx(address)
}

func (s *hookedStateDB) AddLog(log *types.Log) {
Expand All @@ -277,15 +243,32 @@ func (s *hookedStateDB) AddLog(log *types.Log) {

func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) {
defer s.inner.Finalise(deleteEmptyObjects)
if s.hooks.OnBalanceChange == nil {
return
}
for addr := range s.inner.journal.dirties {
obj := s.inner.stateObjects[addr]
if obj != nil && obj.selfDestructed {
// If ether was sent to account post-selfdestruct it is burnt.
if bal := obj.Balance(); bal.Sign() != 0 {
s.hooks.OnBalanceChange(addr, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn)
if s.hooks.OnBalanceChange != nil || s.hooks.OnNonceChangeV2 != nil || s.hooks.OnCodeChangeV2 != nil || s.hooks.OnCodeChange != nil {
for addr := range s.inner.journal.dirties {
obj := s.inner.stateObjects[addr]
if obj != nil && obj.selfDestructed {
// If ether was sent to account post-selfdestruct it is burnt.
if s.hooks.OnBalanceChange != nil {
if bal := obj.Balance(); bal.Sign() != 0 {
s.hooks.OnBalanceChange(addr, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn)
}
}
if s.hooks.OnNonceChangeV2 != nil {
prevNonce := obj.Nonce()
s.hooks.OnNonceChangeV2(addr, prevNonce, 0, tracing.NonceChangeSelfdestruct)
}
prevCodeHash := s.inner.GetCodeHash(addr)
prevCode := s.inner.GetCode(addr)

// if an initcode invokes selfdestruct, do not emit a code change.
if prevCodeHash == types.EmptyCodeHash {
continue
}
if s.hooks.OnCodeChangeV2 != nil {
s.hooks.OnCodeChangeV2(addr, prevCodeHash, prevCode, types.EmptyCodeHash, nil, tracing.CodeChangeSelfDestruct)
} else if s.hooks.OnCodeChange != nil {
s.hooks.OnCodeChange(addr, prevCodeHash, prevCode, types.EmptyCodeHash, nil)
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions core/tracing/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,9 @@ const (
// NonceChangeRevert is emitted when the nonce is reverted back to a previous value due to call failure.
// It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal).
NonceChangeRevert NonceChangeReason = 6

// NonceChangeSelfdestruct is emitted when the nonce is reset to zero due to a self-destruct
NonceChangeSelfdestruct NonceChangeReason = 7
)

// CodeChangeReason is used to indicate the reason for a code change.
Expand Down
17 changes: 15 additions & 2 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -906,8 +906,21 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro
}
beneficiary := scope.Stack.pop()
balance := evm.StateDB.GetBalance(scope.Contract.Address())
evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct)
evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct)
createdInTx := !evm.StateDB.ExistBeforeCurTx(scope.Contract.Address())

if createdInTx {
// if the contract is not preexisting, the balance is immediately burned on selfdestruct-to-self
evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct)
if scope.Contract.Address() != common.BytesToAddress(beneficiary.Bytes()) {
evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct)
}
} else {
// if the contract is preexisting, the balance isn't burned on selfdestruct-to-self
if scope.Contract.Address() != common.BytesToAddress(beneficiary.Bytes()) {
evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct)
evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct)
}
}
evm.StateDB.SelfDestruct6780(scope.Contract.Address())
if tracer := evm.Config.Tracer; tracer != nil {
if tracer.OnEnter != nil {
Expand Down
3 changes: 3 additions & 0 deletions core/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ type StateDB interface {
// Exist reports whether the given account exists in state.
// Notably this also returns true for self-destructed accounts within the current transaction.
Exist(common.Address) bool
// ExistBeforeCurTx returns true if a contract exists and was not created
// in the current transaction.
ExistBeforeCurTx(addr common.Address) bool
// Empty returns whether the given account is empty. Empty
// is defined according to EIP161 (balance = nonce = code = 0).
Empty(common.Address) bool
Expand Down