Skip to content

fix(txpool): Preserve Rejected Insertion State - #4236

Draft
refcell wants to merge 1 commit into
mainfrom
rf/fix/keep-txpool-rejections-side-effect-free
Draft

fix(txpool): Preserve Rejected Insertion State#4236
refcell wants to merge 1 commit into
mainfrom
rf/fix/keep-txpool-rejections-side-effect-free

Conversation

@refcell

@refcell refcell commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Summary

This makes rejected 2D nonce pool insertions side-effect free by completing stale nonce and replacement-price checks before mutating pool state. It also keeps bounded sidecar listeners subscribed when their channels are temporarily full, while still removing closed listeners. Regression tests cover lane cursors, sender identifiers, pool views, and listener delivery after backpressure.

@refcell refcell added bug Flag: Something isn't working execution Area: execution labels Aug 1, 2026
@refcell refcell self-assigned this Aug 1, 2026
@cb-heimdall

Copy link
Copy Markdown
Collaborator

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Summary

The changes are well-structured and correct. Both fixes address real side-effect bugs in the 2D nonce pool:

Channelized path (two_d_nonce_pool.rs): Moving the stale-nonce and replacement-underpriced checks before sender_id_or_create / lane mutation is the right approach. The stale nonce check against state_nonce directly is semantically equivalent to the old lane.next_nonce check (since lane.next_nonce was always set to state_nonce before the check). The replacement price lookup via self.lanes.get(&lane_id) correctly returns None for non-existent lanes, avoiding false positives.

Nonce-free path (two_d_nonce_pool.rs): Same pattern — the underpriced check already preceded sender_id_or_create in the old code, but the sender_id_or_create + TransactionId assignment was happening before the check. Now both are correctly deferred until after validation passes.

Sidecar listeners (pool.rs): Changing from try_send().is_ok() to !matches!(try_send(), Err(TrySendError::Closed(_))) correctly distinguishes "channel full" (keep subscriber) from "channel closed" (remove subscriber). This prevents spurious unsubscription under backpressure.

Tests: Good coverage of the key invariants — stale nonce rejection leaves lane cursor, hashes, and sender IDs untouched; underpriced replacement preserves lane state; full listener channels stay subscribed while closed channels get cleaned up.

No issues found.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Base Std historical fork tests

Fork Result Passed Failed Skipped base/base base-anvil base-std
Beryl pass 616 0 13 20b222e6 6d744e03 4658f1b7
Cobalt pass 703 0 14 20b222e6 ae7557c4 96e96870

View run

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

Labels

bug Flag: Something isn't working execution Area: execution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants