fix(op-reth): evict definitively-invalid interop txs on revalidation#21447
Merged
ajsutton merged 17 commits intoJun 23, 2026
Conversation
a914013 to
dc4f32f
Compare
21fc30d to
2ef0019
Compare
einar-oplabs
requested changes
Jun 18, 2026
einar-oplabs
left a comment
Contributor
There was a problem hiding this comment.
I requested changes because I think it is important we keep the tests super fast.
Let me know if I can help out with something.
Wiz Scan Summary
To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension. |
einar-oplabs
approved these changes
Jun 19, 2026
einar-oplabs
left a comment
Contributor
There was a problem hiding this comment.
I think this is a huge improvement 👍
My main point in this round is that we should probably make the type signatures of the InteropFilter as simple as possible. Unless we have a good reason to pass streams and box futures around and track life times of transactions, let's keep it simple.
Let me know if you want me to help with anything.
91efe99 to
f673a2a
Compare
einar-oplabs
approved these changes
Jun 22, 2026
einar-oplabs
left a comment
Contributor
There was a problem hiding this comment.
I think it is in a good state now. Added some minor points about naming.
einar-oplabs
approved these changes
Jun 22, 2026
a5abec4 to
3eb19bf
Compare
The interop txpool revalidation sweep gated eviction on reth's `is_bad_transaction()`, which is true only for `CrossChainTxPreInterop` and false for `ValidationError(_)`. A definitive invalid verdict from the interop filter (`InvalidEntry`/`Rejected`) therefore stayed buildable until its ~2h cached deadline expired. `is_bad_transaction()` answers reth's peer-penalization question, a different concern from "is this tx now invalid and must be evicted". Add a dedicated `InvalidCrossTx::is_now_invalid()` predicate and use it in the revalidation sweep: evict on a decisive invalid verdict, retain on transient/non-decisive outcomes (quorum miss, timeout, data-unavailable, disagreement) so a flapping or unreachable interop filter cannot drain the pool. Failsafe stays handled by the whole-pool eviction. reth's peer-penalization predicate is left unchanged.
The interop revalidation/eviction sweep iterated `pool.pooled_transactions()`, which filters out `propagate=false` transactions. Non-propagatable interop txs (`Private` origin, e.g. from `eth_sendRawTransactionConditional`) were therefore never revalidated or evicted, yet the builder still selects them via `best_transactions()` (no propagate filter) — a permanent eviction blind spot. Scan `pool.all_transactions()` (pending/basefee/queued, no propagate filter) at the three interop sites: the failsafe eviction in `maintain_transaction_pool_interop`, the stale scan in `revalidate_stale_interop_txs`, and the eviction in `poll_failsafe`. Adds a regression test that admits a `Private`-origin interop tx and asserts the sweep evicts it on a definitive invalid verdict (fails on baseline).
The interop txpool maintenance loop handled only `Commit` notifications and silently dropped `Reorg`. A local-chain reorg can invalidate the initiating message a pooled cross-chain executing tx depends on (and source-chain reorgs are not observable here at all), leaving the dependent tx pooled and buildable. Handle `Reorg` by evicting all interop txs from the pool. Extract the scan-and-remove logic into `evict_all_interop_txs`, shared by the reorg arm, the failsafe path, and `poll_failsafe`. The OpPool reorg-window admission filter is kept: it suppresses re-admission of interop txs via gossip or block reinjection while the chain settles.
3eb19bf to
0a21ba9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three related fixes to the interop txpool revalidation/eviction sweep, all in
maintain.rs.1. Evict on a definitive invalid verdict. The sweep gated eviction on reth's
is_bad_transaction()— a peer-penalization predicate that's false forValidationError(_). A definitive invalid verdict from the interop filter (InvalidEntry/Rejected) was therefore never evicted and stayed buildable until its ~2h cached deadline. Adds a dedicatedInvalidCrossTx::is_now_invalid()and uses it in the sweep: evict on a decisive invalid verdict, retain on transient/non-decisive outcomes (quorum miss, timeout, data-unavailable, disagreement) so a flapping or unreachable interop filter can't drain the pool. Failsafe stays handled by the whole-pool eviction; reth'sis_bad_transaction()is unchanged.2. Revalidate non-propagatable interop txs. The sweep iterated
pool.pooled_transactions(), which filters outpropagate=falsetxs. Non-propagatable interop txs (Privateorigin, e.g. frometh_sendRawTransactionConditional) were never revalidated or evicted, yet the builder still selects them viabest_transactions()(no propagate filter) — a permanent eviction blind spot. Now scanspool.all_transactions()at all interop sites (failsafe eviction, stale scan,poll_failsafe).3. Evict all interop txs on reorg. The maintenance loop handled only
Commitnotifications and silently droppedReorg. A local-chain reorg can invalidate the initiating message a pooled cross-chain executing tx depends on (and source-chain reorgs are not observable here at all), leaving the dependent tx pooled and buildable. The loop now matches on the event and theReorgarm evicts all interop txs from the pool. The scan-and-remove logic is extracted intoevict_all_interop_txs, shared by the reorg arm, the failsafe path, andpoll_failsafe. TheOpPool30s reorg-window admission filter is kept: it suppresses re-admission of interop txs via gossip or block reinjection while the chain settles.The revalidation sweep is extracted into a testable function; tests drive a real pool against a mock interop-filter endpoint and cover the eviction decision across origin × verdict, plus reorg eviction (incl. a non-propagatable
Private-origin tx) and the failsafe-on-commit branch.