Repository navigation
fix(mempool): compare RPC package fees exactly and preserve Core precedence - #1712
Conversation
There was a problem hiding this comment.
Review Summary
This PR makes two targeted fixes to mempool RPC package fee policy:
-
check_package_max_feerate(gateway.rs): Replaces theeffective_fee_rateu64 rate-quote comparison with an exact signedi128modified-fee cross-product comparison. The arithmetic is correct:u64 × u32fitsi128, the comparison is equivalent to Core's cross-product check for integer fees, and signed extremes (e.g.,i128::MIN,i128::MAX) are handled correctly by the guard. -
finish_package(package.rs): Inserts a new early-return forPackageRpcSubmitmode that reportsMinRelayFeeNotMetafter TRUC checks and before cluster/dust/capacity checks. The singleton exemption logic (skipping the per-member cap when the aggregate floor already rejects a singleton) is also correctly structured.
Defect Found
One test assertion appears inconsistent with the new precedence rule. In rpc_package_fee_failure_precedence_preserves_other_planner_errors (package/tests.rs), the (dust=true, full=true) case asserts MissingEphemeralSpends as the package_error. However, the new block in finish_package (lines 211–216) returns MinRelayFeeNotMet before the dust or capacity checks are reached whenever aggregate_error == MinRelayFeeNotMet — which should still hold for the full=true case, since the aggregate fee floor error is computed independently of the capacity constraint. The test comment ("Capacity refusal retains its old position after dust checks") describes the intended behavior but is not matched by the new code path. This assertion should either be corrected to MinRelayFeeNotMet, or the precedence logic needs a guard to suppress the early return when capacity fails.
You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.
| assert_eq!( | ||
| result.package_error, | ||
| Some(if full { | ||
| // Capacity refusal retains its old position after dust checks. | ||
| AcceptanceRejectReason::MissingEphemeralSpends | ||
| } else { | ||
| AcceptanceRejectReason::MinRelayFeeNotMet | ||
| }) | ||
| ); |
There was a problem hiding this comment.
🛑 Logic Error: The test assertion for (dust=true, full=true) expects MissingEphemeralSpends, but the new MinRelayFeeNotMet precedence block in finish_package (lines 211–216) returns MinRelayFeeNotMet before the dust/capacity checks are reached. The aggregate_error passed to finish_package is the fee-floor result (MinRelayFeeNotMet), which is independent of the capacity constraint. With the new code, the result for full=true should be MinRelayFeeNotMet, not MissingEphemeralSpends. Either the test assertion is wrong, or the intended precedence requires that capacity failures suppress the MinRelayFeeNotMet early-return — but there is no such guard in the new code.
|
Approve (comment only, not a formal review) — reviewed head
Non-blocking, pre-existing and outside this PR's stated scope: when the pool is full and the package underpays, we report cluster/dust rather than Core's fee-floor failure, because capacity is checked before the fee floor in Verification: locally ran |
A submitted member can exceed
maxfeerateby one satoshi while its displayed integer sat/kvB rate still equals the limit. RPC package underpayment can also be reported as a later cluster/dust failure.Preserve only these remaining fixes from #1667 after the scoped submitpackage implementation landed in #1693:
i128modified fee exactly againstmax_rate * vsize / 1000. The nonnegativeu64 * u32limit fitsi128; this avoids signed-fee multiplication overflow and works even when nou64rate quote exists. This implements the strict rational comparison for integer fees.MinRelayFeeNotMetafter TRUC checks and before cluster/ephemeral-spend checks. Other planner errors and native/peer submission keep their existing precedence.This is a fresh adaptation to current main, not a copy of #1667's unsigned/GetFee implementation. Pinned Core 31.1 directly compares
CFeeRate(modified_fee, vsize)to the caller's rate in single/package submission, using FeeRateCompare cross-products. It does not apply GetFee's one-satoshi minimum to this comparison. The aggregate floor precedes cluster/dust checks. Existing non-RPC fee-rounding behavior is outside this change.Regression evidence:
cargo test -p bitcoin-rs-mempool --lib package: 44 passed, including current RPC/native/peer package and snapshot CPFP proofs.cargo clippy -p bitcoin-rs-mempool --lib --tests -- -D warnings: passed.cargo fmt --all -- --checkandgit diff --check: passed.No sync impact: this changes only policy decisions over captured mempool admission facts, before the existing stamped mutation boundary. It does not cross authoritative sync, chain validation, durable chainstate commit, retention, or lock-order boundaries; no SYNC-* clause is changed. No schema, protocol, counter, dependency, or RPC surface expansion. The Core evidence is source comparison plus local behavioral tests; a new Core process campaign was not run.