add dedicated selfdestruct hook (TODO: this commit is bugged because a prefunded account which is the target of a create2 initcode can become empty without explicitly selfdestructing

This commit is contained in:
Jared Wasinger 2025-10-05 14:59:09 +08:00
parent cb632f0345
commit 12311ee59e
4 changed files with 39 additions and 18 deletions

View file

@ -49,6 +49,7 @@ func NewBlockAccessListTracer(startIdx int) (*BlockAccessListTracer, *tracing.Ho
OnStorageChange: balTracer.OnStorageChange,
OnColdAccountRead: balTracer.OnColdAccountRead,
OnColdStorageRead: balTracer.OnColdStorageRead,
OnSelfDestructChange: balTracer.OnSelfDestruct,
}
wrappedHooks, err := tracing.WrapWithJournal(hooks)
if err != nil {
@ -107,6 +108,10 @@ func (a *BlockAccessListTracer) OnCodeChange(addr common.Address, prevCodeHash c
a.accessListBuilder.CodeChange(addr, prevCode, code)
}
func (a *BlockAccessListTracer) OnSelfDestruct(addr common.Address) {
a.accessListBuilder.SelfDestruct(addr)
}
func (a *BlockAccessListTracer) OnBalanceChange(addr common.Address, prevBalance, newBalance *big.Int, _ tracing.BalanceChangeReason) {
newU256 := new(uint256.Int).SetBytes(newBalance.Bytes())
prevU256 := new(uint256.Int).SetBytes(prevBalance.Bytes())

View file

@ -254,10 +254,13 @@ func (s *hookedStateDB) AddLog(log *types.Log) {
func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) {
defer s.inner.Finalise(deleteEmptyObjects)
if s.hooks.OnBalanceChange != nil || s.hooks.OnNonceChangeV2 != nil || s.hooks.OnCodeChangeV2 != nil || s.hooks.OnCodeChange != nil {
if s.hooks.OnSelfDestructChange != nil || 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 s.hooks.OnSelfDestructChange != nil {
s.hooks.OnSelfDestructChange(obj.address)
}
// If ether was sent to account post-selfdestruct it is burnt.
if s.hooks.OnBalanceChange != nil {
if bal := obj.Balance(); bal.Sign() != 0 {

View file

@ -189,6 +189,8 @@ type (
// ColdAccountReadHook is called before an previously-unread account is read.
ColdAccountReadHook = func(address common.Address)
SelfDestructHook = func(address common.Address)
// LogHook is called when a log is emitted.
LogHook = func(log *types.Log)
@ -224,6 +226,7 @@ type Hooks struct {
OnCodeChangeV2 CodeChangeHookV2
OnStorageChange StorageChangeHook
OnLog LogHook
OnSelfDestructChange SelfDestructHook
//State read events
OnColdStorageRead ColdStorageReadHook
OnColdAccountRead ColdAccountReadHook

View file

@ -133,6 +133,10 @@ func (c *AccessListBuilder) CodeChange(address common.Address, prev, cur []byte)
acctAccesses.CodeChange(cur)
}
func (c *AccessListBuilder) SelfDestruct(address common.Address) {
delete(c.accessesStack[len(c.accessesStack)-1], address)
}
func (c *AccessListBuilder) NonceChange(address common.Address, prev, cur uint64) {
if _, ok := c.prestates[address]; !ok {
c.prestates[address] = &AccountState{}
@ -203,6 +207,12 @@ func (a *AccessListBuilder) FinaliseIdxChanges() (*StateDiff, StateAccesses) {
}
}
// two cases of account being removed:
// * initcode runs at address, calls SENDALL (account could be prefunded, but not pre-existing as a contract)
// - if the account makes storage mutations, we have no way to distinguish it from a regular contract that didn't selfdestruct
//
// * contract created in same tx calls SENDALL
// two scenarios where an account can become non-existent:
// account was created/deleted in the same transaction
// account only had balance set (prefunded), was target of create2 initcode which called SENDALL leaving the account empty