Add ancestor-aware coin selection for CPFP bump fee calculation#43
Draft
evanlinjin wants to merge 1 commit intobitcoindevkit:masterfrom
Draft
Add ancestor-aware coin selection for CPFP bump fee calculation#43evanlinjin wants to merge 1 commit intobitcoindevkit:masterfrom
evanlinjin wants to merge 1 commit intobitcoindevkit:masterfrom
Conversation
4 tasks
3e59884 to
d168359
Compare
When spending unconfirmed UTXOs, miners evaluate the transaction as a package with its ancestors. This adds support for computing the package-level bump fee needed to bring ancestors up to the target feerate, with automatic deduplication of shared ancestors across candidates. - Add UnconfirmedAncestor struct (weight + fee_paid) - Add ancestors field to Candidate (indices into shared ancestor slice) - Add with_ancestors() builder on CoinSelector - Add selected_ancestor_bump_fee() with package-level computation - Subtract ancestor bump fee from excess and effective_value methods Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d168359 to
ce09897
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When spending unconfirmed UTXOs, miners evaluate the transaction as a package with its unconfirmed ancestors. If ancestors paid below the target feerate, the child must overpay (CPFP). This PR adds support for computing that bump fee during coin selection, with automatic deduplication when multiple candidates share ancestors.
Closes #24. Competes with #38 and #42.
Design
Each ancestor is tracked individually via
UnconfirmedAncestor { weight, fee_paid }. Candidates declare their ancestry throughancestors: &[usize]— indices into a shared ancestor slice passed toCoinSelector::with_ancestors(). This keepsCandidateCopy(a slice reference isCopy).The bump fee is computed at the package level: collect unique ancestor indices across selected candidates, sum their weights and fees, then compute
max(0, implied_fee(total_weight, feerate) - total_fees). This is more accurate than per-ancestor computation because high-feerate ancestors subsidize low-feerate ones within the package (matching Bitcoin Core's package relay approach from PR #27021).Comparison with alternative approaches
#42 (
ancestor_bump_feefield onCandidate) pre-computes the bump fee per candidate. This is simple but has two drawbacks:This PR avoids both issues by storing raw ancestor data (
weight+fee_paid) and computing the bump fee lazily at the selection level, where the full set of selected candidates and the current feerate are known.#38 (
Packagestruct) uses a single aggregatePackage { parent_fee, parent_weight }blob. That works for the single-parent case but breaks down when candidates have different ancestors — there's no way to deduplicate shared ancestors as the selection changes. The per-ancestor model handles this naturally:Selecting both candidates → ancestor A counted once, not twice.
Where the bump fee lives
Rather than modifying
weight()orfee()(which would require_without_packageescape hatches for RBF), the bump fee is a separate term subtracted in the excess calculations. This keeps the core weight/fee accounting clean and avoids special-casing RBF rule 4.API changes
UnconfirmedAncestor— new struct:{ weight: u64, fee_paid: u64 }Candidate— gainsancestors: &'a [usize]field (set to&[]for confirmed UTXOs). This adds a lifetime parameter toCandidate<'a>.CoinSelector::with_ancestors(ancestors) -> Self— builder method, backward compatibleCoinSelector::selected_ancestor_bump_fee(feerate) -> u64— package-level bump fee with deduplicationrate_excess,rate_excess_wu,replacement_excess,replacement_excess_wu,effective_value— now subtract the ancestor bump feeExisting code only needs to add
ancestors: &[]toCandidatestruct literals.