Skip to content

refactor(core/txpool/legacypool): use uint256.Int instead of big.Int #28606#2134

Merged
AnilChinchawale merged 1 commit intoXinFinOrg:dev-upgradefrom
gzliudan:txpool-uint256
Mar 10, 2026
Merged

refactor(core/txpool/legacypool): use uint256.Int instead of big.Int #28606#2134
AnilChinchawale merged 1 commit intoXinFinOrg:dev-upgradefrom
gzliudan:txpool-uint256

Conversation

@gzliudan
Copy link
Copy Markdown
Collaborator

@gzliudan gzliudan commented Mar 4, 2026

Proposed changes

Ref: ethereum#28606

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

Copilot AI review requested due to automatic review settings March 4, 2026 10:18
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 4, 2026

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

Run ID: fa262830-e493-4073-9a5f-95c6fb85cb31

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Refactors the legacy transaction pool to use holiman/uint256.Int for internal cost accounting and adjusts the txpool initialization API to accept a uint64 gas tip derived from config (PriceLimit).

Changes:

  • Update txpool.New and SubPool.Init to accept gasTip uint64 (instead of *big.Int) and adapt call sites.
  • Switch legacypool list cost tracking (costcap, totalcost) from *big.Int to *uint256.Int, adding overflow/underflow handling paths.
  • Update and extend tests/benchmarks to use uint256 and cover very-large-cost transactions.

Reviewed changes

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

Show a summary per file
File Description
eth/backend.go Pass PriceLimit directly into txpool.New after signature change.
core/txpool/txpool.go Change New signature to gasTip uint64 and forward it to subpools.
core/txpool/subpool.go Update SubPool.Init interface to accept gasTip uint64.
core/txpool/legacypool/list.go Convert list cost accounting to uint256.Int and update filter/subtraction logic.
core/txpool/legacypool/list_test.go Add “very expensive cost” test and adjust benchmark to uint256.
core/txpool/legacypool/legacypool.go Store gasTip as atomic.Pointer[uint256.Int] and adapt comparisons/conversions.
core/txpool/legacypool/legacypool_test.go Update init calls and adapt assertions for uint256-based totals.
core/txpool/legacypool/legacypool2_test.go Update init calls for new Init(gasTip uint64, ...) signature.
Comments suppressed due to low confidence (4)

core/txpool/legacypool/legacypool_test.go:238

  • txs.totalcost is a uint256.Int and cannot be negative, so txs.totalcost.Sign() < 0 is dead code and won’t catch accounting regressions. Consider replacing this with a meaningful invariant (e.g., recompute the sum of tx.Cost() across txs.txs.items and compare to txs.totalcost, or at least assert it doesn’t underflow during removals).
		}
		if txs.totalcost.Sign() < 0 {
			return fmt.Errorf("totalcost went negative: %v", txs.totalcost)
		}

core/txpool/legacypool/list.go:386

  • Filter converts costLimit to big.Int inside the per-tx predicate (costLimit.ToBig() / maximum.ToBig()), which likely allocates each iteration. Since Filter can run over large account lists, consider hoisting the ToBig() conversion(s) outside the closure (once per call) to avoid per-transaction allocations.
	// Filter out all the transactions above the account's funds
	removed := l.txs.Filter(func(tx *types.Transaction) bool {
		maximum := costLimit
		if tx.To() != nil {
			if feeCapacity, ok := trc21Issuers[*tx.To()]; ok {
				return tx.Gas() > gasLimit || new(big.Int).Add(costLimit.ToBig(), feeCapacity).Cmp(tx.TxCost(number)) < 0
			}
		}
		return tx.Gas() > gasLimit || tx.Cost().Cmp(maximum.ToBig()) > 0
	})

core/txpool/legacypool/legacypool.go:603

  • Pending(enforceTips=true) converts the atomic uint256 gas tip to *big.Int inside the inner loop (pool.gasTip.Load().ToBig()), likely allocating once per tx. Cache the converted tip once per Pending call (or at least once per account) to avoid repeated allocations when building pending sets.
		// If the miner requests tip enforcement, cap the lists now
		if enforceTips && !pool.locals.contains(addr) {
			for i, tx := range txs {
				if !tx.IsSpecialTransaction() && tx.EffectiveGasTipIntCmp(pool.gasTip.Load().ToBig(), pool.priced.urgent.baseFee) < 0 {
					txs = txs[:i]
					break

core/txpool/legacypool/legacypool.go:663

  • validateTxBasics now calls pool.gasTip.Load().ToBig() for every validation, which likely allocates. Since this runs for each incoming tx, consider caching a *big.Int view of the current tip (updated alongside the atomic) or otherwise avoid repeated conversions on the hot path.
			1<<types.DynamicFeeTxType |
			1<<types.SetCodeTxType,
		MaxSize: txMaxSize,
		MinTip:  pool.gasTip.Load().ToBig(),
		NotSigner: func(from common.Address) bool {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1084 to 1090
// Nothing was replaced, bump the pending counter
pendingGauge.Inc(1)
}
list.txs.Put(tx)
if cost := tx.Cost(); list.costcap.Cmp(cost) < 0 {
if cost := uint256.MustFromBig(tx.Cost()); list.costcap.Cmp(cost) < 0 {
list.costcap = cost
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

promoteSpecialTx updates list.costcap/gascap but never updates list.totalcost. With totalcost now a uint256.Int and subTotalCost using SubOverflow, removing a mined/forwarded special tx (or any later list maintenance that calls subTotalCost) can trigger an underflow and panic, and ExistingExpenditure will also undercount spent funds while the special tx is pending. Update this path to keep totalcost consistent (subtract any replaced tx’s cost and add the new tx’s cost) using the same overflow handling as list.Add.

Copilot uses AI. Check for mistakes.
Comment on lines 475 to +479
for _, tx := range txs {
l.totalcost.Sub(l.totalcost, tx.Cost())
_, underflow := l.totalcost.SubOverflow(l.totalcost, uint256.MustFromBig(tx.Cost()))
if underflow {
panic("totalcost underflow")
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

subTotalCost panics on underflow. Since this runs in normal pool maintenance paths (Forward/Filter/Cap/Ready/Remove), any accounting mismatch (e.g., a tx added without incrementing totalcost, or a cost conversion mismatch) will crash the node. Prefer making this non-fatal (e.g., recompute totalcost from l.txs when an underflow is detected, or clamp to zero and log) so internal inconsistencies don’t take down the process.

Copilot uses AI. Check for mistakes.
@gzliudan gzliudan force-pushed the txpool-uint256 branch 2 times, most recently from 22c3b00 to 3060c58 Compare March 7, 2026 10:01
@AnilChinchawale AnilChinchawale merged commit 9d6e8fc into XinFinOrg:dev-upgrade Mar 10, 2026
13 checks passed
@gzliudan gzliudan deleted the txpool-uint256 branch March 10, 2026 13:39
gzliudan added a commit to gzliudan/XDPoSChain that referenced this pull request Mar 10, 2026
…rve tx replacement accounting, fix XinFinOrg#2134

The XinFinOrg#2134 txpool changes converted account-balance checks to uint256.MustFromBig, which can panic when state balances exceed uint256 range during promote/demote filtering.

Fix this by switching list cost caps and filter thresholds to big.Int and by passing state balances directly into list.Filter instead of forcing uint256 conversion.

Also fix replacement accounting for both regular and special pending paths. Compute totals from the post-replacement base (subtract old cost first, then AddOverflow new cost) to avoid false overflow rejections while still rejecting real uint256 overflow.

Add regression tests for special-tx totalcost consistency and replacement scenarios that previously failed due to intermediate overflow.
gzliudan added a commit to gzliudan/XDPoSChain that referenced this pull request Mar 10, 2026
…able tx filtering, fix XinFinOrg#2134

A runtime panic was triggered in promoteExecutables/demoteUnexecutables when
account balance was converted with uint256.MustFromBig(...):

panic: overflow
... legacypool.go:1637

Root cause:
- pool.currentState.GetBalance(addr) can exceed uint256 range in this code path.
- uint256.MustFromBig(balance) panics on overflow, crashing the reorg loop.

What this commit changes:
- remove uint256.MustFromBig(balance) from executable/non-executable filtering paths
- change list.Filter costLimit from *uint256.Int to *big.Int, and compare costs using big.Int directly
- keep overflow-safe totalcost accounting for replacements (subtract old cost first, then add new)
- return txpool.ErrSpecialTxCostOverflow for special-tx cost/totalcost overflow instead of returning (false, nil)
- avoid partial pending-state mutation by attaching a new pending list only after overflow-safe totalcost calculation succeeds

Tests:
- add regression coverage for special-tx overflow rejection returning non-nil error
- verify no pending/lookup/nonce mutation on overflow rejection
- cover replacement paths to ensure no intermediate-overflow regressions in list.Add/promoteSpecialTx
gzliudan added a commit to gzliudan/XDPoSChain that referenced this pull request Mar 11, 2026
…able tx filtering, fix XinFinOrg#2134

A runtime panic was triggered in promoteExecutables/demoteUnexecutables when
account balance was converted with uint256.MustFromBig(...):

panic: overflow
... legacypool.go:1637

Root cause:
- pool.currentState.GetBalance(addr) can exceed uint256 range in this code path.
- uint256.MustFromBig(balance) panics on overflow, crashing the reorg loop.

What this commit changes:
- remove uint256.MustFromBig(balance) from executable/non-executable filtering paths
- change list.Filter costLimit from *uint256.Int to *big.Int, and compare costs using big.Int directly
- keep overflow-safe totalcost accounting for replacements (subtract old cost first, then add new)
- return txpool.ErrSpecialTxCostOverflow for special-tx cost/totalcost overflow instead of returning (false, nil)
- avoid partial pending-state mutation by attaching a new pending list only after overflow-safe totalcost calculation succeeds

Tests:
- add regression coverage for special-tx overflow rejection returning non-nil error
- verify no pending/lookup/nonce mutation on overflow rejection
- cover replacement paths to ensure no intermediate-overflow regressions in list.Add/promoteSpecialTx
gzliudan added a commit to gzliudan/XDPoSChain that referenced this pull request Mar 11, 2026
…able tx filtering, fix XinFinOrg#2134

A runtime panic was triggered in promoteExecutables/demoteUnexecutables when
account balance was converted with uint256.MustFromBig(...):

panic: overflow
... legacypool.go:1637

Root cause:
- pool.currentState.GetBalance(addr) can exceed uint256 range in this code path.
- uint256.MustFromBig(balance) panics on overflow, crashing the reorg loop.

What this commit changes:
- remove uint256.MustFromBig(balance) from executable/non-executable filtering paths
- change list.Filter costLimit from *uint256.Int to *big.Int, and compare costs using big.Int directly
- keep overflow-safe totalcost accounting for replacements (subtract old cost first, then add new)
- return txpool.ErrSpecialTxCostOverflow for special-tx cost/totalcost overflow instead of returning (false, nil)
- avoid partial pending-state mutation by attaching a new pending list only after overflow-safe totalcost calculation succeeds

Tests:
- add regression coverage for special-tx overflow rejection returning non-nil error
- verify no pending/lookup/nonce mutation on overflow rejection
- cover replacement paths to ensure no intermediate-overflow regressions in list.Add/promoteSpecialTx
gzliudan added a commit to gzliudan/XDPoSChain that referenced this pull request Mar 13, 2026
…able tx filtering, fix XinFinOrg#2134

A runtime panic was triggered in promoteExecutables/demoteUnexecutables when
account balance was converted with uint256.MustFromBig(...):

panic: overflow
... legacypool.go:1637

Root cause:
- pool.currentState.GetBalance(addr) can exceed uint256 range in this code path.
- uint256.MustFromBig(balance) panics on overflow, crashing the reorg loop.

What this commit changes:
- remove uint256.MustFromBig(balance) from executable/non-executable filtering paths
- change list.Filter costLimit from *uint256.Int to *big.Int, and compare costs using big.Int directly
- keep overflow-safe totalcost accounting for replacements (subtract old cost first, then add new)
- return txpool.ErrSpecialTxCostOverflow for special-tx cost/totalcost overflow instead of returning (false, nil)
- avoid partial pending-state mutation by attaching a new pending list only after overflow-safe totalcost calculation succeeds

Tests:
- add regression coverage for special-tx overflow rejection returning non-nil error
- verify no pending/lookup/nonce mutation on overflow rejection
- cover replacement paths to ensure no intermediate-overflow regressions in list.Add/promoteSpecialTx
AnilChinchawale pushed a commit that referenced this pull request Mar 17, 2026
…able tx filtering, fix #2134 (#2168)

A runtime panic was triggered in promoteExecutables/demoteUnexecutables when
account balance was converted with uint256.MustFromBig(...):

panic: overflow
... legacypool.go:1637

Root cause:
- pool.currentState.GetBalance(addr) can exceed uint256 range in this code path.
- uint256.MustFromBig(balance) panics on overflow, crashing the reorg loop.

What this commit changes:
- remove uint256.MustFromBig(balance) from executable/non-executable filtering paths
- change list.Filter costLimit from *uint256.Int to *big.Int, and compare costs using big.Int directly
- keep overflow-safe totalcost accounting for replacements (subtract old cost first, then add new)
- return txpool.ErrSpecialTxCostOverflow for special-tx cost/totalcost overflow instead of returning (false, nil)
- avoid partial pending-state mutation by attaching a new pending list only after overflow-safe totalcost calculation succeeds

Tests:
- add regression coverage for special-tx overflow rejection returning non-nil error
- verify no pending/lookup/nonce mutation on overflow rejection
- cover replacement paths to ensure no intermediate-overflow regressions in list.Add/promoteSpecialTx
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.

6 participants