erigontech / erigontech/erigon

db/integrity: detect and repair stale CodeDomain entries left by EIP-7702 delegation clears

Open
#22,329 2 comments 0 reactions 2 assignees Claimed by @taratorio View on GitHub
Pectra
Dominant language
Go
Stars
3.6k
Forks
1.5k
Avg merge
1d 16h
Merged PRs (30d)
455

Description

## Background

Root cause of #22321: erigon did not delete the `CodeDomain` entry when an EIP-7702 delegation was cleared (authorization to the zero address), leaving `account.codeHash == emptyCodeHash` alongside a stale `0xef0100‖delegate` designator in `CodeDomain`.

**Writer status:** fixed on `main` / 3.6 by #21536 (guard change inside the typed-vio refactor), pinned by the regression test in #22327. **Not fixed on any released line**: the `release/3.4` and `release/3.5` backports (#22328, #22333) were closed without merging, so every 3.x release through v3.5.x — including the current stable — writes a new residue on every delegation clear it executes, until 3.6 rolls out.

The residues **already written** persist in every datadir whose delegation-clear blocks were executed by an affected build, and they are baked into published state snapshot files, so every fresh sync inherits them. A residue heals naturally only when the address receives another code write (re-delegation), which is unbounded.

Consensus and RPC are unaffected — commitment uses the account record's code hash, and EVM/RPC code reads gate on the empty code hash before consulting `CodeDomain`.

## Update after #22701

#22321 was closed by #22701, which scoped the commitment assert to code-bearing accounts (and stopped it from mutating the trie update). Two consequences for this issue:

- The QA-unblock motivation is gone: assert-enabled nodes no longer trip on this residue class, so item 3 of the original ask (heal the QA reference datadirs to green the nightly) is obsolete.
- The inconsistency is now **silent**: no code path detects it anymore. #22701 explicitly defers the data cleanup to this issue, making the integrity check + repair proposed here the only mechanism that will ever surface or remove the residues.

Remaining cost of doing nothing: stale designators permanently baked into `CodeDomain` and every future snapshot lineage (bloat inherited by all fresh syncs), a growing residue population from 3.5.x nodes until 3.6 is the deployed stable, and a standing account↔`CodeDomain` disagreement waiting for any future consumer that assumes they agree.

## Ask

1. An integrity check for the invariant: flag `CodeDomain[addr]` non-empty where the account is missing or has the empty code hash (`db/integrity` addition, runnable via `erigon seg integrity`).
2. A repair mode that deletes such entries at latest state. This is state-root-neutral: the commitment reads the code hash from the account record, not from `CodeDomain` contents.
3. Run the repair on the datadirs that produce published snapshots, so regenerated snapshots stop carrying the residues forward.

Scan cost is one pass over `CodeDomain` latest state plus an `AccountsDomain` lookup per code-bearing key; if that is too slow the residue class from this bug can be pre-filtered to 23-byte `0xef0100…` values, at the cost of missing any residues of other origins.

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.