@@ -11,35 +11,49 @@ use alloc::{borrow::Cow, collections::BTreeSet, vec::Vec};
1111///
1212/// [`select`]: CoinSelector::select
1313/// [`bnb_solutions`]: CoinSelector::bnb_solutions
14- #[ derive( Debug , Clone ) ]
15- pub struct CoinSelector < ' a > {
16- candidates : & ' a [ Candidate ] ,
14+ #[ derive( Debug ) ]
15+ pub struct CoinSelector < ' a , C > {
16+ inputs : & ' a [ C ] ,
17+ candidates : Cow < ' a , [ Candidate ] > ,
1718 selected : Cow < ' a , BTreeSet < usize > > ,
1819 banned : Cow < ' a , BTreeSet < usize > > ,
1920 candidate_order : Cow < ' a , Vec < usize > > ,
2021}
2122
22- impl < ' a > CoinSelector < ' a > {
23- /// Creates a new coin selector from some candidate inputs and a `base_weight`.
24- ///
25- /// The `base_weight` is the weight of the transaction without any inputs and without a change
26- /// output.
27- ///
28- /// The `CoinSelector` does not keep track of the final transaction's output count. The caller
29- /// is responsible for including the potential output-count varint weight change in the
30- /// corresponding [`DrainWeights`].
23+ impl < ' a , C > Clone for CoinSelector < ' a , C > {
24+ fn clone ( & self ) -> Self {
25+ Self {
26+ inputs : self . inputs ,
27+ candidates : self . candidates . clone ( ) ,
28+ selected : self . selected . clone ( ) ,
29+ banned : self . banned . clone ( ) ,
30+ candidate_order : self . candidate_order . clone ( ) ,
31+ }
32+ }
33+ }
34+
35+ impl < ' a , C > CoinSelector < ' a , C > {
36+ /// Create a coin selector from raw `inputs` and a `to_candidate` closure.
3137 ///
32- /// Note that methods in `CoinSelector` will refer to inputs by the index in the `candidates`
33- /// slice you pass in.
34- pub fn new ( candidates : & ' a [ Candidate ] ) -> Self {
38+ /// `to_candidate` maps each raw input to a [`Candidate`] representation.
39+ pub fn new < F > ( inputs : & ' a [ C ] , to_candidate : F ) -> Self
40+ where
41+ F : Fn ( & C ) -> Candidate ,
42+ {
3543 Self {
36- candidates,
44+ inputs,
45+ candidates : inputs. iter ( ) . map ( to_candidate) . collect ( ) ,
3746 selected : Cow :: Owned ( Default :: default ( ) ) ,
3847 banned : Cow :: Owned ( Default :: default ( ) ) ,
39- candidate_order : Cow :: Owned ( ( 0 ..candidates . len ( ) ) . collect ( ) ) ,
48+ candidate_order : Cow :: Owned ( ( 0 ..inputs . len ( ) ) . collect ( ) ) ,
4049 }
4150 }
4251
52+ /// Get a reference to the raw inputs.
53+ pub fn raw_inputs ( & self ) -> & [ C ] {
54+ self . inputs
55+ }
56+
4357 /// Iterate over all the candidates in their currently sorted order. Each item has the original
4458 /// index with the candidate.
4559 pub fn candidates (
@@ -62,11 +76,39 @@ impl<'a> CoinSelector<'a> {
6276 self . selected . to_mut ( ) . remove ( & index)
6377 }
6478
65- /// Convienince method to pick elements of a slice by the indexes that are currently selected.
66- /// Obviously the slice must represent the inputs ordered in the same way as when they were
67- /// passed to `Candidates::new`.
68- pub fn apply_selection < T > ( & self , candidates : & ' a [ T ] ) -> impl Iterator < Item = & ' a T > + ' _ {
69- self . selected . iter ( ) . map ( move |i| & candidates[ * i] )
79+ /// Apply the current coin selection.
80+ ///
81+ /// `apply_action` is a closure that is meant to construct an unsigned transaction based on the
82+ /// current selection. `apply_action` is a [`FnMut`] so it can mutate a structure of the
83+ /// caller's liking (most likely a transaction). The input is a [`FinishAction`], which conveys
84+ /// adding inputs or outputs.
85+ ///
86+ /// # Errors
87+ ///
88+ /// The selection must satisfy `target` otherwise an [`InsufficientFunds`] error is returned.
89+ pub fn finish < F > (
90+ self ,
91+ target : Target ,
92+ change_policy : ChangePolicy ,
93+ mut apply_action : F ,
94+ ) -> Result < ( ) , InsufficientFunds >
95+ where
96+ F : FnMut ( FinishAction < ' a , C > ) ,
97+ {
98+ let excess = self . excess ( target, Drain :: NONE ) ;
99+ if excess < 0 {
100+ let missing = excess. unsigned_abs ( ) ;
101+ return Err ( InsufficientFunds { missing } ) ;
102+ }
103+ let drain = self . drain ( target, change_policy) ;
104+ for i in self . selected . iter ( ) . copied ( ) {
105+ apply_action ( FinishAction :: Input ( & self . inputs [ i] ) ) ;
106+ }
107+ apply_action ( FinishAction :: TargetOutput ( target) ) ;
108+ if drain. is_some ( ) {
109+ apply_action ( FinishAction :: DrainOutput ( drain) ) ;
110+ }
111+ Ok ( ( ) )
70112 }
71113
72114 /// Select the input at `index`. `index` refers to its position in the original `candidates`
@@ -331,7 +373,7 @@ impl<'a> CoinSelector<'a> {
331373 let mut excess_waste = self . excess ( target, drain) . max ( 0 ) as f32 ;
332374 // we allow caller to discount this waste depending on how wasteful excess actually is
333375 // to them.
334- excess_waste *= excess_discount. max ( 0.0 ) . min ( 1.0 ) ;
376+ excess_waste *= excess_discount. clamp ( 0.0 , 1.0 ) ;
335377 waste += excess_waste;
336378 } else {
337379 waste +=
@@ -489,7 +531,7 @@ impl<'a> CoinSelector<'a> {
489531 #[ must_use]
490532 pub fn select_until (
491533 & mut self ,
492- mut predicate : impl FnMut ( & CoinSelector < ' a > ) -> bool ,
534+ mut predicate : impl FnMut ( & CoinSelector < ' a , C > ) -> bool ,
493535 ) -> Option < ( ) > {
494536 loop {
495537 if predicate ( & * self ) {
@@ -503,7 +545,7 @@ impl<'a> CoinSelector<'a> {
503545 }
504546
505547 /// Return an iterator that can be used to select candidates.
506- pub fn select_iter ( self ) -> SelectIter < ' a > {
548+ pub fn select_iter ( self ) -> SelectIter < ' a , C > {
507549 SelectIter { cs : self . clone ( ) }
508550 }
509551
@@ -517,7 +559,7 @@ impl<'a> CoinSelector<'a> {
517559 pub fn bnb_solutions < M : BnbMetric > (
518560 & self ,
519561 metric : M ,
520- ) -> impl Iterator < Item = Option < ( CoinSelector < ' a > , Ordf32 ) > > {
562+ ) -> impl Iterator < Item = Option < ( CoinSelector < ' a , C > , Ordf32 ) > > {
521563 crate :: bnb:: BnbIter :: new ( self . clone ( ) , metric)
522564 }
523565
@@ -545,7 +587,7 @@ impl<'a> CoinSelector<'a> {
545587 }
546588}
547589
548- impl < ' a > core:: fmt:: Display for CoinSelector < ' a > {
590+ impl < ' a , C > core:: fmt:: Display for CoinSelector < ' a , C > {
549591 fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
550592 write ! ( f, "[" ) ?;
551593 let mut candidates = self . candidates ( ) . peekable ( ) ;
@@ -572,12 +614,12 @@ impl<'a> core::fmt::Display for CoinSelector<'a> {
572614/// The `SelectIter` allows you to select candidates by calling [`Iterator::next`].
573615///
574616/// The [`Iterator::Item`] is a tuple of `(selector, last_selected_index, last_selected_candidate)`.
575- pub struct SelectIter < ' a > {
576- cs : CoinSelector < ' a > ,
617+ pub struct SelectIter < ' a , C > {
618+ cs : CoinSelector < ' a , C > ,
577619}
578620
579- impl < ' a > Iterator for SelectIter < ' a > {
580- type Item = ( CoinSelector < ' a > , usize , Candidate ) ;
621+ impl < ' a , C > Iterator for SelectIter < ' a , C > {
622+ type Item = ( CoinSelector < ' a , C > , usize , Candidate ) ;
581623
582624 fn next ( & mut self ) -> Option < Self :: Item > {
583625 let ( index, wv) = self . cs . unselected ( ) . next ( ) ?;
@@ -586,7 +628,7 @@ impl<'a> Iterator for SelectIter<'a> {
586628 }
587629}
588630
589- impl < ' a > DoubleEndedIterator for SelectIter < ' a > {
631+ impl < ' a , C > DoubleEndedIterator for SelectIter < ' a , C > {
590632 fn next_back ( & mut self ) -> Option < Self :: Item > {
591633 let ( index, wv) = self . cs . unselected ( ) . next_back ( ) ?;
592634 self . cs . select ( index) ;
@@ -632,6 +674,18 @@ impl core::fmt::Display for NoBnbSolution {
632674#[ cfg( feature = "std" ) ]
633675impl std:: error:: Error for NoBnbSolution { }
634676
677+ /// Action to apply on a transaction.
678+ ///
679+ /// This is used in [`CoinSelector::finish`] to populate a transaction with the current selection.
680+ pub enum FinishAction < ' a , C > {
681+ /// Input to add to the transaction.
682+ Input ( & ' a C ) ,
683+ /// Recipient output to add to the transaction.
684+ TargetOutput ( Target ) ,
685+ /// Drain (change) output to add to the transction.
686+ DrainOutput ( Drain ) ,
687+ }
688+
635689/// A `Candidate` represents an input candidate for [`CoinSelector`].
636690///
637691/// This can either be a single UTXO, or a group of UTXOs that should be spent together.
0 commit comments