@@ -17,6 +17,7 @@ pub struct CoinSelector<'a> {
1717 selected : Cow < ' a , BTreeSet < usize > > ,
1818 banned : Cow < ' a , BTreeSet < usize > > ,
1919 candidate_order : Cow < ' a , [ usize ] > ,
20+ package : Option < Package > ,
2021}
2122
2223impl < ' a > CoinSelector < ' a > {
@@ -37,9 +38,55 @@ impl<'a> CoinSelector<'a> {
3738 selected : Cow :: Owned ( Default :: default ( ) ) ,
3839 banned : Cow :: Owned ( Default :: default ( ) ) ,
3940 candidate_order : Cow :: Owned ( ( 0 ..candidates. len ( ) ) . collect ( ) ) ,
41+ package : None ,
4042 }
4143 }
4244
45+ /// Configures package-aware coin selection for CPFP (Child Pays for Parent) scenarios.
46+ ///
47+ /// When you need to bump the feerate of unconfirmed parent transactions, this method lets you
48+ /// specify the parent context and which candidates link to those parents.
49+ ///
50+ /// `package` describes the aggregate fee and weight of the parent transactions.
51+ /// `link_indices` are the indices of candidates that spend outputs from the parent
52+ /// transactions. These will be automatically selected since they must be included to create
53+ /// the child relationship.
54+ ///
55+ /// # Example
56+ ///
57+ /// ```
58+ /// # use bdk_coin_select::*;
59+ /// // Parent tx has 200 sats fee and 400 weight units
60+ /// let package = Package { parent_fee: 200, parent_weight: 400 };
61+ ///
62+ /// // Candidate at index 0 spends from the parent
63+ /// # let candidates = vec![Candidate::new_tr_keyspend(10_000)];
64+ /// let selector = CoinSelector::new(&candidates).with_package(package, [0]);
65+ ///
66+ /// // Index 0 is now selected
67+ /// assert!(selector.is_selected(0));
68+ /// ```
69+ pub fn with_package (
70+ mut self ,
71+ package : Package ,
72+ link_indices : impl IntoIterator < Item = usize > ,
73+ ) -> Self {
74+ self . package = Some ( package) ;
75+ for index in link_indices {
76+ self . select ( index) ;
77+ }
78+ self
79+ }
80+
81+ /// Returns the package context if set.
82+ ///
83+ /// See [`with_package`] for more information on package-aware coin selection.
84+ ///
85+ /// [`with_package`]: Self::with_package
86+ pub fn package ( & self ) -> Option < Package > {
87+ self . package
88+ }
89+
4390 /// Iterate over all the candidates in their currently sorted order. Each item has the original
4491 /// index with the candidate.
4592 pub fn candidates (
@@ -164,9 +211,29 @@ impl<'a> CoinSelector<'a> {
164211
165212 /// Current weight of transaction implied by the selection.
166213 ///
214+ /// When a [`Package`] is set, this includes the parent transaction weight. Use
215+ /// [`weight_without_package`] if you need the child transaction weight only.
216+ ///
167217 /// If you don't have any drain outputs (only target outputs) just set drain_weights to
168218 /// [`DrainWeights::NONE`].
219+ ///
220+ /// [`weight_without_package`]: Self::weight_without_package
169221 pub fn weight ( & self , target_ouputs : TargetOutputs , drain_weight : DrainWeights ) -> u64 {
222+ let child_weight = self . weight_without_package ( target_ouputs, drain_weight) ;
223+ match self . package {
224+ Some ( pkg) => child_weight + pkg. parent_weight ,
225+ None => child_weight,
226+ }
227+ }
228+
229+ /// Weight of the child transaction only, excluding any package parent weight.
230+ ///
231+ /// This is useful for RBF calculations where constraints apply to the child transaction only.
232+ pub fn weight_without_package (
233+ & self ,
234+ target_ouputs : TargetOutputs ,
235+ drain_weight : DrainWeights ,
236+ ) -> u64 {
170237 TX_FIXED_FIELD_WEIGHT
171238 + self . input_weight ( )
172239 + target_ouputs. output_weight_with_drain ( drain_weight)
@@ -210,11 +277,18 @@ impl<'a> CoinSelector<'a> {
210277 }
211278
212279 /// How much the current selection overshoots the value needed to satisfy RBF's rule 4.
280+ ///
281+ /// Note: RBF constraints apply to the child transaction only, so this method uses
282+ /// [`weight_without_package`] even when a package is set.
283+ ///
284+ /// [`weight_without_package`]: Self::weight_without_package
213285 pub fn replacement_excess ( & self , target : Target , drain : Drain ) -> i64 {
214286 let mut replacement_excess_needed = 0 ;
215287 if let Some ( replace) = target. fee . replace {
216- replacement_excess_needed =
217- replace. min_fee_to_do_replacement ( self . weight ( target. outputs , drain. weights ) )
288+ // RBF rule 4 applies to the child transaction only
289+ replacement_excess_needed = replace. min_fee_to_do_replacement (
290+ self . weight_without_package ( target. outputs , drain. weights ) ,
291+ )
218292 }
219293 self . selected_value ( ) as i64
220294 - target. value ( ) as i64
@@ -227,8 +301,10 @@ impl<'a> CoinSelector<'a> {
227301 pub fn replacement_excess_wu ( & self , target : Target , drain : Drain ) -> i64 {
228302 let mut replacement_excess_needed = 0 ;
229303 if let Some ( replace) = target. fee . replace {
230- replacement_excess_needed =
231- replace. min_fee_to_do_replacement_wu ( self . weight ( target. outputs , drain. weights ) )
304+ // RBF rule 4 applies to the child transaction only
305+ replacement_excess_needed = replace. min_fee_to_do_replacement_wu (
306+ self . weight_without_package ( target. outputs , drain. weights ) ,
307+ )
232308 }
233309 self . selected_value ( ) as i64
234310 - target. value ( ) as i64
@@ -239,15 +315,19 @@ impl<'a> CoinSelector<'a> {
239315 /// The feerate the transaction would have if we were to use this selection of inputs to achieve
240316 /// the `target`'s value and weight. It is essentially telling you what target feerate you currently have.
241317 ///
318+ /// When a [`Package`] is set, this returns the package feerate:
319+ /// `(parent_fee + child_fee) / (parent_weight + child_weight)`.
320+ ///
242321 /// Returns `None` if the feerate would be negative or infinity.
243322 pub fn implied_feerate ( & self , target_outputs : TargetOutputs , drain : Drain ) -> Option < FeeRate > {
244- let numerator =
245- self . selected_value ( ) as i64 - target_outputs. value_sum as i64 - drain. value as i64 ;
246- let denom = self . weight ( target_outputs, drain. weights ) ;
247- if numerator < 0 || denom == 0 {
323+ let total_fee = self . fee ( target_outputs. value_sum , drain. value ) ;
324+ let total_weight = self . weight ( target_outputs, drain. weights ) ;
325+ if total_fee < 0 || total_weight == 0 {
248326 return None ;
249327 }
250- Some ( FeeRate :: from_sat_per_wu ( numerator as f32 / denom as f32 ) )
328+ Some ( FeeRate :: from_sat_per_wu (
329+ total_fee as f32 / total_weight as f32 ,
330+ ) )
251331 }
252332
253333 /// The fee the current selection and `drain_weight` should pay to satisfy `target_fee`.
@@ -286,8 +366,25 @@ impl<'a> CoinSelector<'a> {
286366 /// The actual fee the selection would pay if it was used in a transaction that had
287367 /// `target_value` value for outputs and change output of `drain_value`.
288368 ///
369+ /// When a [`Package`] is set, this includes the parent transaction fee. Use
370+ /// [`fee_without_package`] if you need the child transaction fee only.
371+ ///
289372 /// This can be negative when the selection is invalid (outputs are greater than inputs).
373+ ///
374+ /// [`fee_without_package`]: Self::fee_without_package
290375 pub fn fee ( & self , target_value : u64 , drain_value : u64 ) -> i64 {
376+ let child_fee = self . fee_without_package ( target_value, drain_value) ;
377+ match self . package {
378+ Some ( pkg) => child_fee + pkg. parent_fee as i64 ,
379+ None => child_fee,
380+ }
381+ }
382+
383+ /// Fee of the child transaction only, excluding any package parent fee.
384+ ///
385+ /// This is useful when you need to know what the child transaction actually pays,
386+ /// separate from the package context.
387+ pub fn fee_without_package ( & self , target_value : u64 , drain_value : u64 ) -> i64 {
291388 self . selected_value ( ) as i64 - target_value as i64 - drain_value as i64
292389 }
293390
0 commit comments