Skip to content

fix(core/txpool): keep local tracker consistent with gas price floor - #2541

Merged
gzliudan merged 2 commits into
XinFinOrg:dev-upgradefrom
gzliudan:untrack-lower-price-tx
Sep 1, 2026
Merged

fix(core/txpool): keep local tracker consistent with gas price floor#2541
gzliudan merged 2 commits into
XinFinOrg:dev-upgradefrom
gzliudan:untrack-lower-price-tx

Conversation

@gzliudan

@gzliudan gzliudan commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two fixes to keep the local transaction tracker aligned with the gas price floor and with the pool's nonce ownership. Hold back local resubmits priced below the current gas price floor, so a gas schedule fork that raises the floor no longer makes the tracker re-submit transactions that can only fail with ErrUnderMinGasPrice; held-back transactions stay tracked and are picked up again automatically if the floor drops (reorg / rollback past the fork). Drop a tracked transaction that is superseded at its nonce by another tracked transaction in TrackAll, instead of leaving both in the journal forever (which could resurrect a replaced transaction on the next journal load).

Motivation & Context

After a gas tier fork the floor can rise above transactions admitted under the previous tier. The tracker's periodic recheck would re-submit them every cycle, only to be rejected with ErrUnderMinGasPrice, and ErrUnderMinGasPrice was previously treated as a temporary rejection, which is wrong: the floor only rises, so retrying cannot help while the tx stays where it is, and dropping a tracked tx is wrong because a rollback past the fork would make it admissible again. The tracker previously put every tracked tx into all without considering that two tracked transactions can share a nonce (concurrent submissions, or a journal written by an older version holding both a tx and its replacement), and the silent overwrite in SortedMap.Put left the superseded tx stranded in all/journal, where it could win the nonce on reload.

Changes

core/txpool/validation.go: add pendingBlockNumber helper; admission validation now uses it (no behavior change). core/txpool/txpool.go: add TxPool.MinGasPrice() returning the floor resolved at the pending-block height. core/txpool/locals/tx_tracker.go: TrackAll drops the tracked tx superseded at a nonce, deciding the winner the same way the pool does (which tx the pool holds, special-tx priority, then fee-cap/tip comparison); recheck skips non-special tracked txs below the floor (counted in the new txpool/local/belowfloor gauge) and returns the rest for resubmission; IsTemporaryReject (errors.go doc) explicitly excludes ErrUnderMinGasPrice. Tests: tx_tracker_test.go, txpool_test.go, txpool_local_test.go, errors_test.go cover both behaviors.

Testing

New/updated unit tests in core/txpool and core/txpool/locals. Run make quick-test / make test, or go test ./core/txpool/... ./core/txpool/locals/....

Related Issues

None.

Risk & Impact

Local (tracked) transactions only; the public pool admission path is unchanged except the floor is now resolved through a shared helper. Behavior change: tracked transactions priced out by a fork are no longer re-submitted every minute and are no longer eligible to be dropped as stale; they persist until admissible again, which avoids log/metric spam and incorrect drops. Journal convergence: journals written by older versions holding a tx and its replacement now converge to the replacement on load.

Notes for Reviewers

TxPool.MinGasPrice resolves the floor against the chain head, while admission resolves it against the subpool head; the two can drift briefly while a head event is in flight, but this is self-correcting on the next recheck (period far longer than the drift window) — see the doc comment. The nonce-supersession decision in TrackAll deliberately does not repeat the price-bump policy, to avoid making any case worse than before.

TODO

None.

@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: Team

Run ID: 0fc25e96-8ffc-4997-b70f-6cd26b4130d3

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

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()

@gzliudan
gzliudan force-pushed the untrack-lower-price-tx branch from 69d8ff1 to caf9b92 Compare August 27, 2026 07:28
@gzliudan
gzliudan force-pushed the untrack-lower-price-tx branch 11 times, most recently from 1c1fb44 to dd015f2 Compare August 30, 2026 22:49
@gzliudan
gzliudan force-pushed the untrack-lower-price-tx branch from dd015f2 to c5373cf Compare August 31, 2026 10:14
@gzliudan
gzliudan force-pushed the untrack-lower-price-tx branch from c5373cf to f9b404a Compare August 31, 2026 16:57
@gzliudan gzliudan changed the title fix(core/txpool/locals): untrack rejected and superseded local txs on resubmit fix(core/txpool,core/txpool/locals): fix local tracker resubmits Aug 31, 2026
@gzliudan
gzliudan requested a balanced review from Copilot August 31, 2026 17:04

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 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread core/txpool/locals/tx_tracker.go
@gzliudan
gzliudan force-pushed the untrack-lower-price-tx branch 3 times, most recently from a9ba231 to 247dfcd Compare August 31, 2026 22:48
…aces

TrackAll only ever added to the tracked set, while the per-nonce SortedMap
silently overwrote the entry a replacement displaced. The replaced transaction
was therefore never returned by Forward again, stayed tracked forever, was
rewritten into the journal on every rotation and could win the nonce on the
next load, resurrecting a transaction the user had already replaced, because
rotation writes the tracked set in map order through a non-stable sort.

Decide which of the two supersedes the other the way the pool decides it.
While the pool holds one of them it is authoritative: it occupies a nonce with
at most one transaction, and AddLocal tracks a local transaction only after Add
has released the subpool lock, so two concurrent submissions can be accepted in
one order and reach TrackAll in the other. When the pool holds neither -- two
submissions it has since discarded, or a journal an older version wrote -- fall
back to the substitution rules legacypool applies in list.Add: a special
transaction always claims its nonce, a regular one must not evict a pending
special one, and otherwise a replacement has to beat the transaction it
replaces on both fee cap and tip. The price bump is deliberately not repeated
here: it is pool policy, and a transaction that beats the old one but misses
the bump behaves exactly as before, so no case gets worse.

Load now converges a journal written by an older version on the replacement
rather than on whichever entry the rotation happened to sort last, so the entry
later in the file no longer decides the nonce. That is a behaviour change for
journals left behind by an older version: the replacement survives now even
when it was written first. Equally priced transactions are not a substitution
either, so the one already tracked keeps the nonce where the previous behaviour
let the later one take it.

Build the test environment from an explicit chain config so tests can pin the
gas schedule instead of sharing the package level genesis, and price the shared
replacement pair to clear the pool's price bump, which is what makes it a
substitution rather than two transactions the pool would reject.

Cover both orders in which a replacement can reach the tracker, the concurrent
interleaving where the original is added first and tracked last, the fallback
for two transactions the pool holds neither of, the special transaction rules
in both directions, the tie between equally priced transactions, and a dearer
tracked transaction losing to the one the pool accepted.
…the gas price floor

A gas schedule fork raises the floor above transactions that were admitted
under the previous tier, and the pool sweeps them out. The local tracker kept
resubmitting them every minute: recheck ignored the result of pool.Add, and the
transactions never went stale because their nonce never advanced, so they
stayed tracked and journalled forever.

Hold back any tracked transaction priced below the current floor, resolved for
the block pending on top of the head exactly as admission validation resolves
it. They stay tracked, so a reorg or a set-head rollback that lowers the floor
picks them up again on the next recheck: the pool does not bring back what it
swept, so the tracker is the only thing that can. Special transactions stay
exempt, as they are during admission.

Resolve the floor through a new TxPool.MinGasPrice, which goes through the
same pendingBlockNumber helper as admission validation and the sweep, so the
tracker and the pool cannot price the same transaction at different heights.

Report the held back transactions through txpool/local/belowfloor, since they
are still counted by txpool/local.

Pin that ErrUnderMinGasPrice is not a temporary reject: the floor only rises as
the chain advances, so retrying cannot help and AddLocal must not track the
transaction, but the error must not be used to drop a tracked one either.

Cover the floor with tests that rewind the head with SetHead across every tier
boundary and cover a head without a block number, the hold-back at exactly the
floor and one wei below it, resumption once the floor drops, and the exemption
special transactions keep.

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 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread core/txpool/locals/tx_tracker_test.go
@gzliudan gzliudan changed the title fix(core/txpool,core/txpool/locals): fix local tracker resubmits fix(core/txpool): keep local tracker consistent with gas price floor Sep 1, 2026
@gzliudan
gzliudan requested a balanced review from Copilot September 1, 2026 00:33

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 8 out of 8 changed files in this pull request and generated no new comments.

@gzliudan
gzliudan merged commit 0658240 into XinFinOrg:dev-upgrade Sep 1, 2026
20 checks passed
@gzliudan
gzliudan deleted the untrack-lower-price-tx branch September 1, 2026 00:45
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.

5 participants