Skip to content

feat: top-of-block pre-simulation to filter reverting tx spam#62

Closed
dmarzzz wants to merge 1 commit into
flashtestations-testsfrom
feat/presim-revert-filter
Closed

feat: top-of-block pre-simulation to filter reverting tx spam#62
dmarzzz wants to merge 1 commit into
flashtestations-testsfrom
feat/presim-revert-filter

Conversation

@dmarzzz
Copy link
Copy Markdown
Member

@dmarzzz dmarzzz commented Apr 16, 2026

Summary

  • Adds a new PreSimulateOrders pipeline step that simulates pending pool transactions at the top of each block before the flashblock building loop, removing reverting txs from the pool so they never consume building time on the critical path.
  • Uses a random coinbase during simulation (configurable) so adversaries can't detect the simulation environment via the COINBASE opcode — same anti-gaming technique as rbuilder's OrderSimulationPool.
  • Step is placed after OptimismPrologue/FlashtestationsPrologue so it sees post-sequencer state. Bundles are deferred to the existing RemoveRevertedTransactions step. EVM-level errors (wrong nonce, etc.) are kept since they may become valid later.

Why

Builder time is being burned simulating adversarial reverting txs during flashblock production. RemoveRevertedTransactions currently filters them but only after the expensive simulation has already run on the critical path. This moves the filtering to the top of the block (once per payload job), so the flashblock loop sees a pre-cleaned pool.

This is the L2 analogue of rbuilder's two-phase approach on L1: pre-sim → filter → build, with random coinbase so tx.origin == known_builder style detection doesn't work.

Configuration

Two new CLI flags (both default true):

  • --builder.presim (env PRESIM_ENABLED)
  • --builder.presim-random-coinbase (env PRESIM_RANDOM_COINBASE)

Metrics

New metrics emitted by the step:

  • txs_simulated_total, txs_dropped_total, gas_saved_total (counters)
  • txs_simulated_per_job, txs_dropped_per_job, gas_saved_per_job (histograms)
  • presim_duration (histogram, seconds)

Test plan

  • cargo check — clean for new code (pre-existing flashtestations typos unrelated)
  • cargo clippy — clean for new code
  • cargo +nightly fmt --check — clean for new code
  • cargo test --workspace --all-features — run full suite
  • Manual: spin up against playground, send a reverting tx, confirm it's filtered before building (grep "presim: dropping reverting transaction" in logs) and metrics are emitted
  • Manual: toggle --builder.presim-random-coinbase=false and confirm a tx that branches on COINBASE to revert can detect simulation (negative test for the anti-gaming property)
  • Confirm flashblock build time histograms drop under the same adversarial workload that motivated this PR

Tests added (src/tests/presim.rs)

  • presim_filters_reverting_tx_without_revert_protection — the key test: presim ON, revert protection OFF. Proves presim alone is sufficient.
  • presim_and_revert_protection_both_active — belt and suspenders.
  • presim_keeps_valid_transactions — no false positives.

Open questions / follow-ups

  • Should there be a presim time budget (e.g. --builder.presim-time-budget=200ms) to bound the worst case if the pool is huge or sims are unusually expensive? Easy to add.
  • Bundles are intentionally skipped here. Worth a separate pass to pre-sim bundles too once we've validated the loose-tx path.
  • remove_any_with removes from both order pool and system pool. If a loose tx is also part of a bundle, the bundle gets dropped too. Unlikely in practice but worth flagging.

🤖 Generated with Claude Code

Adds a new `PreSimulateOrders` pipeline step that simulates pending
pool transactions at the top of each new block before the flashblock
building loop starts. Transactions that revert during pre-simulation
are removed from the pool, preventing them from consuming block
building time on the critical path.

Inspired by rbuilder's `OrderSimulationPool`. Uses a random coinbase
during simulation (configurable) to prevent adversaries from detecting
the simulation environment by checking the COINBASE opcode.

Step is placed after `OptimismPrologue` and `FlashtestationsPrologue`
so it sees post-sequencer state. Only loose transactions are
pre-simulated; bundles continue to be handled by the existing
`RemoveRevertedTransactions` step during building. EVM-level errors
(wrong nonce, insufficient balance) are treated as "keep" since they
may become valid after earlier transactions execute.

New CLI flags (both default true):
  --builder.presim
  --builder.presim-random-coinbase

Verification:
  cargo check    -- clean for new code
  cargo clippy   -- clean for new code
  cargo fmt      -- clean for new code

Tests in src/tests/presim.rs validate that presim alone filters
reverting txs when revert protection is disabled, that presim + revert
protection both keep reverting txs out, and that valid txs are kept.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@dmarzzz
Copy link
Copy Markdown
Member Author

dmarzzz commented Apr 16, 2026

Wrong repo — redoing this in op-rbuilder where the work is actually needed.

@dmarzzz dmarzzz closed this Apr 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant