erigontech / erigontech/erigon

Rationalize normalizeWriteSet: collapse its 7 write-set passes (behavior-preserving)

Open
#22,216 2 comments 0 reactions 0 assignees View on GitHub
tech debt reduction
Dominant language
Go
Stars
3.6k
Forks
1.5k
Avg merge
1d 16h
Merged PRs (30d)
455

Description

### 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

1. `writes.SelfDestructs()` — build the self-destruct set
2. `writes.AllHeaders()` — main filter loop
3. `writes.AllHeaders()` — **again**, only to collect `allAddresses`
4. `filtered.AllHeaders()` — build `addrFields` (which fields are already present)
5. `allAddresses` — account-field backfill (version map → state reader)
6. `filtered.Codes()` — `codeInOutput`
7. `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.

Contributor guide

Open the contributing guide

Research direction

Start with normalizeWriteSet in execution/stagedsync/exec3_parallel.go and trace the existing write-set passes and their state-dependent ordering. Run the coverage in exec3_finalize_test.go and calc_state_test.go, including the branches from PR #21536, then the execmodule reorg and self-destruct tests. Done means fewer passes with behavior unchanged and every listed test remaining green.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend, performance
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.