From f196057728ee24f2c5f97e70ff24c2e7a7f81df1 Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Thu, 23 Apr 2026 15:54:10 +0800 Subject: [PATCH] core/state: track the block-level accessList --- core/state/state_object.go | 7 +++++++ core/state/statedb.go | 39 +++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/core/state/state_object.go b/core/state/state_object.go index db03c09e41..7c4ee9e427 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -274,6 +274,13 @@ func (s *stateObject) finalise() { // map as the dirty slot might have been committed already (before the // byzantium fork) and entry is necessary to modify the value back. s.pendingStorage[key] = value + + // Aggregate storage writes into the block-level access list. + // All slots in the dirtyStorage set must have post-transaction + // values that differ from their pre-transaction values. + if s.db.stateAccessList != nil { + s.db.stateAccessList.StorageWrite(uint16(s.db.txIndex+1), s.address, key, value) + } } if s.db.prefetcher != nil && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash { if err := s.db.prefetcher.prefetch(s.addrHash(), s.data.Root, s.address, nil, slotsToPrefetch, false); err != nil { diff --git a/core/state/statedb.go b/core/state/statedb.go index aacd926ba4..d9f3ad86f2 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -18,6 +18,7 @@ package state import ( + "bytes" "errors" "fmt" "maps" @@ -801,7 +802,7 @@ func (s *StateDB) LogsForBurnAccounts() []*types.Log { // into the tries just yet. Only IntermediateRoot or Commit will do that. func (s *StateDB) Finalise(deleteEmptyObjects bool) *bal.ConstructionBlockAccessList { addressesToPrefetch := make([]common.Address, 0, len(s.journal.mutations)) - for addr := range s.journal.mutations { + for addr, state := range s.journal.mutations { obj, exist := s.stateObjects[addr] if !exist { // RIPEMD160 (0x03) gets an extra dirty marker for a historical @@ -822,7 +823,43 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) *bal.ConstructionBlockAccess if _, ok := s.stateObjectsDestruct[obj.address]; !ok { s.stateObjectsDestruct[obj.address] = obj } + // Aggregate the account mutation into the block-level accessList + // if Amsterdam has been activated. + if s.stateAccessList != nil { + // Notably, if the account is deleted during the transaction, + // its pre-transaction nonce, code, and storage must be empty. + // + // EIP-6780 restricts self-destruct to contracts deployed within + // the same transaction, while EIP-7610 rejects deployments to + // destinations with non-empty storage, non-zero nonce and non-empty + // code. + // + // Therefore, when an account is deleted, its pre-transaction nonce + // code and storage is guaranteed to be empty, leaving nothing to + // clean up here. + balance := uint256.NewInt(0) + if state.balanceSet && balance.Cmp(state.balance) != 0 { + s.stateAccessList.BalanceChange(uint16(s.txIndex+1), addr, balance) + } + } } else { + // Aggregate the account mutation into the block-level accessList + // if Amsterdam has been activated. + if s.stateAccessList != nil { + balance := obj.Balance() + if state.balanceSet && balance.Cmp(state.balance) != 0 { + s.stateAccessList.BalanceChange(uint16(s.txIndex+1), addr, balance) + } + nonce := obj.Nonce() + if state.nonceSet && nonce != state.nonce { + s.stateAccessList.NonceChange(addr, uint16(s.txIndex+1), nonce) + } + if state.codeSet { + if code := obj.Code(); !bytes.Equal(code, state.code) { + s.stateAccessList.CodeChange(addr, uint16(s.txIndex+1), code) + } + } + } obj.finalise() s.markUpdate(addr) }