Skip to content

Design: TransactionState/BlockState separation, TxTask refactoring, cross-process TX caching #19855

Description

@mh0lt

Context

Follow-on design from #19623 (2-cache IBS refactor). While #19623 focuses on eliminating stateObject and the IBS round-trip within the current architecture, this issue captures the higher-level ownership restructuring and cross-process optimization opportunities that should inform the implementation to avoid costly future refactors.

These observations emerged during PR #19814 (direct finalize path + unit tests), where the brittleness of IBS's multiple implicit roles became apparent.

Problem

IntraBlockState serves multiple implicit roles:

  1. Execution context — per-TX state objects, journal, snapshots
  2. Version tracking — versioned reads/writes for parallel validation
  3. Finalize orchestrator — FinalizeTx / CommitBlock / MakeWriteSet
  4. BAL read generation — refreshVersionedAccount for EIP-7928

The FinalizeTx vs CommitBlock distinction is extremely subtle:

  • FinalizeTx: useBlockOrigin=false (originStorage — last written value)
  • CommitBlock: useBlockOrigin=true (blockOriginStorage — block-start value)

This exists because originStorage gets updated by each FinalizeTx, so by CommitBlock time it equals the dirty value.

1. TransactionState / BlockState Separation

TransactionState (per-TX, request-scoped)

Key design decision: TransactionState should NOT be owned by the worker and reset between TXs. It should be part of the request — a value object with immutable inputs and value-type outputs. This avoids:

  • Worker lifecycle bleeding into state lifecycle
  • Hidden state leaks between TXs (today's IBS.Reset() is fragile)
  • Coupling that prevents caching

The execution model becomes: (TransactionState, inputs) → (TransactionState, outputs)

Contains:

  • Account state objects (balance, nonce, code, storage)
  • Journal for revert snapshots
  • Dirty tracking
  • No version map, no BAL, no block-level concerns

BlockState (per-block)

Contains:

  • Version map (parallel execution)
  • Block-origin storage values
  • Accumulated block IO (reads/writes per TX)
  • BAL computation state
  • Fee-calc coordination (coinbase/burnt deltas)
  • Combine(txResult) merges TX results into block state

2. TxTask Refactoring

TxTask has grown organically serving multiple scenarios:

  • Historical queries (simpler, no dependencies)
  • Parallel speculative execution (dependencies, delayed fee calc)
  • RPC calls (eth_call, eth_estimateGas)
  • AA transaction batches

The flags shouldDelayFeeCalc, HistoryExecution, InBatch, AAValidationBatchSize are symptoms of one object doing too many jobs.

TxTask should be split into:

  • TxInput: immutable execution request (tx, header, config, gas pool)
  • TxScheduling: parallel-specific (dependencies, incarnation, version)
  • TxResult: execution output (state changes, receipt, logs, errors)

3. Cross-Process TX Caching

TX execution happens in multiple places in Erigon:

  • Block execution (serial + parallel)
  • Block building (miner/proposer)
  • TxPool validation (and future expanded validation)
  • RPC (eth_call, eth_estimateGas, trace_*)

If TransactionState is a pure function of inputs, the cache key becomes:

hash(tx_bytes, account_prestate_at_block) → TxResult

This enables:

  • TxPool → Block Building: skip re-execution of already-validated TXs
  • RPC → Recent blocks: cache recent eth_call results
  • Parallel execution: cache results across incarnations when prestate matches

4. BAL Read Generation Decoupling

Currently tightly coupled to IBS via refreshVersionedAccount. Must be decoupled so that:

  • The direct finalize path (finalizeTx) can generate correct BAL reads without IBS reconstruction
  • BAL reads become a pure function of (TxOut addresses, prestate)

Implementation Sequencing

These changes build on #19623's 5-phase plan and extend it:

Phase Description Dependency
#19623 Phases 1-5 2-cache model, eliminate stateObject Foundation
Fix finalizeTx BAL reads Direct finalize generates correct BalancePath reads #19623 Phase 2
Extract BlockState Move version map, block IO, BAL state out of IBS #19623 Phase 5
Request-scoped TransactionState Per-TX creation, pure function model, TxTask split BlockState extraction
Cross-process caching TX result cache shared across execution contexts TransactionState + serial exec deprecation

Key Files

  • execution/stagedsync/exec3_parallel.go — finalize dispatch, finalizeTx, finalizeWithIBS
  • execution/stagedsync/exec3_finalize_test.go — comparison unit tests (PR exec3_parallel: eliminate IBS round-trip in finalize path #19814)
  • execution/state/intra_block_state.go — IBS (the monolith)
  • execution/state/versionedio.go — version map, BAL computation
  • execution/state/state_object.go — account state, updateStorage
  • execution/exec/txtask.go — TxTask (the multi-purpose envelope)

Notes

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions