@@ -2,7 +2,19 @@ use super::*;
22#[ allow( unused) ] // some bug in <= 1.48.0 sees this as unused when it isn't
33use crate :: float:: FloatExt ;
44use crate :: { bnb:: BnbMetric , float:: Ordf32 , ChangePolicy , FeeRate , Target } ;
5- use alloc:: { borrow:: Cow , collections:: BTreeSet } ;
5+ use alloc:: { borrow:: Cow , collections:: BTreeSet , vec:: Vec } ;
6+
7+ /// An unconfirmed ancestor transaction that may need a fee bump (CPFP).
8+ ///
9+ /// When spending unconfirmed UTXOs, miners evaluate the transaction as a package with its
10+ /// unconfirmed ancestors. If ancestors paid below the target feerate, the child must overpay.
11+ #[ derive( Debug , Clone , Copy ) ]
12+ pub struct UnconfirmedAncestor {
13+ /// The weight of the ancestor transaction in weight units.
14+ pub weight : u64 ,
15+ /// The fee already paid by the ancestor transaction in satoshis.
16+ pub fee_paid : u64 ,
17+ }
618
719/// [`CoinSelector`] selects/deselects coins from a set of canididate coins.
820///
@@ -14,6 +26,7 @@ use alloc::{borrow::Cow, collections::BTreeSet};
1426#[ derive( Debug , Clone ) ]
1527pub struct CoinSelector < ' a > {
1628 candidates : & ' a [ Candidate ] ,
29+ ancestors : & ' a [ UnconfirmedAncestor ] ,
1730 selected : Cow < ' a , BTreeSet < usize > > ,
1831 banned : Cow < ' a , BTreeSet < usize > > ,
1932 candidate_order : Cow < ' a , [ usize ] > ,
@@ -34,26 +47,35 @@ impl<'a> CoinSelector<'a> {
3447 pub fn new ( candidates : & ' a [ Candidate ] ) -> Self {
3548 Self {
3649 candidates,
50+ ancestors : & [ ] ,
3751 selected : Cow :: Owned ( Default :: default ( ) ) ,
3852 banned : Cow :: Owned ( Default :: default ( ) ) ,
3953 candidate_order : Cow :: Owned ( ( 0 ..candidates. len ( ) ) . collect ( ) ) ,
4054 }
4155 }
4256
57+ /// Set the shared ancestor data for CPFP bump fee calculations.
58+ ///
59+ /// Each [`Candidate`]'s `ancestors` field contains indices into this slice.
60+ pub fn with_ancestors ( mut self , ancestors : & ' a [ UnconfirmedAncestor ] ) -> Self {
61+ self . ancestors = ancestors;
62+ self
63+ }
64+
4365 /// Iterate over all the candidates in their currently sorted order. Each item has the original
4466 /// index with the candidate.
4567 pub fn candidates (
4668 & self ,
47- ) -> impl DoubleEndedIterator < Item = ( usize , Candidate ) > + ExactSizeIterator + ' _ {
69+ ) -> impl DoubleEndedIterator < Item = ( usize , & Candidate ) > + ExactSizeIterator + ' _ {
4870 self . candidate_order
4971 . iter ( )
50- . map ( move |i| ( * i, self . candidates [ * i] ) )
72+ . map ( move |i| ( * i, & self . candidates [ * i] ) )
5173 }
5274
5375 /// Get the candidate at `index`. `index` refers to its position in the original `candidates`
5476 /// slice passed into [`CoinSelector::new`].
55- pub fn candidate ( & self , index : usize ) -> Candidate {
56- self . candidates [ index]
77+ pub fn candidate ( & self , index : usize ) -> & Candidate {
78+ & self . candidates [ index]
5779 }
5880
5981 /// Deselect a candidate at `index`. `index` refers to its position in the original `candidates`
@@ -172,6 +194,36 @@ impl<'a> CoinSelector<'a> {
172194 + target_ouputs. output_weight_with_drain ( drain_weight)
173195 }
174196
197+ /// Compute the package-level ancestor bump fee for the current selection at the given feerate.
198+ ///
199+ /// This collects unique ancestor indices across all selected candidates, sums their weights
200+ /// and fees, then computes `max(0, implied_fee(total_weight, feerate) - total_fees)`.
201+ ///
202+ /// High-feerate ancestors subsidize low-feerate ones within the package (matching Bitcoin
203+ /// Core's package relay approach).
204+ pub fn selected_ancestor_bump_fee ( & self , feerate : FeeRate ) -> u64 {
205+ if self . ancestors . is_empty ( ) {
206+ return 0 ;
207+ }
208+ let mut indices: Vec < usize > = self
209+ . selected
210+ . iter ( )
211+ . flat_map ( |& i| self . candidates [ i] . ancestors . iter ( ) . copied ( ) )
212+ . collect ( ) ;
213+ indices. sort_unstable ( ) ;
214+ indices. dedup ( ) ;
215+
216+ let mut total_weight = 0u64 ;
217+ let mut total_fee_paid = 0u64 ;
218+ for anc_index in indices {
219+ let anc = & self . ancestors [ anc_index] ;
220+ total_weight += anc. weight ;
221+ total_fee_paid += anc. fee_paid ;
222+ }
223+ let implied = feerate. implied_fee ( total_weight) ;
224+ implied. saturating_sub ( total_fee_paid)
225+ }
226+
175227 /// How much the current selection overshoots the value needed to achieve `target`.
176228 ///
177229 /// In order for the resulting transaction to be valid this must be 0 or above. If it's above 0
@@ -199,6 +251,7 @@ impl<'a> CoinSelector<'a> {
199251 - target. value ( ) as i64
200252 - drain. value as i64
201253 - self . implied_fee_from_feerate ( target, drain. weights ) as i64
254+ - self . selected_ancestor_bump_fee ( target. fee . rate ) as i64
202255 }
203256
204257 /// Same as [rate_excess](Self::rate_excess) except `target.fee.rate` is applied to the
@@ -208,6 +261,7 @@ impl<'a> CoinSelector<'a> {
208261 - target. value ( ) as i64
209262 - drain. value as i64
210263 - self . implied_fee_from_feerate_wu ( target, drain. weights ) as i64
264+ - self . selected_ancestor_bump_fee ( target. fee . rate ) as i64
211265 }
212266
213267 /// How much the current selection overshoots the value needed to satisfy `target.fee.absolute`
@@ -230,6 +284,7 @@ impl<'a> CoinSelector<'a> {
230284 - target. value ( ) as i64
231285 - drain. value as i64
232286 - replacement_excess_needed as i64
287+ - self . selected_ancestor_bump_fee ( target. fee . rate ) as i64
233288 }
234289
235290 /// Same as [replacement_excess](Self::replacement_excess) except the replacement fee
@@ -244,6 +299,7 @@ impl<'a> CoinSelector<'a> {
244299 - target. value ( ) as i64
245300 - drain. value as i64
246301 - replacement_excess_needed as i64
302+ - self . selected_ancestor_bump_fee ( target. fee . rate ) as i64
247303 }
248304
249305 /// The feerate the transaction would have if we were to use this selection of inputs to achieve
@@ -304,8 +360,11 @@ impl<'a> CoinSelector<'a> {
304360 }
305361
306362 /// The value of the current selected inputs minus the fee needed to pay for the selected inputs
363+ /// and any ancestor bump fee.
307364 pub fn effective_value ( & self , feerate : FeeRate ) -> i64 {
308- self . selected_value ( ) as i64 - ( self . input_weight ( ) as f32 * feerate. spwu ( ) ) . ceil ( ) as i64
365+ self . selected_value ( ) as i64
366+ - ( self . input_weight ( ) as f32 * feerate. spwu ( ) ) . ceil ( ) as i64
367+ - self . selected_ancestor_bump_fee ( feerate) as i64
309368 }
310369
311370 // /// Waste sum of all selected inputs.
@@ -324,11 +383,11 @@ impl<'a> CoinSelector<'a> {
324383 /// [`unselected`]: CoinSelector::unselected
325384 pub fn sort_candidates_by < F > ( & mut self , mut cmp : F )
326385 where
327- F : FnMut ( ( usize , Candidate ) , ( usize , Candidate ) ) -> core:: cmp:: Ordering ,
386+ F : FnMut ( ( usize , & Candidate ) , ( usize , & Candidate ) ) -> core:: cmp:: Ordering ,
328387 {
329388 let order = self . candidate_order . to_mut ( ) ;
330389 let candidates = & self . candidates ;
331- order. sort_by ( |a, b| cmp ( ( * a, candidates[ * a] ) , ( * b, candidates[ * b] ) ) )
390+ order. sort_by ( |a, b| cmp ( ( * a, & candidates[ * a] ) , ( * b, & candidates[ * b] ) ) )
332391 }
333392
334393 /// Sorts the candidates by the key function.
@@ -342,10 +401,10 @@ impl<'a> CoinSelector<'a> {
342401 /// [`unselected`]: CoinSelector::unselected
343402 pub fn sort_candidates_by_key < F , K > ( & mut self , mut key_fn : F )
344403 where
345- F : FnMut ( ( usize , Candidate ) ) -> K ,
404+ F : FnMut ( ( usize , & Candidate ) ) -> K ,
346405 K : Ord ,
347406 {
348- self . sort_candidates_by ( |a, b| key_fn ( a) . cmp ( & key_fn ( b) ) )
407+ self . sort_candidates_by ( |a, b| key_fn ( a) . cmp ( & key_fn ( b) ) ) ;
349408 }
350409
351410 /// Sorts the candidates by descending value per weight unit, tie-breaking with value.
@@ -391,20 +450,20 @@ impl<'a> CoinSelector<'a> {
391450 /// The selected candidates with their index.
392451 pub fn selected (
393452 & self ,
394- ) -> impl ExactSizeIterator < Item = ( usize , Candidate ) > + DoubleEndedIterator + ' _ {
453+ ) -> impl ExactSizeIterator < Item = ( usize , & Candidate ) > + DoubleEndedIterator + ' _ {
395454 self . selected
396455 . iter ( )
397- . map ( move |& index| ( index, self . candidates [ index] ) )
456+ . map ( move |& index| ( index, & self . candidates [ index] ) )
398457 }
399458
400459 /// The unselected candidates with their index.
401460 ///
402461 /// The candidates are returned in sorted order. See [`sort_candidates_by`].
403462 ///
404463 /// [`sort_candidates_by`]: Self::sort_candidates_by
405- pub fn unselected ( & self ) -> impl DoubleEndedIterator < Item = ( usize , Candidate ) > + ' _ {
464+ pub fn unselected ( & self ) -> impl DoubleEndedIterator < Item = ( usize , & Candidate ) > + ' _ {
406465 self . unselected_indices ( )
407- . map ( move |i| ( i, self . candidates [ i] ) )
466+ . map ( move |i| ( i, & self . candidates [ i] ) )
408467 }
409468
410469 /// The indices of the selelcted candidates.
@@ -624,20 +683,23 @@ pub struct SelectIter<'a> {
624683}
625684
626685impl < ' a > Iterator for SelectIter < ' a > {
627- type Item = ( CoinSelector < ' a > , usize , Candidate ) ;
686+ type Item = ( CoinSelector < ' a > , usize , & ' a Candidate ) ;
628687
629688 fn next ( & mut self ) -> Option < Self :: Item > {
630- let ( index, wv) = self . cs . unselected ( ) . next ( ) ?;
689+ let index = self . cs . unselected_indices ( ) . next ( ) ?;
690+ // Access the underlying slice directly to get the `'a` lifetime.
691+ let candidates: & ' a [ Candidate ] = self . cs . candidates ;
631692 self . cs . select ( index) ;
632- Some ( ( self . cs . clone ( ) , index, wv ) )
693+ Some ( ( self . cs . clone ( ) , index, & candidates [ index ] ) )
633694 }
634695}
635696
636- impl DoubleEndedIterator for SelectIter < ' _ > {
697+ impl < ' a > DoubleEndedIterator for SelectIter < ' a > {
637698 fn next_back ( & mut self ) -> Option < Self :: Item > {
638- let ( index, wv) = self . cs . unselected ( ) . next_back ( ) ?;
699+ let index = self . cs . unselected_indices ( ) . next_back ( ) ?;
700+ let candidates: & ' a [ Candidate ] = self . cs . candidates ;
639701 self . cs . select ( index) ;
640- Some ( ( self . cs . clone ( ) , index, wv ) )
702+ Some ( ( self . cs . clone ( ) , index, & candidates [ index ] ) )
641703 }
642704}
643705
@@ -682,7 +744,7 @@ impl std::error::Error for NoBnbSolution {}
682744/// A `Candidate` represents an input candidate for [`CoinSelector`].
683745///
684746/// This can either be a single UTXO, or a group of UTXOs that should be spent together.
685- #[ derive( Debug , Clone , Copy ) ]
747+ #[ derive( Debug , Clone ) ]
686748pub struct Candidate {
687749 /// Total value of the UTXO(s) that this [`Candidate`] represents.
688750 pub value : u64 ,
@@ -694,6 +756,12 @@ pub struct Candidate {
694756 pub input_count : usize ,
695757 /// Whether this [`Candidate`] contains at least one segwit spend.
696758 pub is_segwit : bool ,
759+ /// Indices into the shared [`UnconfirmedAncestor`] slice (passed to
760+ /// [`CoinSelector::with_ancestors`]) that this candidate depends on.
761+ ///
762+ /// When multiple candidates share ancestors, those ancestors are automatically deduplicated
763+ /// during bump fee computation.
764+ pub ancestors : Vec < usize > ,
697765}
698766
699767impl Candidate {
@@ -707,13 +775,14 @@ impl Candidate {
707775 ///
708776 /// `satisfaction_weight` is the weight of `scriptSigLen + scriptSig + scriptWitnessLen +
709777 /// scriptWitness`.
710- pub fn new ( value : u64 , satisfaction_weight : u64 , is_segwit : bool ) -> Candidate {
778+ pub fn new ( value : u64 , satisfaction_weight : u64 , is_segwit : bool ) -> Self {
711779 let weight = TXIN_BASE_WEIGHT + satisfaction_weight;
712780 Candidate {
713781 value,
714782 weight,
715783 input_count : 1 ,
716784 is_segwit,
785+ ancestors : Vec :: new ( ) ,
717786 }
718787 }
719788
0 commit comments