3
3
` bdk_coin_select ` is a tool to help you select inputs for making Bitcoin (ticker: BTC) transactions.
4
4
It's got zero dependencies so you can paste it into your project without concern.
5
5
6
+ > ⚠ This work is only ready to use by those who expect (potentially catastrophic) bugs and will have
7
+ > the time to investigate them and contribute back to this crate.
8
+
6
9
## Constructing the ` CoinSelector `
7
10
8
11
The main structure is [ ` CoinSelector ` ] ( crate::CoinSelector ) . To construct it, we specify a list of
@@ -11,18 +14,12 @@ and mandatory inputs (if any).
11
14
12
15
``` rust
13
16
use std :: str :: FromStr ;
14
- use bdk_coin_select :: { CoinSelector , Candidate , TR_KEYSPEND_SATISFACTION_WEIGHT , TXIN_BASE_WEIGHT };
17
+ use bdk_coin_select :: { CoinSelector , Candidate , TR_KEYSPEND_TXIN_WEIGHT };
15
18
use bitcoin :: { Address , Network , Transaction , TxIn , TxOut };
16
19
17
- // You should use miniscript to figure out the satisfaction weight for your coins!
18
- const TR_INPUT_WEIGHT : u32 = TXIN_BASE_WEIGHT + TR_KEYSPEND_SATISFACTION_WEIGHT ;
19
-
20
20
// The address where we want to send our coins.
21
21
let recipient_addr =
22
- Address :: from_str (" tb1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xq7hps46" )
23
- . expect (" address must be valid" )
24
- . require_network (Network :: Testnet )
25
- . expect (" network must match" );
22
+ Address :: from_str (" tb1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xq7hps46" ). unwrap ();
26
23
27
24
let candidates = vec! [
28
25
Candidate {
@@ -32,7 +29,8 @@ let candidates = vec![
32
29
// the value of the input
33
30
value : 1_000_000 ,
34
31
// the total weight of the input(s).
35
- weight : TR_INPUT_WEIGHT ,
32
+ // you may need to use miniscript to figure out the correct value here.
33
+ weight : TR_KEYSPEND_TXIN_WEIGHT ,
36
34
// wether it's a segwit input. Needed so we know whether to include the
37
35
// segwit header in total weight calculations.
38
36
is_segwit : true
@@ -41,7 +39,7 @@ let candidates = vec![
41
39
// A candidate can represent multiple inputs in the case where you
42
40
// always want some inputs to be spent together.
43
41
input_count : 2 ,
44
- weight : 2 * TR_INPUT_WEIGHT ,
42
+ weight : 2 * TR_KEYSPEND_TXIN_WEIGHT ,
45
43
value : 3_000_000 ,
46
44
is_segwit : true
47
45
}
@@ -52,7 +50,7 @@ let base_tx = Transaction {
52
50
// include your recipient outputs here
53
51
output : vec! [TxOut {
54
52
value : 900_000 ,
55
- script_pubkey : recipient_addr . script_pubkey (),
53
+ script_pubkey : recipient_addr . payload . script_pubkey (),
56
54
}],
57
55
lock_time : bitcoin :: absolute :: LockTime :: from_height (0 ). unwrap (),
58
56
version : 0x02 ,
@@ -67,30 +65,25 @@ coin_selector.select(0);
67
65
68
66
## Change Policy
69
67
70
- A change policy determines whether the drain output(s) should be in the final solution. A change
71
- policy is represented by a closure of signature ` Fn(&CoinSelector, Target) -> Drain ` . We provide 3
72
- built-in change policies; ` min_value ` , ` min_waste ` and ` min_value_and_waste ` (refer to the
73
- [ module-level docs] ( crate::change_policy ) for more).
68
+ A change policy determines whether the drain output(s) should be in the final solution. The
69
+ determination is simple: if the excess value is above a threshold then the drain should be added. To
70
+ construct a change policy you always provide ` DrainWeights ` which tell the coin selector the weight
71
+ cost of adding the drain. ` DrainWeights ` includes two weights. One is the weight of the drain
72
+ output(s). The other is the weight of spending the drain output later on (the input weight).
74
73
75
- Typically, to construct a change policy, the [ ` DrainWeights ` ] need to be provided. ` DrainWeights `
76
- includes two weights. One is the weight of the drain output(s). The other is the weight of spending
77
- the drain output later on (the input weight).
78
74
79
75
``` rust
80
- # use std :: str :: FromStr ;
81
- # use bdk_coin_select :: { CoinSelector , Candidate , DrainWeights , TXIN_BASE_WEIGHT };
82
- # use bitcoin :: { Address , Network , Transaction , TxIn , TxOut };
83
- use bdk_coin_select :: change_policy :: min_value;
84
- # const TR_SATISFACTION_WEIGHT : u32 = 66 ;
85
- # const TR_INPUT_WEIGHT : u32 = TXIN_BASE_WEIGHT + TR_SATISFACTION_WEIGHT ;
86
- # let base_tx = Transaction {
87
- # input : vec! [],
88
- # // include your recipient outputs here
89
- # output : vec! [],
90
- # lock_time : bitcoin :: absolute :: LockTime :: from_height (0 ). unwrap (),
91
- # version : 1 ,
92
- # };
93
- # let base_weight = base_tx . weight (). to_wu () as u32 ;
76
+ use std :: str :: FromStr ;
77
+ use bdk_coin_select :: {CoinSelector , Candidate , DrainWeights , TXIN_BASE_WEIGHT , ChangePolicy , TR_KEYSPEND_TXIN_WEIGHT };
78
+ use bitcoin :: {Address , Network , Transaction , TxIn , TxOut };
79
+ const TR_SATISFACTION_WEIGHT : u32 = 66 ;
80
+ let base_tx = Transaction {
81
+ input : vec! [],
82
+ output : vec! [/* include your recipient outputs here */ ],
83
+ lock_time : bitcoin :: absolute :: LockTime :: from_height (0 ). unwrap (),
84
+ version : 0x02 ,
85
+ };
86
+ let base_weight = base_tx . weight (). to_wu () as u32 ;
94
87
95
88
// The change output that may or may not be included in the final transaction.
96
89
let drain_addr =
@@ -114,12 +107,12 @@ println!("drain output weight: {}", drain_output_weight);
114
107
115
108
let drain_weights = DrainWeights {
116
109
output_weight : drain_output_weight ,
117
- spend_weight : TR_INPUT_WEIGHT ,
110
+ spend_weight : TR_KEYSPEND_TXIN_WEIGHT ,
118
111
};
119
112
120
113
// This constructs a change policy that creates change when the change value is
121
114
// greater than or equal to the dust limit.
122
- let change_policy = min_value (
115
+ let change_policy = ChangePolicy :: min_value (
123
116
drain_weights ,
124
117
drain_addr . script_pubkey (). dust_value (). to_sat (),
125
118
);
@@ -136,14 +129,13 @@ Built-in metrics are provided in the [`metrics`] submodule. Currently, only the
136
129
[ ` LowestFee ` ] ( metrics::LowestFee ) metric is considered stable.
137
130
138
131
``` rust
139
- use bdk_coin_select :: { Candidate , CoinSelector , FeeRate , Target };
132
+ use bdk_coin_select :: { Candidate , CoinSelector , FeeRate , Target , ChangePolicy };
140
133
use bdk_coin_select :: metrics :: LowestFee ;
141
- use bdk_coin_select :: change_policy :: min_value_and_waste;
142
- # let candidates = [];
143
- # let base_weight = 0 ;
144
- # let drain_weights = bdk_coin_select :: DrainWeights :: default ();
145
- # let dust_limit = 0 ;
146
- # let long_term_feerate = FeeRate :: default_min_relay_fee ();
134
+ let candidates = [];
135
+ let base_weight = 0 ;
136
+ let drain_weights = bdk_coin_select :: DrainWeights :: default ();
137
+ let dust_limit = 0 ;
138
+ let long_term_feerate = FeeRate :: default_min_relay_fee ();
147
139
148
140
let mut coin_selector = CoinSelector :: new (& candidates , base_weight );
149
141
@@ -156,9 +148,10 @@ let target = Target {
156
148
// We use a change policy that introduces a change output if doing so reduces
157
149
// the "waste" and that the change output's value is at least that of the
158
150
// `dust_limit`.
159
- let change_policy = min_value_and_waste (
151
+ let change_policy = ChangePolicy :: min_value_and_waste (
160
152
drain_weights ,
161
153
dust_limit ,
154
+ target . feerate,
162
155
long_term_feerate ,
163
156
);
164
157
@@ -168,7 +161,7 @@ let change_policy = min_value_and_waste(
168
161
let metric = LowestFee {
169
162
target ,
170
163
long_term_feerate ,
171
- change_policy : & change_policy ,
164
+ change_policy
172
165
};
173
166
174
167
// We run the branch and bound algorithm with a max round limit of 100,000.
@@ -180,10 +173,10 @@ match coin_selector.run_bnb(metric, 100_000) {
180
173
let selection = coin_selector
181
174
. apply_selection (& candidates )
182
175
. collect :: <Vec <_ >>();
183
- let change = change_policy ( & coin_selector , target );
176
+ let change = coin_selector . drain ( target , change_policy );
184
177
185
178
println! (" we selected {} inputs" , selection . len ());
186
- println! (" are we including the change output? {} " , change . is_some () );
179
+ println! (" We are including a change output of {} value (0 means not change) " , change . value );
187
180
}
188
181
};
189
182
```
@@ -199,26 +192,34 @@ match coin_selector.run_bnb(metric, 100_000) {
199
192
[ `Target` ] : crate::Target
200
193
201
194
``` rust
202
- use bdk_coin_select :: { CoinSelector , Candidate , DrainWeights , Target };
203
- use bdk_coin_select :: change_policy :: min_value ;
204
- use bitcoin :: { Amount , ScriptBuf , TxOut } ;
205
- # let base_weight = 0_ u32 ;
206
- # let drain_weights = DrainWeights :: new_tr_keyspend () ;
195
+ use bdk_coin_select :: {CoinSelector , Candidate , DrainWeights , Target , ChangePolicy , TR_KEYSPEND_TXIN_WEIGHT , Drain };
196
+ use bitcoin :: { Amount , TxOut , Address } ;
197
+ let base_weight = 0_ u32 ;
198
+ let drain_weights = DrainWeights :: new_tr_keyspend () ;
199
+ use core :: str :: FromStr ;
207
200
208
201
// A random target, as an example.
209
202
let target = Target {
210
203
value : 21_000 ,
211
204
.. Default :: default ()
212
205
};
213
- // A random drain policy, as an example.
214
- let drain_policy = min_value (drain_weights , 0 );
206
+ // Am arbitary drain policy, for the example.
207
+ let change_policy = ChangePolicy :: min_value (drain_weights , 1337 );
215
208
216
209
// This is a list of candidate txouts for coin selection. If a txout is picked,
217
210
// our transaction's input will spend it.
218
211
let candidate_txouts = vec! [
219
212
TxOut {
220
213
value : 100_000 ,
221
- script_pubkey : ScriptBuf :: new (),
214
+ script_pubkey : Address :: from_str (" bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr" ). unwrap (). payload. script_pubkey (),
215
+ },
216
+ TxOut {
217
+ value : 150_000 ,
218
+ script_pubkey : Address :: from_str (" bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh" ). unwrap (). payload. script_pubkey (),
219
+ },
220
+ TxOut {
221
+ value : 200_000 ,
222
+ script_pubkey : Address :: from_str (" bc1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzs9khxz8" ). unwrap (). payload. script_pubkey ()
222
223
}
223
224
];
224
225
// We transform the candidate txouts into something `CoinSelector` can
@@ -228,19 +229,19 @@ let candidates = candidate_txouts
228
229
. map (| txout | Candidate {
229
230
input_count : 1 ,
230
231
value : txout . value,
231
- weight : txout . weight () as u32 ,
232
+ weight : TR_KEYSPEND_TXIN_WEIGHT , // you need to figure out the weight of the txin somehow
232
233
is_segwit : txout . script_pubkey. is_witness_program (),
233
234
})
234
235
. collect :: <Vec <_ >>();
235
236
236
237
let mut selector = CoinSelector :: new (& candidates , base_weight );
237
238
let _result = selector
238
- . select_until_target_met (target , drain_policy ( & selector , target ));
239
+ . select_until_target_met (target , Drain :: none ( ));
239
240
240
241
// Determine what the drain output will be, based on our selection.
241
- let drain = drain_policy ( & selector , target );
242
+ let drain = selector . drain ( target , change_policy );
242
243
243
- // Check that selection is finished!
244
+ // In theory the target must always still be met at this point
244
245
assert! (selector . is_target_met (target , drain ));
245
246
246
247
// Get a list of coins that are selected.
@@ -252,7 +253,7 @@ assert_eq!(selected_coins.len(), 1);
252
253
253
254
# Minimum Supported Rust Version (MSRV)
254
255
255
- This library should compile with Rust 1.54.0.
256
+ This library is tested to compile on 1.54
256
257
257
258
To build with the MSRV, you will need to pin the following dependencies:
258
259
0 commit comments