1
1
use core:: fmt:: { Debug , Display } ;
2
- use std:: collections:: { BTreeMap , HashSet } ;
2
+ use std:: collections:: HashSet ;
3
3
use std:: vec:: Vec ;
4
4
5
- use bdk_chain:: KeychainIndexed ;
6
- use bdk_chain:: { local_chain:: LocalChain , Anchor , TxGraph } ;
7
5
use bdk_coin_select:: float:: Ordf32 ;
8
6
use bdk_coin_select:: metrics:: LowestFee ;
9
7
use bdk_coin_select:: {
10
8
Candidate , ChangePolicy , CoinSelector , DrainWeights , FeeRate , NoBnbSolution , Target , TargetFee ,
11
9
TargetOutputs ,
12
10
} ;
13
- use bitcoin:: { absolute , Amount , OutPoint , TxOut } ;
11
+ use bitcoin:: { Amount , OutPoint , TxOut } ;
14
12
use miniscript:: bitcoin;
15
- use miniscript:: { plan:: Assets , Descriptor , DescriptorPublicKey , ForEachKey } ;
16
13
17
14
use crate :: { DefiniteDescriptor , Input , InputGroup , Output } ;
18
15
19
- /// Error
20
- #[ derive( Debug ) ]
21
- pub enum GetCandidateInputsError < K > {
22
- /// Descriptor is missing for keychain K.
23
- MissingDescriptor ( K ) ,
24
- /// Cannot plan descriptor. Missing assets?
25
- CannotPlan ( DefiniteDescriptor ) ,
26
- }
27
-
28
- impl < K : Debug > Display for GetCandidateInputsError < K > {
29
- fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
30
- match self {
31
- GetCandidateInputsError :: MissingDescriptor ( k) => {
32
- write ! ( f, "missing descriptor for keychain {:?}" , k)
33
- }
34
- GetCandidateInputsError :: CannotPlan ( descriptor) => {
35
- write ! ( f, "cannot plan input with descriptor {}" , descriptor)
36
- }
37
- }
38
- }
39
- }
40
-
41
- #[ cfg( feature = "std" ) ]
42
- impl < K : Debug > std:: error:: Error for GetCandidateInputsError < K > { }
43
-
44
- /// Get candidate inputs.
45
- ///
46
- /// This does not do any UTXO filtering or grouping.
47
- pub fn get_candidate_inputs < A : Anchor , K : Clone + Ord + core:: fmt:: Debug > (
48
- tx_graph : & TxGraph < A > ,
49
- chain : & LocalChain ,
50
- outpoints : impl IntoIterator < Item = KeychainIndexed < K , OutPoint > > ,
51
- owned_descriptors : BTreeMap < K , Descriptor < DescriptorPublicKey > > ,
52
- additional_assets : Assets ,
53
- ) -> Result < Vec < Input > , GetCandidateInputsError < K > > {
54
- let tip = chain. tip ( ) . block_id ( ) ;
55
-
56
- let mut pks = vec ! [ ] ;
57
- for desc in owned_descriptors. values ( ) {
58
- desc. for_each_key ( |k| {
59
- pks. extend ( k. clone ( ) . into_single_keys ( ) ) ;
60
- true
61
- } ) ;
62
- }
63
-
64
- let assets = Assets :: new ( )
65
- . after ( absolute:: LockTime :: from_height ( tip. height ) . expect ( "must be valid height" ) )
66
- . add ( pks)
67
- . add ( additional_assets) ;
68
-
69
- tx_graph
70
- . filter_chain_unspents ( chain, tip, outpoints)
71
- . map (
72
- move |( ( k, i) , txo) | -> Result < _ , GetCandidateInputsError < K > > {
73
- let descriptor = owned_descriptors
74
- . get ( & k)
75
- . ok_or ( GetCandidateInputsError :: MissingDescriptor ( k) ) ?
76
- . at_derivation_index ( i)
77
- // TODO: Is this safe?
78
- . expect ( "derivation index must not overflow" ) ;
79
-
80
- let plan = match descriptor. desc_type ( ) . segwit_version ( ) {
81
- Some ( _) => descriptor. plan ( & assets) ,
82
- None => descriptor. plan_mall ( & assets) ,
83
- }
84
- . map_err ( GetCandidateInputsError :: CannotPlan ) ?;
85
-
86
- // BDK cannot spend from floating txouts so we will always have the full tx.
87
- let tx = tx_graph
88
- . get_tx ( txo. outpoint . txid )
89
- . expect ( "must have full tx" ) ;
90
-
91
- let input = Input :: from_prev_tx ( plan, tx, txo. outpoint . vout as _ )
92
- . expect ( "tx must have output" ) ;
93
- Ok ( input)
94
- } ,
95
- )
96
- . collect ( )
97
- }
98
-
99
16
/// Parameters for creating tx.
100
17
#[ derive( Debug , Clone ) ]
101
18
pub struct CreateSelectionParams {
@@ -108,7 +25,6 @@ pub struct CreateSelectionParams {
108
25
/// To derive change output.
109
26
///
110
27
/// Will error if this is unsatisfiable descriptor.
111
- ///
112
28
pub change_descriptor : DefiniteDescriptor ,
113
29
114
30
/// Feerate target!
@@ -124,13 +40,37 @@ pub struct CreateSelectionParams {
124
40
pub max_rounds : usize ,
125
41
}
126
42
43
+ impl CreateSelectionParams {
44
+ /// With default params.
45
+ pub fn new (
46
+ input_candidates : Vec < InputGroup > ,
47
+ change_descriptor : DefiniteDescriptor ,
48
+ target_outputs : Vec < Output > ,
49
+ target_feerate : bitcoin:: FeeRate ,
50
+ ) -> Self {
51
+ Self {
52
+ input_candidates,
53
+ must_spend : HashSet :: new ( ) ,
54
+ change_descriptor,
55
+ target_feerate,
56
+ long_term_feerate : None ,
57
+ target_outputs,
58
+ max_rounds : 100_000 ,
59
+ }
60
+ }
61
+ }
62
+
127
63
/// Final selection of inputs and outputs.
128
64
#[ derive( Debug , Clone ) ]
129
65
pub struct Selection {
130
66
/// Inputs in this selection.
131
67
pub inputs : Vec < Input > ,
132
68
/// Outputs in this selection.
133
69
pub outputs : Vec < Output > ,
70
+ }
71
+
72
+ /// Selection Metrics.
73
+ pub struct SelectionMetrics {
134
74
/// Selection score.
135
75
pub score : Ordf32 ,
136
76
/// Whether there is a change output in this selection.
@@ -159,7 +99,9 @@ impl Display for CreateSelectionError {
159
99
impl std:: error:: Error for CreateSelectionError { }
160
100
161
101
/// TODO
162
- pub fn create_selection ( params : CreateSelectionParams ) -> Result < Selection , CreateSelectionError > {
102
+ pub fn create_selection (
103
+ params : CreateSelectionParams ,
104
+ ) -> Result < ( Selection , SelectionMetrics ) , CreateSelectionError > {
163
105
fn convert_feerate ( feerate : bitcoin:: FeeRate ) -> bdk_coin_select:: FeeRate {
164
106
FeeRate :: from_sat_per_wu ( feerate. to_sat_per_kwu ( ) as f32 / 1000.0 )
165
107
}
@@ -239,23 +181,27 @@ pub fn create_selection(params: CreateSelectionParams) -> Result<Selection, Crea
239
181
. map_err ( CreateSelectionError :: NoSolution ) ?;
240
182
241
183
let maybe_drain = selector. drain ( target, change_policy) ;
242
- Ok ( Selection {
243
- inputs : selector
244
- . apply_selection ( & must_spend. into_iter ( ) . chain ( may_spend) . collect :: < Vec < _ > > ( ) )
245
- . flat_map ( |group| group. inputs ( ) )
246
- . cloned ( )
247
- . collect :: < Vec < Input > > ( ) ,
248
- outputs : {
249
- let mut outputs = params. target_outputs ;
250
- if maybe_drain. is_some ( ) {
251
- outputs. push ( Output :: with_descriptor (
252
- params. change_descriptor ,
253
- Amount :: from_sat ( maybe_drain. value ) ,
254
- ) ) ;
255
- }
256
- outputs
184
+ Ok ( (
185
+ Selection {
186
+ inputs : selector
187
+ . apply_selection ( & must_spend. into_iter ( ) . chain ( may_spend) . collect :: < Vec < _ > > ( ) )
188
+ . flat_map ( |group| group. inputs ( ) )
189
+ . cloned ( )
190
+ . collect :: < Vec < Input > > ( ) ,
191
+ outputs : {
192
+ let mut outputs = params. target_outputs ;
193
+ if maybe_drain. is_some ( ) {
194
+ outputs. push ( Output :: with_descriptor (
195
+ params. change_descriptor ,
196
+ Amount :: from_sat ( maybe_drain. value ) ,
197
+ ) ) ;
198
+ }
199
+ outputs
200
+ } ,
201
+ } ,
202
+ SelectionMetrics {
203
+ score,
204
+ has_change : maybe_drain. is_some ( ) ,
257
205
} ,
258
- score,
259
- has_change : maybe_drain. is_some ( ) ,
260
- } )
206
+ ) )
261
207
}
0 commit comments