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.

---------

Co-authored-by: jwasinger <j-wasinger@hotmail.com>
This commit is contained in:
rayoo 2026-04-24 23:30:03 +08:00 committed by MariusVanDerWijden
parent a262af0430
commit decfab7f6f
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.
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
}