Skip to content

Commit 8b2e1bb

Browse files
committed
wip
1 parent 49f0705 commit 8b2e1bb

File tree

4 files changed

+32
-64
lines changed

4 files changed

+32
-64
lines changed

Cargo.toml

+9-9
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ readme = "README.md"
1414
rust-version = "1.63.0"
1515

1616
[dependencies]
17-
bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "7df5e7c1bcb4aaf3247f0b76591db9744f03425e" }
17+
bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "319e87c07c8aca3a4b56027ad3ba29ce359e760d" }
1818
rand = { version = "0.8.5", default-features = false, optional = true }
1919

2020
[dev-dependencies]
21-
bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "7df5e7c1bcb4aaf3247f0b76591db9744f03425e", features = ["arbitrary"] }
21+
bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "319e87c07c8aca3a4b56027ad3ba29ce359e760d", features = ["arbitrary"] }
2222
criterion = "0.3"
2323
bitcoin-coin-selection = {path = ".", features = ["rand"]}
2424
rand = "0.8.5"
@@ -31,10 +31,10 @@ name = "coin_selection"
3131
harness = false
3232

3333
[patch.crates-io]
34-
bitcoin_hashes = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "7df5e7c1bcb4aaf3247f0b76591db9744f03425e" }
35-
base58ck = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "7df5e7c1bcb4aaf3247f0b76591db9744f03425e" }
36-
bitcoin-internals = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "7df5e7c1bcb4aaf3247f0b76591db9744f03425e" }
37-
bitcoin-io = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "7df5e7c1bcb4aaf3247f0b76591db9744f03425e" }
38-
bitcoin-primitives = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "7df5e7c1bcb4aaf3247f0b76591db9744f03425e" }
39-
bitcoin-addresses = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "7df5e7c1bcb4aaf3247f0b76591db9744f03425e" }
40-
bitcoin-units = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "7df5e7c1bcb4aaf3247f0b76591db9744f03425e" }
34+
bitcoin_hashes = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "319e87c07c8aca3a4b56027ad3ba29ce359e760d" }
35+
base58ck = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "319e87c07c8aca3a4b56027ad3ba29ce359e760d" }
36+
bitcoin-internals = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "319e87c07c8aca3a4b56027ad3ba29ce359e760d" }
37+
bitcoin-io = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "319e87c07c8aca3a4b56027ad3ba29ce359e760d" }
38+
bitcoin-primitives = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "319e87c07c8aca3a4b56027ad3ba29ce359e760d" }
39+
bitcoin-addresses = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "319e87c07c8aca3a4b56027ad3ba29ce359e760d" }
40+
bitcoin-units = { git = "https://github.com/rust-bitcoin/rust-bitcoin.git", rev = "319e87c07c8aca3a4b56027ad3ba29ce359e760d" }

src/branch_and_bound.rs

+18-17
Original file line numberDiff line numberDiff line change
@@ -161,18 +161,18 @@ pub fn select_coins_bnb<Utxo: WeightedUtxo>(
161161
let mut index = 0;
162162
let mut backtrack;
163163

164-
let mut value = Amount::ZERO;
164+
let mut value = 0;
165165

166-
let mut current_waste: SignedAmount = SignedAmount::ZERO;
167-
let mut best_waste = SignedAmount::MAX_MONEY;
166+
let mut current_waste: i64 = 0;
167+
let mut best_waste: i64 = Amount::MAX_MONEY.to_sat() as i64;
168168

169169
let mut index_selection: Vec<usize> = vec![];
170170
let mut best_selection: Vec<usize> = vec![];
171171

172172
let upper_bound = target.checked_add(cost_of_change)?;
173173

174174
// Creates a tuple of (effective_value, waste, weighted_utxo)
175-
let mut w_utxos: Vec<(Amount, SignedAmount, &Utxo)> = weighted_utxos
175+
let mut w_utxos: Vec<(u64, i64, &Utxo)> = weighted_utxos
176176
.iter()
177177
// calculate effective_value and waste for each w_utxo.
178178
.map(|wu| (wu.effective_value(fee_rate), wu.waste(fee_rate, long_term_fee_rate), wu))
@@ -183,17 +183,17 @@ pub fn select_coins_bnb<Utxo: WeightedUtxo>(
183183
// filter out all effective_values that are negative.
184184
.filter(|(eff_val, _, _)| eff_val.is_positive())
185185
// all utxo effective_values are now positive (see previous step) - cast to unsigned.
186-
.map(|(eff_val, waste, wu)| (eff_val.to_unsigned().unwrap(), waste, wu))
186+
.map(|(eff_val, waste, wu)| (eff_val.to_sat() as u64, waste.to_sat(), wu))
187187
.collect();
188188

189189
// descending sort by effective_value using satisfaction weight as tie breaker.
190190
w_utxos.sort_by(|a, b| {
191191
b.0.cmp(&a.0).then(b.2.satisfaction_weight().cmp(&a.2.satisfaction_weight()))
192192
});
193193

194-
let mut available_value = w_utxos.clone().into_iter().map(|(ev, _, _)| ev).checked_sum()?;
194+
let mut available_value: u64 = w_utxos.clone().into_iter().map(|(ev, _, _)| ev).sum();
195195

196-
if available_value < target || target == Amount::ZERO {
196+
if available_value < target.to_sat() || target == Amount::ZERO {
197197
return None;
198198
}
199199

@@ -205,7 +205,7 @@ pub fn select_coins_bnb<Utxo: WeightedUtxo>(
205205
// unchecked_add is used here for performance. Before entering the search loop, all
206206
// utxos are summed and checked for overflow. Since there was no overflow then, any
207207
// subset of addition will not overflow.
208-
if available_value.unchecked_add(value) < target
208+
if available_value + value < target.to_sat()
209209
// Provides an upper bound on the excess value that is permissible.
210210
// Since value is lost when we create a change output due to increasing the size of the
211211
// transaction by an output (the change output), we accept solutions that may be
@@ -217,7 +217,7 @@ pub fn select_coins_bnb<Utxo: WeightedUtxo>(
217217
//
218218
// That is, the range includes solutions that exactly equal the target up to but not
219219
// including values greater than target + cost_of_change.
220-
|| value > upper_bound
220+
|| value > upper_bound.to_sat()
221221
// if current_waste > best_waste, then backtrack. However, only backtrack if
222222
// it's high fee_rate environment. During low fee environments, a utxo may
223223
// have negative waste, therefore adding more utxos in such an environment
@@ -228,12 +228,11 @@ pub fn select_coins_bnb<Utxo: WeightedUtxo>(
228228
}
229229
// * value meets or exceeds the target.
230230
// Record the solution and the waste then continue.
231-
else if value >= target {
231+
else if value >= target.to_sat() {
232232
backtrack = true;
233233

234-
let v = value.to_signed().ok()?;
235-
let t = target.to_signed().ok()?;
236-
let waste: SignedAmount = v.checked_sub(t)?;
234+
// TODO checked
235+
let waste: i64 = value as i64 - (target.to_sat() as i64);
237236
current_waste = current_waste.checked_add(waste)?;
238237

239238
// Check if index_selection is better than the previous known best, and
@@ -265,7 +264,9 @@ pub fn select_coins_bnb<Utxo: WeightedUtxo>(
265264
assert_eq!(index, *index_selection.last().unwrap());
266265
let (eff_value, utxo_waste, _) = w_utxos[index];
267266
current_waste = current_waste.checked_sub(utxo_waste)?;
268-
value = value.checked_sub(eff_value)?;
267+
268+
// TODO this checked
269+
value = value - eff_value;
269270
index_selection.pop().unwrap();
270271
}
271272
// * Add next node to the inclusion branch.
@@ -275,7 +276,7 @@ pub fn select_coins_bnb<Utxo: WeightedUtxo>(
275276
// unchecked sub is used her for performance.
276277
// The bounds for available_value are at most the sum of utxos
277278
// and at least zero.
278-
available_value = available_value.unchecked_sub(eff_value);
279+
available_value = available_value - eff_value;
279280

280281
// Check if we can omit the currently selected depending on if the last
281282
// was omitted. Therefore, check if index_selection has a previous one.
@@ -290,7 +291,7 @@ pub fn select_coins_bnb<Utxo: WeightedUtxo>(
290291

291292
// unchecked add is used here for performance. Since the sum of all utxo values
292293
// did not overflow, then any positive subset of the sum will not overflow.
293-
value = value.unchecked_add(eff_value);
294+
value = value - eff_value;
294295
}
295296
}
296297

@@ -305,7 +306,7 @@ pub fn select_coins_bnb<Utxo: WeightedUtxo>(
305306
fn index_to_utxo_list<Utxo: WeightedUtxo>(
306307
iterations: u32,
307308
index_list: Vec<usize>,
308-
wu: Vec<(Amount, SignedAmount, &Utxo)>,
309+
wu: Vec<(u64, i64, &Utxo)>,
309310
) -> Return<Utxo> {
310311
let mut result: Vec<_> = Vec::new();
311312
let list = index_list;

src/lib.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub use crate::single_random_draw::select_coins_srd;
2323
pub(crate) type Return<'a, Utxo> = Option<(u32, Vec<&'a Utxo>)>;
2424

2525
// https://github.com/bitcoin/bitcoin/blob/f722a9bd132222d9d5cd503b5af25c905b205cdb/src/wallet/coinselection.h#L20
26-
const CHANGE_LOWER: Amount = Amount::from_sat(50_000);
26+
const CHANGE_LOWER: u64 = 50_000;
2727

2828
// https://github.com/rust-bitcoin/rust-bitcoin/blob/35202ba51bef3236e6ed1007a0d2111265b6498c/bitcoin/src/blockdata/transaction.rs#L357
2929
const SEQUENCE_SIZE: u64 = 4;
@@ -58,8 +58,8 @@ pub trait WeightedUtxo {
5858
/// see also:
5959
/// <https://github.com/rust-bitcoin/rust-bitcoin/blob/59c806996ce18e88394eb4e2c265986c8d3a6620/bitcoin/src/blockdata/transaction.rs>
6060
fn effective_value(&self, fee_rate: FeeRate) -> Option<SignedAmount> {
61-
let signed_input_fee = self.calculate_fee(fee_rate)?.to_signed().ok()?;
62-
self.value().to_signed().ok()?.checked_sub(signed_input_fee)
61+
let signed_input_fee = self.calculate_fee(fee_rate)?.to_signed();
62+
self.value().to_signed().checked_sub(signed_input_fee)
6363
}
6464

6565
/// Computes the fee to spend this `Utxo`.
@@ -75,8 +75,8 @@ pub trait WeightedUtxo {
7575
/// The waste is the difference of the fee to spend this `Utxo` now compared with the expected
7676
/// fee to spend in the future (long_term_fee_rate).
7777
fn waste(&self, fee_rate: FeeRate, long_term_fee_rate: FeeRate) -> Option<SignedAmount> {
78-
let fee: SignedAmount = self.calculate_fee(fee_rate)?.to_signed().ok()?;
79-
let lt_fee: SignedAmount = self.calculate_fee(long_term_fee_rate)?.to_signed().ok()?;
78+
let fee: SignedAmount = self.calculate_fee(fee_rate)?.to_signed();
79+
let lt_fee: SignedAmount = self.calculate_fee(long_term_fee_rate)?.to_signed();
8080
fee.checked_sub(lt_fee)
8181
}
8282
}

src/single_random_draw.rs

-33
Original file line numberDiff line numberDiff line change
@@ -39,39 +39,6 @@ pub fn select_coins_srd<'a, R: rand::Rng + ?Sized, Utxo: WeightedUtxo>(
3939
weighted_utxos: &'a [Utxo],
4040
rng: &mut R,
4141
) -> Return<'a, Utxo> {
42-
if target > Amount::MAX_MONEY {
43-
return None;
44-
}
45-
46-
let mut result: Vec<_> = weighted_utxos.iter().collect();
47-
let mut origin = result.to_owned();
48-
origin.shuffle(rng);
49-
50-
result.clear();
51-
52-
let threshold = target + CHANGE_LOWER;
53-
let mut value = Amount::ZERO;
54-
55-
let mut iteration = 0;
56-
for w_utxo in origin {
57-
iteration += 1;
58-
let utxo_value = w_utxo.value();
59-
let utxo_weight = w_utxo.satisfaction_weight();
60-
let effective_value = effective_value(fee_rate, utxo_weight, utxo_value);
61-
62-
if let Some(e) = effective_value {
63-
if let Ok(v) = e.to_unsigned() {
64-
value += v;
65-
66-
result.push(w_utxo);
67-
68-
if value >= threshold {
69-
return Some((iteration, result));
70-
}
71-
}
72-
}
73-
}
74-
7542
None
7643
}
7744

0 commit comments

Comments
 (0)