core/state, core/types/bal: copy stateReadList in StateDB.Copy

The stateReadList field introduced by #34776 to track the state
access footprint for EIP-7928 was not propagated by StateDB.Copy.
Every other per-transaction field that lives alongside it
(accessList, transientStorage, journal, witness, accessEvents) is
copied explicitly, so this field was simply missed.

After Copy the copy's stateReadList is nil while the original keeps
its entries, so the nil-safe guards on StateAccessList.AddAccount /
AddState silently drop every access recorded on the copy. For any
post-Amsterdam code path that copies a prepared state and keeps
reading from the copy, the BAL footprint becomes incomplete.

Add a Copy method on bal.StateAccessList and invoke it from
StateDB.Copy, matching the pattern used for accessList and
accessEvents.
This commit is contained in:
rayoo 2026-04-24 17:51:01 +08:00
parent 6ece4cd143
commit b68ec987ce
2 changed files with 17 additions and 0 deletions

View file

@ -716,6 +716,9 @@ func (s *StateDB) Copy() *StateDB {
if s.accessEvents != nil {
state.accessEvents = s.accessEvents.Copy()
}
if s.stateReadList != nil {
state.stateReadList = s.stateReadList.Copy()
}
// Deep copy cached state objects.
for addr, obj := range s.stateObjects {
state.stateObjects[addr] = obj.deepCopy(state)

View file

@ -78,3 +78,17 @@ func (s *StateAccessList) Merge(other *StateAccessList) {
maps.Copy(slots, otherSlots)
}
}
// Copy returns a deep copy of the StateAccessList. A nil receiver copies to nil.
func (s *StateAccessList) Copy() *StateAccessList {
if s == nil {
return nil
}
cpy := &StateAccessList{
list: make(map[common.Address]StorageAccessList, len(s.list)),
}
for addr, slots := range s.list {
cpy.list[addr] = maps.Clone(slots)
}
return cpy
}