-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a113d8a
commit 63b9fbf
Showing
3 changed files
with
199 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
//! This library provides efficient algorithms to compose a set of unspent transaction outputs | ||
//! (UTXOs). | ||
|
||
#[cfg(any(test, feature = "rand"))] | ||
use crate::Utxo; | ||
|
||
#[cfg(any(test, feature = "rand"))] | ||
use crate::spend::Spend; | ||
|
||
#[cfg(any(test, feature = "rand"))] | ||
use rand::{seq::SliceRandom, thread_rng}; | ||
|
||
/// Randomly select coins for the given target by shuffling the utxo pool and | ||
/// taking UTXOs until the given target is reached, or returns None if the target | ||
/// cannot be reached with the given utxo pool. | ||
/// Requires compilation with the "rand" feature. | ||
#[cfg(any(test, feature = "rand"))] | ||
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))] | ||
pub fn select_coins_srd<T: Utxo>(target: u64, utxo_pool: &mut [T], spend: Spend) -> Option<Vec<T>> { | ||
utxo_pool.shuffle(&mut thread_rng()); | ||
|
||
let mut sum = 0; | ||
|
||
let res = utxo_pool | ||
.iter() | ||
.take_while(|x| { | ||
if sum >= target { | ||
return false; | ||
} | ||
sum += spend.get_effective_value(*x); | ||
true | ||
}) | ||
.cloned() | ||
.collect::<Vec<T>>(); | ||
|
||
if sum >= target { | ||
return Some(res); | ||
} | ||
|
||
None | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::single_random_draw::select_coins_srd; | ||
pub use crate::spend::Spend; | ||
use crate::Utxo; | ||
|
||
// https://github.com/bitcoin/bitcoin/blob/b264410e012a61b103e1a03c43df4e17b9b75452/src/test/util/setup_common.h#L80 | ||
const CENT:u64 = 1_000_000; | ||
const WEIGHT:u64 = 1_000; | ||
const FEE_RATE:u64 = 10; | ||
|
||
const UTXO_POOL: [MinimalUtxo; 4] = [ | ||
MinimalUtxo { value: CENT, weight: WEIGHT }, | ||
MinimalUtxo { value: 2 * CENT, weight: WEIGHT }, | ||
MinimalUtxo { value: 3 * CENT, weight: WEIGHT }, | ||
MinimalUtxo { value: 4 * CENT, weight: WEIGHT }, | ||
]; | ||
|
||
#[derive(Clone, Debug, Eq, PartialEq)] | ||
struct MinimalUtxo { | ||
value: u64, | ||
weight: u64 | ||
} | ||
|
||
impl Utxo for MinimalUtxo { | ||
fn get_value(&self) -> u64 { | ||
self.value | ||
} | ||
|
||
fn get_weight(&self) -> u64 { | ||
self.weight | ||
} | ||
} | ||
|
||
#[test] | ||
fn select_coins_srd_no_solution() { | ||
let mut pool = UTXO_POOL.clone(); | ||
let spend = Spend::new(FEE_RATE); | ||
|
||
let utxo_match = select_coins_srd(11 * CENT, &mut pool, spend); | ||
assert!(utxo_match.is_none()); | ||
} | ||
|
||
#[test] | ||
fn select_coins_srd_effective_value_too_small() { | ||
let mut pool = vec![UTXO_POOL[0].clone()]; | ||
let spend = Spend::new(FEE_RATE); | ||
|
||
// This test will fail if SRD is using value instead of effective_value | ||
let utxo_match = select_coins_srd(CENT, &mut pool, spend); | ||
assert!(utxo_match.is_none()); | ||
} | ||
|
||
#[test] | ||
fn select_coins_srd_with_solution() { | ||
let mut pool = UTXO_POOL.clone(); | ||
let spend = Spend::new(FEE_RATE); | ||
|
||
let utxo_match = select_coins_srd(CENT, &mut pool, spend); | ||
utxo_match.expect("Did not properly randomly select coins"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/// This module provides functionality needed to spend a UTXO. | ||
|
||
#[cfg(any(test, feature = "rand"))] | ||
use crate::Utxo; | ||
|
||
/// Dynamic values required at the time a UTXO is spent. | ||
#[derive(Debug, PartialEq)] | ||
pub struct Spend { | ||
/// The fee_rate the participant chooses to spend. | ||
fee_rate: u64, | ||
} | ||
|
||
// Currently only single_random_draw uses Spend, however | ||
// in the future when other algos use Spend, then the rand | ||
// feature flag can be removed. | ||
#[cfg(any(test, feature = "rand"))] | ||
impl Spend { | ||
/// Create a new Spend type. | ||
pub fn new(fee_rate: u64) -> Spend { | ||
Spend { | ||
fee_rate | ||
} | ||
} | ||
|
||
/// the value that's left after subtracting the fee to spend the output. | ||
pub fn get_effective_value(&self, utxo: &impl Utxo) -> u64 { | ||
let fee = utxo.get_weight() * self.fee_rate; | ||
utxo.get_value() - fee | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::spend::*; | ||
|
||
// https://github.com/bitcoin/bitcoin/blob/b264410e012a61b103e1a03c43df4e17b9b75452/src/test/util/setup_common.h#L80 | ||
const CENT:u64 = 1_000_000; | ||
|
||
#[derive(Clone, Debug, Eq, PartialEq)] | ||
struct MinimalUtxo { | ||
value: u64, | ||
weight: u64 | ||
} | ||
|
||
impl Utxo for MinimalUtxo { | ||
fn get_value(&self) -> u64 { | ||
self.value | ||
} | ||
|
||
fn get_weight(&self) -> u64 { | ||
self.weight | ||
} | ||
} | ||
|
||
#[test] | ||
fn get_effective_value() { | ||
let utxo = MinimalUtxo { value: CENT, weight: 1_000 }; | ||
let spend = Spend::new(10); | ||
let effective_value = spend.get_effective_value(&utxo); | ||
|
||
// value - (weight * fee_rate) | ||
// 1_000_000 - (1_000 * 10) | ||
assert_eq!(990_000, effective_value); | ||
} | ||
} |