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

@ -39,16 +39,17 @@ func NewBlockAccessListTracer(startIdx int) (*BlockAccessListTracer, *tracing.Ho
accessListBuilder: bal.NewAccessListBuilder(),
}
hooks := &tracing.Hooks{
OnTxEnd: balTracer.TxEndHook,
OnTxStart: balTracer.TxStartHook,
OnEnter: balTracer.OnEnter,
OnExit: balTracer.OnExit,
OnCodeChangeV2: balTracer.OnCodeChange,
OnBalanceChange: balTracer.OnBalanceChange,
OnNonceChangeV2: balTracer.OnNonceChange,
OnStorageChange: balTracer.OnStorageChange,
OnColdAccountRead: balTracer.OnColdAccountRead,
OnColdStorageRead: balTracer.OnColdStorageRead,
OnTxEnd: balTracer.TxEndHook,
OnTxStart: balTracer.TxStartHook,
OnEnter: balTracer.OnEnter,
OnExit: balTracer.OnExit,
OnCodeChangeV2: balTracer.OnCodeChange,
OnBalanceChange: balTracer.OnBalanceChange,
OnNonceChangeV2: balTracer.OnNonceChange,
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)
@ -217,13 +219,14 @@ type Hooks struct {
OnSystemCallEnd OnSystemCallEndHook
// State events
OnBalanceChange BalanceChangeHook
OnNonceChange NonceChangeHook
OnNonceChangeV2 NonceChangeHookV2
OnCodeChange CodeChangeHook
OnCodeChangeV2 CodeChangeHookV2
OnStorageChange StorageChangeHook
OnLog LogHook
OnBalanceChange BalanceChangeHook
OnNonceChange NonceChangeHook
OnNonceChangeV2 NonceChangeHookV2
OnCodeChange CodeChangeHook
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