Skip to content

fix(core/txpool/locals): untrack rejected and superseded local txs on resubmit - #2541

Open
gzliudan wants to merge 2 commits into
XinFinOrg:dev-upgradefrom
gzliudan:untrack-lower-price-tx
Open

fix(core/txpool/locals): untrack rejected and superseded local txs on resubmit#2541
gzliudan wants to merge 2 commits into
XinFinOrg:dev-upgradefrom
gzliudan:untrack-lower-price-tx

Conversation

@gzliudan

Copy link
Copy Markdown
Collaborator

Proposed changes

Summary

The core/txpool/locals tracker resubmits locally-tracked transactions every recheck interval but discards the errors returned by pool.Add. After the gas schedule fork sweep drops underpriced transactions from the pool, a local transaction priced out by the fork turns into a permanent zombie: it is never untracked, survives in the on-disk journal, and is re-tracked unconditionally on restart. This PR fixes how the tracker classifies resubmit outcomes so the tracked set and the journal stay consistent with the pool's actual state.

Root cause

TxTracker.loop called pool.Add(resubmits, false) and ignored the returned errors, and there was no path to remove a transaction from the tracked set or journal once the pool permanently rejected it.

Changes

Reuse the same error classification that AddLocal applies to the initial admission, now on resubmission: untrack permanently rejected txs (removed from tracker.all and tracker.byAddr, matched by hash not nonce so a same-nonce replacement is not taken down with the tx it superseded); treat ErrAlreadyKnown as success (a resubmission losing a race to a concurrent Add returning txpool.ErrAlreadyKnown is already in the desired state and stays tracked); drop superseded same-nonce replacements in TrackAll (remove the previously tracked hash at a nonce when a replacement is tracked, keeping the tracked set consistent with the per-address map); and move journal rotation out of recheck so it runs only after resubmission rejections are processed and fires immediately when a transaction was untracked, wiping rejected transactions from disk in the same cycle.

Risk / notes

Hot path: untrackRejected runs only when there are resubmits and acquires tracker.mu once for the whole batch, with no per-transaction pool calls added. The change is confined to the local tracker; no txpool public API changed.

Types of changes

What types of changes does your code introduce to XDC network?
Put an in the boxes that apply

  • build: Changes that affect the build system or external dependencies
  • ci: Changes to CI configuration files and scripts
  • chore: Changes that don't change source code or tests
  • docs: Documentation only changes
  • feat: A new feature
  • fix: A bug fix
  • perf: A code change that improves performance
  • refactor: A code change that neither fixes a bug nor adds a feature
  • revert: Revert something
  • style: Changes that do not affect the meaning of the code
  • test: Adding missing tests or correcting existing tests

Impacted Components

Which parts of the codebase does this PR touch?
Put an in the boxes that apply

  • Consensus
  • Account
  • Network
  • Geth
  • Smart Contract
  • External components
  • Not sure (Please specify below)

Checklist

Put an in the boxes once you have confirmed below actions (or provide reasons on not doing so) that

  • This PR has sufficient test coverage (unit/integration test) OR I have provided reason in the PR description for not having test coverage
  • Tested on a private network from the genesis block and monitored the chain operating correctly for multiple epochs.
  • Provide an end-to-end test plan in the PR description on how to manually test it on the devnet/testnet.
  • Tested the backwards compatibility.
  • Tested with XDC nodes running this version co-exist with those running the previous version.
  • Relevant documentation has been updated as part of this PR
  • N/A

… resubmit

The gas schedule fork sweep (56adc5a97) drops underpriced transactions
from the pool, but the local tracker keeps resubmitting them every
minute and discards the rejection, so a transaction priced out by the
fork turns into a permanent zombie: it stays in the tracked set and in
the journal, and is re-tracked unconditionally on restart.

Consume the resubmit errors the same way AddLocal consumes the initial
admission: a transaction the pool refuses for a non-temporary reason is
untracked, while accepted and temporarily rejected ones stay tracked.
Match the per-address map by hash, not by nonce, so a replacement at the
same nonce is not taken down with the transaction it superseded.

This commit addresses three issues found during review:

  * Orphaned transactions: a same-nonce replacement supersedes the
    previously tracked transaction, so TrackAll now drops the superseded
    hash from the tracked set to keep it consistent with the per-address
    map (the journal catches up on the next rotation).
  * ErrAlreadyKnown is not a rejection: a resubmission that loses a race
    to a concurrent Add returning txpool.ErrAlreadyKnown is already in the
    desired state, so untrackRejected treats it like a success and keeps
    tracking the transaction.
  * Journal resurrection window: the journal rotation is moved out of
    recheck and performed only after resubmission rejections are
    processed, and fires immediately when a transaction was untracked, so
    rejected transactions are removed from disk in the same cycle instead
    of surviving until the next scheduled rotation.
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8525955f-6ceb-40f8-a3b0-2e0baf95e351

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes local transaction tracking so permanently rejected or superseded transactions are removed from memory and disk.

Changes:

  • Classifies resubmission errors and untracks permanent rejections.
  • Removes superseded same-nonce transactions.
  • Rotates journals after removals and adds regression tests.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
core/txpool/locals/tx_tracker.go Updates resubmission, untracking, and journal rotation logic.
core/txpool/locals/tx_tracker_test.go Tests rejection, replacement, and journal persistence behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread core/txpool/locals/tx_tracker.go Outdated
untrackRejected previously called SortedMap.Filter once per rejected
transaction. Filter rescans the whole per-address map and rebuilds its
heap, so a fork sweep that permanently rejects k tracked transactions
from one sender turned into O(k²) work while tracker.mu was held,
blocking new local tracking and journal writes.

Group permanently rejected hashes by sender and filter each per-address
map exactly once, using a hash set for O(1) membership checks. Add a
test covering the fork-sweep shape.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

core/txpool/locals/tx_tracker_test.go:165

  • Handle the key-generation error instead of passing a potentially nil key to SignTx; the repository's Go error-handling convention does not allow ignored errors.
	otherKey, _ := crypto.GenerateKey()

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.

2 participants