Skip to content

Commit 0c30cdf

Browse files
committed
draft: move weighted_utxo to seperate module
1 parent af5f8d6 commit 0c30cdf

File tree

3 files changed

+18
-89
lines changed

3 files changed

+18
-89
lines changed

src/branch_and_bound.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,11 @@ pub fn branch_and_bound<'a, T: IntoIterator<Item = &'a WeightedUtxo> + std::mark
171171
let mut weighted_utxos: Vec<_> = weighted_utxos.into_iter().collect();
172172

173173
// descending sort by effective_value, ascending sort by waste.
174-
weighted_utxos
175-
.sort_by(|a, b| b.effective_value().cmp(&a.effective_value()).then(a.waste.cmp(&b.waste)));
174+
weighted_utxos.sort_by(|a, b| {
175+
b.effective_value_raw()
176+
.cmp(&a.effective_value_raw())
177+
.then(a.waste_raw().cmp(&b.waste_raw()))
178+
});
176179

177180
if available_value < target {
178181
return Err(InsufficentFunds);
@@ -247,13 +250,13 @@ pub fn branch_and_bound<'a, T: IntoIterator<Item = &'a WeightedUtxo> + std::mark
247250
break;
248251
}
249252

250-
let eff_value = weighted_utxos[index].effective_value;
253+
let eff_value = weighted_utxos[index].effective_value_raw();
251254
available_value += eff_value;
252255
}
253256

254257
assert_eq!(index, *index_selection.last().unwrap());
255-
let eff_value = weighted_utxos[index].effective_value;
256-
let utxo_waste = weighted_utxos[index].waste;
258+
let eff_value = weighted_utxos[index].effective_value_raw();
259+
let utxo_waste = weighted_utxos[index].waste_raw();
257260
let utxo_weight = weighted_utxos[index].weight();
258261
current_waste = current_waste.checked_sub(utxo_waste).ok_or(Overflow(Subtraction))?;
259262
value = value.checked_sub(eff_value).ok_or(Overflow(Addition))?;
@@ -262,9 +265,9 @@ pub fn branch_and_bound<'a, T: IntoIterator<Item = &'a WeightedUtxo> + std::mark
262265
}
263266
// * Add next node to the inclusion branch.
264267
else {
265-
let eff_value = weighted_utxos[index].effective_value;
268+
let eff_value = weighted_utxos[index].effective_value_raw();
266269
let utxo_weight = weighted_utxos[index].weight();
267-
let utxo_waste = weighted_utxos[index].waste;
270+
let utxo_waste = weighted_utxos[index].waste_raw();
268271

269272
// unchecked sub is used her for performance.
270273
// The bounds for available_value are at most the sum of utxos
@@ -277,7 +280,7 @@ pub fn branch_and_bound<'a, T: IntoIterator<Item = &'a WeightedUtxo> + std::mark
277280
// Check if the previous UTXO was included.
278281
|| index - 1 == *index_selection.last().unwrap()
279282
// Check if the previous UTXO has the same value has the previous one.
280-
|| weighted_utxos[index].effective_value != weighted_utxos[index - 1].effective_value
283+
|| weighted_utxos[index].effective_value_raw() != weighted_utxos[index - 1].effective_value_raw()
281284
{
282285
index_selection.push(index);
283286
current_waste = current_waste.checked_add(utxo_waste).ok_or(Overflow(Addition))?;

src/coin_grinder.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,7 @@ mod tests {
672672
let w = u.int_in_range::<u64>(1..=Weight::MAX.to_wu()).unwrap();
673673
let wu = Weight::from_wu(w);
674674
WeightedUtxo::new(
675-
utxo.value,
675+
utxo.value(),
676676
wu,
677677
exclusion_set.fee_rate,
678678
exclusion_set.long_term_fee_rate,
@@ -685,14 +685,14 @@ mod tests {
685685
.iter()
686686
.map(|utxo| {
687687
WeightedUtxo::new(
688-
utxo.value,
688+
utxo.value(),
689689
Weight::ZERO,
690690
inclusion_set.fee_rate,
691691
inclusion_set.long_term_fee_rate,
692692
)
693693
.unwrap()
694694
})
695-
.filter(|utxo| utxo.value == Amount::ZERO)
695+
.filter(|utxo| utxo.value() == Amount::ZERO)
696696
.collect();
697697

698698
if let Some(target) = weightless_pool.iter().map(|utxo| utxo.value()).checked_sum() {

src/lib.rs

Lines changed: 4 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ mod branch_and_bound;
1515
mod coin_grinder;
1616
mod single_random_draw;
1717

18+
mod weighted_utxo;
19+
20+
pub use crate::weighted_utxo::WeightedUtxo;
21+
1822
/// Possible returned error types if no match is found.
1923
pub mod errors;
2024

21-
use std::cmp::Ordering;
22-
2325
use bitcoin_units::{Amount, FeeRate, SignedAmount, Weight};
2426
#[cfg(feature = "rand")]
2527
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
@@ -60,82 +62,6 @@ pub(crate) fn effective_value(
6062
Some(eff_value)
6163
}
6264

63-
#[derive(Debug, Clone, PartialEq, Eq)]
64-
/// Represents the spendable conditions of a `UTXO`.
65-
pub struct WeightedUtxo {
66-
/// The `Amount` that the output contributes towards the selection target.
67-
value: Amount,
68-
/// The estimated `Weight` (satisfaction weight + base weight) of the output.
69-
weight: Weight,
70-
/// The positive effective value `(value - fee)`. This value is stored as a `u64` for
71-
/// better performance.
72-
effective_value: u64,
73-
/// The `SignedAmount` required to spend the output at the given `fee_rate`.
74-
fee: SignedAmount,
75-
/// The `SignedAmount` required to spend the output at the given `long_term_fee_rate`.
76-
long_term_fee: SignedAmount,
77-
/// A metric for how wasteful it is to spend this `WeightedUtxo` given the current fee
78-
/// environment.
79-
waste: i64,
80-
}
81-
82-
impl WeightedUtxo {
83-
/// Creates a new `WeightedUtxo`.
84-
pub fn new(
85-
value: Amount,
86-
weight: Weight,
87-
fee_rate: FeeRate,
88-
long_term_fee_rate: FeeRate,
89-
) -> Option<WeightedUtxo> {
90-
let positive_effective_value = Self::positive_effective_value(fee_rate, weight, value);
91-
92-
if let Some(effective_value) = positive_effective_value {
93-
let fee = fee_rate.fee_wu(weight)?.to_signed();
94-
let long_term_fee: SignedAmount = long_term_fee_rate.fee_wu(weight)?.to_signed();
95-
let waste = Self::calculate_waste_score(fee, long_term_fee);
96-
return Some(Self { value, weight, effective_value, fee, long_term_fee, waste });
97-
}
98-
99-
None
100-
}
101-
102-
/// Calculates if the current fee environment is expensive.
103-
pub fn is_fee_expensive(&self) -> bool { self.fee > self.long_term_fee }
104-
105-
/// Returns the associated value.
106-
pub fn value(&self) -> Amount { self.value }
107-
108-
/// Returns the associated weight.
109-
pub fn weight(&self) -> Weight { self.weight }
110-
111-
/// Returns the calculated effective value.
112-
pub fn effective_value(&self) -> Amount { Amount::from_sat(self.effective_value).unwrap() }
113-
114-
fn positive_effective_value(fee_rate: FeeRate, weight: Weight, value: Amount) -> Option<u64> {
115-
if let Some(eff_value) = effective_value(fee_rate, weight, value) {
116-
if let Ok(unsigned) = eff_value.to_unsigned() {
117-
return Some(unsigned.to_sat());
118-
}
119-
}
120-
121-
None
122-
}
123-
124-
fn calculate_waste_score(fee: SignedAmount, long_term_fee: SignedAmount) -> i64 {
125-
fee.to_sat() - long_term_fee.to_sat()
126-
}
127-
}
128-
129-
impl Ord for WeightedUtxo {
130-
fn cmp(&self, other: &Self) -> Ordering {
131-
other.effective_value.cmp(&self.effective_value).then(self.weight.cmp(&other.weight))
132-
}
133-
}
134-
135-
impl PartialOrd for WeightedUtxo {
136-
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
137-
}
138-
13965
/// Attempt a match with [`branch_and_bound`] falling back to [`single_random_draw`].
14066
///
14167
/// If [`branch_and_bound`] fails to find a changeless solution (basically, an exact match), then

0 commit comments

Comments
 (0)