Summary
normalizeWriteSet (execution/stagedsync/exec3_parallel.go) makes seven separate passes over the write-set collections, including two full writes.AllHeaders() walks. It should be rationalized to fewer passes.
The passes today
writes.SelfDestructs() — build the self-destruct set
writes.AllHeaders() — main filter loop
writes.AllHeaders() — again, only to collect allAddresses
filtered.AllHeaders() — build addrFields (which fields are already present)
allAddresses — account-field backfill (version map → state reader)
filtered.Codes() — codeInOutput
filtered.CodeHashes() — codeHashInOutput (CodePath-travels-with-CodeHash recovery)
The two writes.AllHeaders() passes (2 and 3) are plainly redundant — allAddresses can be gathered during pass 2. Passes 4/6/7 over filtered can largely fold into the backfill.
Why it's non-trivial (and gated on tests)
The main loop is ordering- and state-dependent: the self-destruct storage-delete cascade, per-slot no-op filtering (version-map floor vs SD-zero baseline vs state-reader pre-block vs SD history scan), account-field resolution, EIP-161 empty-account removal, and the 7702 CodePath recovery all interact. It's a consensus-critical path (a mistake is a wrong trie root), so it needs a full characterization-test net before refactoring.
Test net (in place)
Existing unit coverage in exec3_finalize_test.go / calc_state_test.go plus the branches added in PR #21536 (1c6c29e7e0):
- metamorphic same-tx SELFDESTRUCT-then-CREATE2 keeps recreate writes
- stale (non-validated) incarnation SelfDestructPath ignored
- post-self-destruct zero storage write dropped via the SD history scan
- CREATE2-after-SD zeroes missing fields (not stale pre-SD values); value-transfer resurrect inherits them
- storage write-back of the pre-block value dropped via the state reader
These plus the end-to-end execmodule reorg/SD tests (TestDeleteRecreateSlots*, TestSelfDestructReceive, TestCVE2020_26265, TestRecreateAndRewind) should stay green across the refactor.
Scope
Pure refactor — no behavior change. Fold the redundant passes, keep every listed test green. Split out from PR #21536 (typed-vio) so that PR stays scoped to correctness + the safe hot-path wins.
Summary
normalizeWriteSet(execution/stagedsync/exec3_parallel.go) makes seven separate passes over the write-set collections, including two fullwrites.AllHeaders()walks. It should be rationalized to fewer passes.The passes today
writes.SelfDestructs()— build the self-destruct setwrites.AllHeaders()— main filter loopwrites.AllHeaders()— again, only to collectallAddressesfiltered.AllHeaders()— buildaddrFields(which fields are already present)allAddresses— account-field backfill (version map → state reader)filtered.Codes()—codeInOutputfiltered.CodeHashes()—codeHashInOutput(CodePath-travels-with-CodeHash recovery)The two
writes.AllHeaders()passes (2 and 3) are plainly redundant —allAddressescan be gathered during pass 2. Passes 4/6/7 overfilteredcan largely fold into the backfill.Why it's non-trivial (and gated on tests)
The main loop is ordering- and state-dependent: the self-destruct storage-delete cascade, per-slot no-op filtering (version-map floor vs SD-zero baseline vs state-reader pre-block vs SD history scan), account-field resolution, EIP-161 empty-account removal, and the 7702 CodePath recovery all interact. It's a consensus-critical path (a mistake is a wrong trie root), so it needs a full characterization-test net before refactoring.
Test net (in place)
Existing unit coverage in
exec3_finalize_test.go/calc_state_test.goplus the branches added in PR #21536 (1c6c29e7e0):These plus the end-to-end
execmodulereorg/SD tests (TestDeleteRecreateSlots*,TestSelfDestructReceive,TestCVE2020_26265,TestRecreateAndRewind) should stay green across the refactor.Scope
Pure refactor — no behavior change. Fold the redundant passes, keep every listed test green. Split out from PR #21536 (typed-vio) so that PR stays scoped to correctness + the safe hot-path wins.