erigontech / erigontech/erigon
Harmonize/de-duplicate consensus admission checks: TxnExecutor.preCheck vs TxPool.validateTx
- Dominant language
- Go
- Stars
- 3.6k
- Forks
- 1.5k
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 455
Description
## Summary
The consensus-level transaction admission rules are implemented **twice**, independently:
- `TxnExecutor.preCheck` — `execution/protocol/txn_executor.go` (block execution / `evm t8n` / `trace_call`), operating on a `Message` + `IntraBlockState`.
- `TxPool.validateTx` (+ `validateBlobTxn`) — `txnprovider/txpool/pool.go:959` (pool admission), operating on a `TxnSlot` + `kvcache.CacheView`.
Both encode the same EIP rules in different shapes, so a fix in one can silently miss the other. #21907 is a concrete example: several consensus checks were reordered/added in `preCheck` (EIP-7702 SetCode prerequisites moved before the debit, EIP-3860 initcode-size moved into `preCheck`, blob-gas folded into `CheckBlockGasInclusion`, blob-fee nil-cap guard) with no corresponding review of `validateTx`.
## Duplicated consensus checks
| Rule | `preCheck` | `validateTx` |
| --- | --- | --- |
| Intrinsic gas + EIP-7623 floor, `gas < intrinsic` | clause 12 | yes (`mdgas` is shared; the *comparison* is duplicated) |
| EIP-7825 gas-limit cap (`max(regular, floor)` vs `MaxTxnGasLimit`, Amsterdam) | clause 8 | **verbatim copy** |
| EIP-3860 initcode size (`vm.CheckMaxInitCodeSize`) | clause 13 | yes |
| EIP-7702 SetCode prerequisites (pre-Prague / no-create / empty-auth) | clause 9 (`checkSetCodeAuthorizations`) | **re-implemented inline** |
| Blob fee-cap, versioned-hash, blob count | clauses 2/7 | `validateBlobTxn` |
| Balance / affordability | clauses 10–11 | `requiredBalance` |
| Nonce | clause 3 (exact + too-high/low/max) | too-low only |
| Block gas limit | clause 5 (`CheckBlockGasInclusion`) | `tx.gas > blockGasLimit` |
`mdgas.IntrinsicGas`/`CalcIntrinsicGas` are already shared. The clear next candidates to extract into shared, input-agnostic helpers are the **EIP-7825 cap**, the **intrinsic-vs-gas comparison**, and the **EIP-7702 prerequisites** — `checkSetCodeAuthorizations` already exists but is unexported in `protocol`, so `validateTx` duplicates it.
## Why this isn't a trivial merge
- Different inputs: `Message` + `IntraBlockState` vs `TxnSlot` + `kvcache.CacheView`.
- Different return contracts: `error` vs `txpoolcfg.DiscardReason`.
- `validateTx` also has pool-only **policy** checks (`MinFeeCap`/underpriced, `AccountSlots`/spammer, RIP-7560 AA validation) that must stay at the call site.
- Nonce semantics differ on purpose (the pool keeps future nonces queued).
- Error/precedence ordering was chosen independently.
## Proposed direction
Extract the **pure, state-independent** consensus checks (EIP-7825 cap, EIP-3860 initcode, EIP-7702 prerequisites, intrinsic-vs-gas) into shared helpers in a common package, returning a neutral error that each caller maps to its own result type. Keep state access (nonce/balance) and pool policy at the call sites. Goal: one source of truth per consensus admission rule so the two paths can't drift.
Follow-up to #21907.
Contributor guide
Assessment
This issue has not been assessed yet.