@@ -11,35 +11,49 @@ use alloc::{borrow::Cow, collections::BTreeSet, vec::Vec};
11
11
///
12
12
/// [`select`]: CoinSelector::select
13
13
/// [`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 ] > ,
17
18
selected : Cow < ' a , BTreeSet < usize > > ,
18
19
banned : Cow < ' a , BTreeSet < usize > > ,
19
20
candidate_order : Cow < ' a , Vec < usize > > ,
20
21
}
21
22
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.
31
37
///
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
+ {
35
43
Self {
36
- candidates,
44
+ inputs,
45
+ candidates : inputs. iter ( ) . map ( to_candidate) . collect ( ) ,
37
46
selected : Cow :: Owned ( Default :: default ( ) ) ,
38
47
banned : Cow :: Owned ( Default :: default ( ) ) ,
39
- candidate_order : Cow :: Owned ( ( 0 ..candidates . len ( ) ) . collect ( ) ) ,
48
+ candidate_order : Cow :: Owned ( ( 0 ..inputs . len ( ) ) . collect ( ) ) ,
40
49
}
41
50
}
42
51
52
+ /// Get a reference to the raw inputs.
53
+ pub fn raw_inputs ( & self ) -> & [ C ] {
54
+ self . inputs
55
+ }
56
+
43
57
/// Iterate over all the candidates in their currently sorted order. Each item has the original
44
58
/// index with the candidate.
45
59
pub fn candidates (
@@ -62,11 +76,39 @@ impl<'a> CoinSelector<'a> {
62
76
self . selected . to_mut ( ) . remove ( & index)
63
77
}
64
78
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 ( ( ) )
70
112
}
71
113
72
114
/// Select the input at `index`. `index` refers to its position in the original `candidates`
@@ -331,7 +373,7 @@ impl<'a> CoinSelector<'a> {
331
373
let mut excess_waste = self . excess ( target, drain) . max ( 0 ) as f32 ;
332
374
// we allow caller to discount this waste depending on how wasteful excess actually is
333
375
// to them.
334
- excess_waste *= excess_discount. max ( 0.0 ) . min ( 1.0 ) ;
376
+ excess_waste *= excess_discount. clamp ( 0.0 , 1.0 ) ;
335
377
waste += excess_waste;
336
378
} else {
337
379
waste +=
@@ -489,7 +531,7 @@ impl<'a> CoinSelector<'a> {
489
531
#[ must_use]
490
532
pub fn select_until (
491
533
& mut self ,
492
- mut predicate : impl FnMut ( & CoinSelector < ' a > ) -> bool ,
534
+ mut predicate : impl FnMut ( & CoinSelector < ' a , C > ) -> bool ,
493
535
) -> Option < ( ) > {
494
536
loop {
495
537
if predicate ( & * self ) {
@@ -503,7 +545,7 @@ impl<'a> CoinSelector<'a> {
503
545
}
504
546
505
547
/// 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 > {
507
549
SelectIter { cs : self . clone ( ) }
508
550
}
509
551
@@ -517,7 +559,7 @@ impl<'a> CoinSelector<'a> {
517
559
pub fn bnb_solutions < M : BnbMetric > (
518
560
& self ,
519
561
metric : M ,
520
- ) -> impl Iterator < Item = Option < ( CoinSelector < ' a > , Ordf32 ) > > {
562
+ ) -> impl Iterator < Item = Option < ( CoinSelector < ' a , C > , Ordf32 ) > > {
521
563
crate :: bnb:: BnbIter :: new ( self . clone ( ) , metric)
522
564
}
523
565
@@ -545,7 +587,7 @@ impl<'a> CoinSelector<'a> {
545
587
}
546
588
}
547
589
548
- impl < ' a > core:: fmt:: Display for CoinSelector < ' a > {
590
+ impl < ' a , C > core:: fmt:: Display for CoinSelector < ' a , C > {
549
591
fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
550
592
write ! ( f, "[" ) ?;
551
593
let mut candidates = self . candidates ( ) . peekable ( ) ;
@@ -572,12 +614,12 @@ impl<'a> core::fmt::Display for CoinSelector<'a> {
572
614
/// The `SelectIter` allows you to select candidates by calling [`Iterator::next`].
573
615
///
574
616
/// 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 > ,
577
619
}
578
620
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 ) ;
581
623
582
624
fn next ( & mut self ) -> Option < Self :: Item > {
583
625
let ( index, wv) = self . cs . unselected ( ) . next ( ) ?;
@@ -586,7 +628,7 @@ impl<'a> Iterator for SelectIter<'a> {
586
628
}
587
629
}
588
630
589
- impl < ' a > DoubleEndedIterator for SelectIter < ' a > {
631
+ impl < ' a , C > DoubleEndedIterator for SelectIter < ' a , C > {
590
632
fn next_back ( & mut self ) -> Option < Self :: Item > {
591
633
let ( index, wv) = self . cs . unselected ( ) . next_back ( ) ?;
592
634
self . cs . select ( index) ;
@@ -632,6 +674,18 @@ impl core::fmt::Display for NoBnbSolution {
632
674
#[ cfg( feature = "std" ) ]
633
675
impl std:: error:: Error for NoBnbSolution { }
634
676
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
+
635
689
/// A `Candidate` represents an input candidate for [`CoinSelector`].
636
690
///
637
691
/// This can either be a single UTXO, or a group of UTXOs that should be spent together.
0 commit comments