refactor(core/txpool/legacypool): use uint256.Int instead of big.Int #28606#2134
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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.NewandSubPool.Initto acceptgasTip uint64(instead of*big.Int) and adapt call sites. - Switch
legacypoollist cost tracking (costcap,totalcost) from*big.Intto*uint256.Int, adding overflow/underflow handling paths. - Update and extend tests/benchmarks to use
uint256and 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.totalcostis auint256.Intand cannot be negative, sotxs.totalcost.Sign() < 0is dead code and won’t catch accounting regressions. Consider replacing this with a meaningful invariant (e.g., recompute the sum oftx.Cost()acrosstxs.txs.itemsand compare totxs.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
FilterconvertscostLimittobig.Intinside the per-tx predicate (costLimit.ToBig()/maximum.ToBig()), which likely allocates each iteration. SinceFiltercan run over large account lists, consider hoisting theToBig()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 atomicuint256gas tip to*big.Intinside the inner loop (pool.gasTip.Load().ToBig()), likely allocating once per tx. Cache the converted tip once perPendingcall (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
validateTxBasicsnow callspool.gasTip.Load().ToBig()for every validation, which likely allocates. Since this runs for each incoming tx, consider caching a*big.Intview 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.
| // 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 | ||
| } |
There was a problem hiding this comment.
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.
| 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") | ||
| } |
There was a problem hiding this comment.
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.
22c3b00 to
3060c58
Compare
…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.
…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
…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
…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
…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
…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
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 applyImpacted Components
Which parts of the codebase does this PR touch?
Put an
✅in the boxes that applyChecklist
Put an
✅in the boxes once you have confirmed below actions (or provide reasons on not doing so) that