11use core:: fmt:: { Debug , Display } ;
2- use std:: collections:: { BTreeMap , HashSet } ;
2+ use std:: collections:: HashSet ;
33use std:: vec:: Vec ;
44
5- use bdk_chain:: KeychainIndexed ;
6- use bdk_chain:: { local_chain:: LocalChain , Anchor , TxGraph } ;
75use bdk_coin_select:: float:: Ordf32 ;
86use bdk_coin_select:: metrics:: LowestFee ;
97use bdk_coin_select:: {
108 Candidate , ChangePolicy , CoinSelector , DrainWeights , FeeRate , NoBnbSolution , Target , TargetFee ,
119 TargetOutputs ,
1210} ;
13- use bitcoin:: { absolute , Amount , OutPoint , TxOut } ;
11+ use bitcoin:: { Amount , OutPoint , TxOut } ;
1412use miniscript:: bitcoin;
15- use miniscript:: { plan:: Assets , Descriptor , DescriptorPublicKey , ForEachKey } ;
1613
1714use crate :: { DefiniteDescriptor , Input , InputGroup , Output } ;
1815
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-
9916/// Parameters for creating tx.
10017#[ derive( Debug , Clone ) ]
10118pub struct CreateSelectionParams {
@@ -108,7 +25,6 @@ pub struct CreateSelectionParams {
10825 /// To derive change output.
10926 ///
11027 /// Will error if this is unsatisfiable descriptor.
111- ///
11228 pub change_descriptor : DefiniteDescriptor ,
11329
11430 /// Feerate target!
@@ -124,13 +40,37 @@ pub struct CreateSelectionParams {
12440 pub max_rounds : usize ,
12541}
12642
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+
12763/// Final selection of inputs and outputs.
12864#[ derive( Debug , Clone ) ]
12965pub struct Selection {
13066 /// Inputs in this selection.
13167 pub inputs : Vec < Input > ,
13268 /// Outputs in this selection.
13369 pub outputs : Vec < Output > ,
70+ }
71+
72+ /// Selection Metrics.
73+ pub struct SelectionMetrics {
13474 /// Selection score.
13575 pub score : Ordf32 ,
13676 /// Whether there is a change output in this selection.
@@ -159,7 +99,9 @@ impl Display for CreateSelectionError {
15999impl std:: error:: Error for CreateSelectionError { }
160100
161101/// TODO
162- pub fn create_selection ( params : CreateSelectionParams ) -> Result < Selection , CreateSelectionError > {
102+ pub fn create_selection (
103+ params : CreateSelectionParams ,
104+ ) -> Result < ( Selection , SelectionMetrics ) , CreateSelectionError > {
163105 fn convert_feerate ( feerate : bitcoin:: FeeRate ) -> bdk_coin_select:: FeeRate {
164106 FeeRate :: from_sat_per_wu ( feerate. to_sat_per_kwu ( ) as f32 / 1000.0 )
165107 }
@@ -239,23 +181,27 @@ pub fn create_selection(params: CreateSelectionParams) -> Result<Selection, Crea
239181 . map_err ( CreateSelectionError :: NoSolution ) ?;
240182
241183 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 ( ) ,
257205 } ,
258- score,
259- has_change : maybe_drain. is_some ( ) ,
260- } )
206+ ) )
261207}
0 commit comments