11use core:: fmt:: { Debug , Display } ;
22use std:: vec:: Vec ;
33
4- use bdk_coin_select:: float:: Ordf32 ;
5- use bdk_coin_select:: metrics:: LowestFee ;
6- use bdk_coin_select:: {
7- ChangePolicy , CoinSelector , DrainWeights , FeeRate , NoBnbSolution , Target , TargetFee ,
8- TargetOutputs ,
9- } ;
10- use bitcoin:: { absolute, transaction, Amount , TxOut } ;
4+ use bdk_coin_select:: FeeRate ;
5+ use bitcoin:: { absolute, transaction} ;
116use miniscript:: bitcoin;
127use miniscript:: psbt:: PsbtExt ;
138
14- use crate :: { DefiniteDescriptor , Finalizer , Input , InputCandidates , Output } ;
9+ use crate :: { Finalizer , Input , Output } ;
1510
1611const FALLBACK_SEQUENCE : bitcoin:: Sequence = bitcoin:: Sequence :: ENABLE_LOCKTIME_NO_RBF ;
1712
13+ pub ( crate ) fn cs_feerate ( feerate : bitcoin:: FeeRate ) -> bdk_coin_select:: FeeRate {
14+ FeeRate :: from_sat_per_wu ( feerate. to_sat_per_kwu ( ) as f32 / 1000.0 )
15+ }
16+
1817/// Final selection of inputs and outputs.
1918#[ derive( Debug , Clone ) ]
2019pub struct Selection {
@@ -24,45 +23,6 @@ pub struct Selection {
2423 pub outputs : Vec < Output > ,
2524}
2625
27- /// Parameters for creating tx.
28- #[ derive( Debug , Clone ) ]
29- pub struct SelectionParams {
30- /// To derive change output.
31- ///
32- /// Will error if this is unsatisfiable descriptor.
33- pub change_descriptor : DefiniteDescriptor ,
34-
35- /// Feerate target!
36- pub target_feerate : bitcoin:: FeeRate ,
37-
38- /// Uses `target_feerate` as a fallback.
39- pub long_term_feerate : Option < bitcoin:: FeeRate > ,
40-
41- /// Outputs that must be included.
42- pub target_outputs : Vec < Output > ,
43-
44- /// Max rounds of branch-and-bound.
45- /// TODO: Remove.
46- pub max_rounds : usize ,
47- }
48-
49- impl SelectionParams {
50- /// With default params.
51- pub fn new (
52- change_descriptor : DefiniteDescriptor ,
53- target_outputs : Vec < Output > ,
54- target_feerate : bitcoin:: FeeRate ,
55- ) -> Self {
56- Self {
57- change_descriptor,
58- target_feerate,
59- long_term_feerate : None ,
60- target_outputs,
61- max_rounds : 100_000 ,
62- }
63- }
64- }
65-
6626/// Parameters for creating a psbt.
6727#[ derive( Debug , Clone ) ]
6828pub struct PsbtParams {
@@ -76,6 +36,9 @@ pub struct PsbtParams {
7636 /// It is best practive to set this to the latest block height to avoid fee sniping.
7737 pub fallback_locktime : absolute:: LockTime ,
7838
39+ /// Yes.
40+ pub fallback_sequence : bitcoin:: Sequence ,
41+
7942 /// Recommended.
8043 pub mandate_full_tx_for_segwit_v0 : bool ,
8144}
@@ -85,40 +48,12 @@ impl Default for PsbtParams {
8548 Self {
8649 version : transaction:: Version :: TWO ,
8750 fallback_locktime : absolute:: LockTime :: ZERO ,
51+ fallback_sequence : FALLBACK_SEQUENCE ,
8852 mandate_full_tx_for_segwit_v0 : true ,
8953 }
9054 }
9155}
9256
93- /// Selection Metrics.
94- pub struct SelectionMetrics {
95- /// Selection score.
96- pub score : Ordf32 ,
97- /// Whether there is a change output in this selection.
98- pub has_change : bool ,
99- }
100-
101- /// When create_tx fails.
102- #[ derive( Debug ) ]
103- pub enum SelectionError {
104- /// No solution.
105- NoSolution ( NoBnbSolution ) ,
106- /// Cannot satisfy change descriptor.
107- CannotSatisfyChangeDescriptor ( miniscript:: Error ) ,
108- }
109-
110- impl Display for SelectionError {
111- fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
112- match self {
113- SelectionError :: NoSolution ( no_bnb_solution) => Display :: fmt ( & no_bnb_solution, f) ,
114- SelectionError :: CannotSatisfyChangeDescriptor ( error) => Display :: fmt ( & error, f) ,
115- }
116- }
117- }
118-
119- #[ cfg( feature = "std" ) ]
120- impl std:: error:: Error for SelectionError { }
121-
12257/// Occurs when creating a psbt fails.
12358#[ derive( Debug ) ]
12459pub enum CreatePsbtError {
@@ -160,100 +95,6 @@ impl core::fmt::Display for CreatePsbtError {
16095impl std:: error:: Error for CreatePsbtError { }
16196
16297impl Selection {
163- /// Constructor
164- pub fn new (
165- candidates : InputCandidates ,
166- params : SelectionParams ,
167- ) -> Result < ( Self , SelectionMetrics ) , SelectionError > {
168- fn convert_feerate ( feerate : bitcoin:: FeeRate ) -> bdk_coin_select:: FeeRate {
169- FeeRate :: from_sat_per_wu ( feerate. to_sat_per_kwu ( ) as f32 / 1000.0 )
170- }
171-
172- let cs_candidates = candidates. coin_select_candidates ( ) ;
173-
174- let target_feerate = convert_feerate ( params. target_feerate ) ;
175- let long_term_feerate =
176- convert_feerate ( params. long_term_feerate . unwrap_or ( params. target_feerate ) ) ;
177- println ! ( "target_feerate: {} sats/vb" , target_feerate. as_sat_vb( ) ) ;
178-
179- let target = Target {
180- fee : TargetFee :: from_feerate ( target_feerate) ,
181- outputs : TargetOutputs :: fund_outputs (
182- params
183- . target_outputs
184- . iter ( )
185- . map ( |output| ( output. txout ( ) . weight ( ) . to_wu ( ) , output. value . to_sat ( ) ) ) ,
186- ) ,
187- } ;
188-
189- let change_policy = ChangePolicy :: min_value_and_waste (
190- DrainWeights {
191- output_weight : ( TxOut {
192- script_pubkey : params. change_descriptor . script_pubkey ( ) ,
193- value : Amount :: ZERO ,
194- } )
195- . weight ( )
196- . to_wu ( ) ,
197- spend_weight : params
198- . change_descriptor
199- . max_weight_to_satisfy ( )
200- . map_err ( SelectionError :: CannotSatisfyChangeDescriptor ) ?
201- . to_wu ( ) ,
202- n_outputs : 1 ,
203- } ,
204- params
205- . change_descriptor
206- . script_pubkey ( )
207- . minimal_non_dust ( )
208- . to_sat ( ) ,
209- target_feerate,
210- long_term_feerate,
211- ) ;
212-
213- let bnb_metric = LowestFee {
214- target,
215- long_term_feerate,
216- change_policy,
217- } ;
218-
219- let mut selector = CoinSelector :: new ( cs_candidates) ;
220-
221- // Select input candidates that must be spent.
222- for index in 0 ..candidates. must_select_len ( ) {
223- selector. select ( index) ;
224- }
225-
226- // We assume that this still works if the current selection is already a solution.
227- let score = selector
228- . run_bnb ( bnb_metric, params. max_rounds )
229- . map_err ( SelectionError :: NoSolution ) ?;
230-
231- let maybe_drain = selector. drain ( target, change_policy) ;
232- Ok ( (
233- Selection {
234- inputs : selector
235- . apply_selection ( candidates. groups ( ) )
236- . flat_map ( |group| group. inputs ( ) )
237- . cloned ( )
238- . collect :: < Vec < Input > > ( ) ,
239- outputs : {
240- let mut outputs = params. target_outputs ;
241- if maybe_drain. is_some ( ) {
242- outputs. push ( Output :: with_descriptor (
243- params. change_descriptor ,
244- Amount :: from_sat ( maybe_drain. value ) ,
245- ) ) ;
246- }
247- outputs
248- } ,
249- } ,
250- SelectionMetrics {
251- score,
252- has_change : maybe_drain. is_some ( ) ,
253- } ,
254- ) )
255- }
256-
25798 /// Returns none if there is a mismatch of units in `locktimes`.
25899 ///
259100 /// As according to BIP-64...
@@ -298,7 +139,7 @@ impl Selection {
298139 . map ( |input| bitcoin:: TxIn {
299140 previous_output : input. prev_outpoint ( ) ,
300141 // TODO: Custom fallback sequence?
301- sequence : input. sequence ( ) . unwrap_or ( FALLBACK_SEQUENCE ) ,
142+ sequence : input. sequence ( ) . unwrap_or ( params . fallback_sequence ) ,
302143 ..Default :: default ( )
303144 } )
304145 . collect ( ) ,
0 commit comments