erigontech / erigontech/erigon

Parallel-exec: route the per-tx writeset through one faithful path; remove normalizeWriteSet/calcState; carry serial-finalize signals on ExecutionResult

Open
#21,138 14 comments 0 reactions 1 assignee Claimed by @mh0lt View on GitHub
tech debt reduction
Dominant language
Go
Stars
3.6k
Forks
1.5k
Avg merge
1d 16h
Merged PRs (30d)
455

Description

Follow-up to #21136. The recurring parallel-exec-vs-serial divergences (SELFDESTRUCT/recreate, EIP-161 empty-removal, EIP-7708 burn logs, fork-transition, coinbase) all come from the post-execution wrapper *re-deriving* "what did this tx do" via the heuristic `normalizeWriteSet(versionWritten-log)` path and the `calcState` re-derivation, instead of using the faithful `MakeWriteSet`-style output that the validated worker already produces. ~half of genuine parallel-executor PRs over the last 2 months are this family. This issue is the consolidation that makes those bugs structurally impossible.

## Target per-tx flow (incremental — does NOT require the full "IBS = pure transaction-state" rework)

1. **Parallel-execute** txs (unchanged).
2. **Validate + serialize** via BlockSTM (unchanged) — at this point we have a verified version. The validated worker's IBS already ran `MakeWriteSet` into a `LightCollector` → `result.CollectorWrites` = every dirty object's *final* state.
3. **Per-tx serial-finalize** — done from **data + the writeset**, no reconstructed IBS:
- per-tx delta = `filterWritesByVersionMap(result.CollectorWrites, vmWrites)` (faithful, pruned to the keys the tx actually touched — both `filterWritesByVersionMap` and `CollectorWrites` already exist).
- accumulate tip serially → final coinbase / burnt balances → patch the writeset (read pre-block balance from `sd`).
- EIP-161 empty-removal: a writeset entry that's {bal=0, nonce=0, empty codehash} → convert to a delete.
- EIP-7708 transfer/burn logs: compute from the writeset + a self-destructed-addresses list carried on `ExecutionResult` (see below) — **delete the `minIBS` + `postApplyMessageFunc` reconstruction** (`exec3_parallel.go` ~line 2807; the `minIBS` exists *only* to `SetBalance` then `LogSelfDestructedAccounts` reads it).
- SD storage cascade if still needed (or it falls out of the collector's final state).
4. **Pass the finished tx output to the commitment calculator**: the one writeset → `applyVersionedWrites` writes it into `SharedDomains`/`sd.mem` (the public DB the calc reads from), AND `VersionedWrites.TouchUpdates(updates)` folds it into the calc's `commitment.Updates`. Calc reads post-block siblings from `sd.mem` via `asOfStateReader`.

Block-end finalize (block reward, withdrawals, EIP-4788/7002/7251 syscalls) genuinely runs state transitions and keeps a real IBS — it's outside the per-tx flow and not in scope here.

## Concrete changes

- `ExecutionResult` gains `SelfDestructedAddresses []common.Address` (regardless of residual balance) — populated by the worker; consumed by per-tx serial-finalize for EIP-7708 and (potentially) the SD storage cascade.
- `txResult.writes = filterWritesByVersionMap(result.CollectorWrites, be.blockIO.WriteSet(txIndex))` instead of `normalizeWriteSet(...)`.
- the commitment calc consumes `txResult.writes.TouchUpdates(...)` — delete `calcState.ApplyWrites`/`FlushToUpdates`.
- delete `normalizeWriteSet` and `resolveStorageWrites` (the latter is already DEPRECATED dead code).
- unify the per-tx and block-finalize collector paths (`LightCollector` vs `versionedWriteCollector` — one of them).
- `IBS.VersionedWrites(checkDirty)` stays, used **only** for BlockSTM read/write dependency tracking — it must stop being the apply/commitment payload.

## Validation gate before deleting anything

Remove the `EXEC3_PARALLEL`-gated `SkipLoad`/`t.Skip`s from #21136 one by one — if the consolidated path is faithful, `test_double_kill`, `test_dynamic_create2_selfdestruct_collision`, the prague `test_system_contract_deployment` variants, `tipInsideBlock`, and `TestEIP7708BurnLogWhenCoinbaseSelfDestructs` should all pass without per-bug heuristics. That's the success criterion.

## Eventual rework (separate, larger)

Extract the whole serial flow (`MakeWriteSet` → commitment fold → domain write → BAL) to run post-validation as the single path; serial becomes a 1-tx-at-a-time consumer of it; IBS becomes a pure per-tx transaction-state. This issue is the stepping stone.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.