Consolidate fee_amount
and amount_needed
Before this commit `fee_amount` and `amount_needed` were passed as independent parameters. From the perspective of coin selection algorithms, they are always used jointly for the same purpose, to create a coin selection with a total effective value greater than it's summed values. This commit removes the abstraction that the use of the two parameter introduced by consolidating both into a single parameter, `target_amount`, who carries their values added up.
This commit is contained in:
parent
1730e0150f
commit
e8df3d2d91
@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Change the interface of `SqliteDatabase::new` to accept any type that implement AsRef<Path>
|
- Change the interface of `SqliteDatabase::new` to accept any type that implement AsRef<Path>
|
||||||
- Add the ability to specify which leaves to sign in a taproot transaction through `TapLeavesOptions` in `SignOptions`
|
- Add the ability to specify which leaves to sign in a taproot transaction through `TapLeavesOptions` in `SignOptions`
|
||||||
- Add the ability to specify whether a taproot transaction should be signed using the internal key or not, using `sign_with_tap_internal_key` in `SignOptions`
|
- Add the ability to specify whether a taproot transaction should be signed using the internal key or not, using `sign_with_tap_internal_key` in `SignOptions`
|
||||||
|
- Consolidate params `fee_amount` and `amount_needed` in `target_amount` in `CoinSelectionAlgorithm::coin_select` signature.
|
||||||
|
- Change the meaning of the `fee_amount` field inside `CoinSelectionResult`: from now on the `fee_amount` will represent only the fees asociated with the utxos in the `selected` field of `CoinSelectionResult`.
|
||||||
|
|
||||||
## [v0.20.0] - [v0.19.0]
|
## [v0.20.0] - [v0.19.0]
|
||||||
|
|
||||||
|
@ -41,8 +41,7 @@
|
|||||||
//! required_utxos: Vec<WeightedUtxo>,
|
//! required_utxos: Vec<WeightedUtxo>,
|
||||||
//! optional_utxos: Vec<WeightedUtxo>,
|
//! optional_utxos: Vec<WeightedUtxo>,
|
||||||
//! fee_rate: FeeRate,
|
//! fee_rate: FeeRate,
|
||||||
//! amount_needed: u64,
|
//! target_amount: u64,
|
||||||
//! fee_amount: u64,
|
|
||||||
//! drain_script: &Script,
|
//! drain_script: &Script,
|
||||||
//! ) -> Result<CoinSelectionResult, bdk::Error> {
|
//! ) -> Result<CoinSelectionResult, bdk::Error> {
|
||||||
//! let mut selected_amount = 0;
|
//! let mut selected_amount = 0;
|
||||||
@ -60,7 +59,7 @@
|
|||||||
//! )
|
//! )
|
||||||
//! .collect::<Vec<_>>();
|
//! .collect::<Vec<_>>();
|
||||||
//! let additional_fees = fee_rate.fee_wu(additional_weight);
|
//! let additional_fees = fee_rate.fee_wu(additional_weight);
|
||||||
//! let amount_needed_with_fees = (fee_amount + additional_fees) + amount_needed;
|
//! let amount_needed_with_fees = additional_fees + target_amount;
|
||||||
//! if selected_amount < amount_needed_with_fees {
|
//! if selected_amount < amount_needed_with_fees {
|
||||||
//! return Err(bdk::Error::InsufficientFunds {
|
//! return Err(bdk::Error::InsufficientFunds {
|
||||||
//! needed: amount_needed_with_fees,
|
//! needed: amount_needed_with_fees,
|
||||||
@ -74,7 +73,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Ok(CoinSelectionResult {
|
//! Ok(CoinSelectionResult {
|
||||||
//! selected: all_utxos_selected,
|
//! selected: all_utxos_selected,
|
||||||
//! fee_amount: fee_amount + additional_fees,
|
//! fee_amount: additional_fees,
|
||||||
//! excess,
|
//! excess,
|
||||||
//! })
|
//! })
|
||||||
//! }
|
//! }
|
||||||
@ -148,7 +147,7 @@ pub enum Excess {
|
|||||||
pub struct CoinSelectionResult {
|
pub struct CoinSelectionResult {
|
||||||
/// List of outputs selected for use as inputs
|
/// List of outputs selected for use as inputs
|
||||||
pub selected: Vec<Utxo>,
|
pub selected: Vec<Utxo>,
|
||||||
/// Total fee amount in satoshi
|
/// Total fee amount for the selected utxos in satoshis
|
||||||
pub fee_amount: u64,
|
pub fee_amount: u64,
|
||||||
/// Remaining amount after deducing fees and outgoing outputs
|
/// Remaining amount after deducing fees and outgoing outputs
|
||||||
pub excess: Excess,
|
pub excess: Excess,
|
||||||
@ -183,14 +182,13 @@ pub trait CoinSelectionAlgorithm<D: Database>: std::fmt::Debug {
|
|||||||
///
|
///
|
||||||
/// - `database`: a reference to the wallet's database that can be used to lookup additional
|
/// - `database`: a reference to the wallet's database that can be used to lookup additional
|
||||||
/// details for a specific UTXO
|
/// details for a specific UTXO
|
||||||
/// - `required_utxos`: the utxos that must be spent regardless of `amount_needed` with their
|
/// - `required_utxos`: the utxos that must be spent regardless of `target_amount` with their
|
||||||
/// weight cost
|
/// weight cost
|
||||||
/// - `optional_utxos`: the remaining available utxos to satisfy `amount_needed` with their
|
/// - `optional_utxos`: the remaining available utxos to satisfy `target_amount` with their
|
||||||
/// weight cost
|
/// weight cost
|
||||||
/// - `fee_rate`: fee rate to use
|
/// - `fee_rate`: fee rate to use
|
||||||
/// - `amount_needed`: the amount in satoshi to select
|
/// - `target_amount`: the outgoing amount in satoshis and the fees already
|
||||||
/// - `fee_amount`: the amount of fees in satoshi already accumulated from adding outputs and
|
/// accumulated from added outputs and transaction’s header.
|
||||||
/// the transaction's header
|
|
||||||
/// - `drain_script`: the script to use in case of change
|
/// - `drain_script`: the script to use in case of change
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn coin_select(
|
fn coin_select(
|
||||||
@ -199,8 +197,7 @@ pub trait CoinSelectionAlgorithm<D: Database>: std::fmt::Debug {
|
|||||||
required_utxos: Vec<WeightedUtxo>,
|
required_utxos: Vec<WeightedUtxo>,
|
||||||
optional_utxos: Vec<WeightedUtxo>,
|
optional_utxos: Vec<WeightedUtxo>,
|
||||||
fee_rate: FeeRate,
|
fee_rate: FeeRate,
|
||||||
amount_needed: u64,
|
target_amount: u64,
|
||||||
fee_amount: u64,
|
|
||||||
drain_script: &Script,
|
drain_script: &Script,
|
||||||
) -> Result<CoinSelectionResult, Error>;
|
) -> Result<CoinSelectionResult, Error>;
|
||||||
}
|
}
|
||||||
@ -219,14 +216,12 @@ impl<D: Database> CoinSelectionAlgorithm<D> for LargestFirstCoinSelection {
|
|||||||
required_utxos: Vec<WeightedUtxo>,
|
required_utxos: Vec<WeightedUtxo>,
|
||||||
mut optional_utxos: Vec<WeightedUtxo>,
|
mut optional_utxos: Vec<WeightedUtxo>,
|
||||||
fee_rate: FeeRate,
|
fee_rate: FeeRate,
|
||||||
amount_needed: u64,
|
target_amount: u64,
|
||||||
fee_amount: u64,
|
|
||||||
drain_script: &Script,
|
drain_script: &Script,
|
||||||
) -> Result<CoinSelectionResult, Error> {
|
) -> Result<CoinSelectionResult, Error> {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"amount_needed = `{}`, fee_amount = `{}`, fee_rate = `{:?}`",
|
"target_amount = `{}`, fee_rate = `{:?}`",
|
||||||
amount_needed,
|
target_amount,
|
||||||
fee_amount,
|
|
||||||
fee_rate
|
fee_rate
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -240,7 +235,7 @@ impl<D: Database> CoinSelectionAlgorithm<D> for LargestFirstCoinSelection {
|
|||||||
.chain(optional_utxos.into_iter().rev().map(|utxo| (false, utxo)))
|
.chain(optional_utxos.into_iter().rev().map(|utxo| (false, utxo)))
|
||||||
};
|
};
|
||||||
|
|
||||||
select_sorted_utxos(utxos, fee_rate, amount_needed, fee_amount, drain_script)
|
select_sorted_utxos(utxos, fee_rate, target_amount, drain_script)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,8 +253,7 @@ impl<D: Database> CoinSelectionAlgorithm<D> for OldestFirstCoinSelection {
|
|||||||
required_utxos: Vec<WeightedUtxo>,
|
required_utxos: Vec<WeightedUtxo>,
|
||||||
mut optional_utxos: Vec<WeightedUtxo>,
|
mut optional_utxos: Vec<WeightedUtxo>,
|
||||||
fee_rate: FeeRate,
|
fee_rate: FeeRate,
|
||||||
amount_needed: u64,
|
target_amount: u64,
|
||||||
fee_amount: u64,
|
|
||||||
drain_script: &Script,
|
drain_script: &Script,
|
||||||
) -> Result<CoinSelectionResult, Error> {
|
) -> Result<CoinSelectionResult, Error> {
|
||||||
// query db and create a blockheight lookup table
|
// query db and create a blockheight lookup table
|
||||||
@ -300,7 +294,7 @@ impl<D: Database> CoinSelectionAlgorithm<D> for OldestFirstCoinSelection {
|
|||||||
.chain(optional_utxos.into_iter().map(|utxo| (false, utxo)))
|
.chain(optional_utxos.into_iter().map(|utxo| (false, utxo)))
|
||||||
};
|
};
|
||||||
|
|
||||||
select_sorted_utxos(utxos, fee_rate, amount_needed, fee_amount, drain_script)
|
select_sorted_utxos(utxos, fee_rate, target_amount, drain_script)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,16 +327,16 @@ pub fn decide_change(remaining_amount: u64, fee_rate: FeeRate, drain_script: &Sc
|
|||||||
fn select_sorted_utxos(
|
fn select_sorted_utxos(
|
||||||
utxos: impl Iterator<Item = (bool, WeightedUtxo)>,
|
utxos: impl Iterator<Item = (bool, WeightedUtxo)>,
|
||||||
fee_rate: FeeRate,
|
fee_rate: FeeRate,
|
||||||
amount_needed: u64,
|
target_amount: u64,
|
||||||
mut fee_amount: u64,
|
|
||||||
drain_script: &Script,
|
drain_script: &Script,
|
||||||
) -> Result<CoinSelectionResult, Error> {
|
) -> Result<CoinSelectionResult, Error> {
|
||||||
let mut selected_amount = 0;
|
let mut selected_amount = 0;
|
||||||
|
let mut fee_amount = 0;
|
||||||
let selected = utxos
|
let selected = utxos
|
||||||
.scan(
|
.scan(
|
||||||
(&mut selected_amount, &mut fee_amount),
|
(&mut selected_amount, &mut fee_amount),
|
||||||
|(selected_amount, fee_amount), (must_use, weighted_utxo)| {
|
|(selected_amount, fee_amount), (must_use, weighted_utxo)| {
|
||||||
if must_use || **selected_amount < amount_needed + **fee_amount {
|
if must_use || **selected_amount < target_amount + **fee_amount {
|
||||||
**fee_amount +=
|
**fee_amount +=
|
||||||
fee_rate.fee_wu(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight);
|
fee_rate.fee_wu(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight);
|
||||||
**selected_amount += weighted_utxo.utxo.txout().value;
|
**selected_amount += weighted_utxo.utxo.txout().value;
|
||||||
@ -361,8 +355,7 @@ fn select_sorted_utxos(
|
|||||||
)
|
)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let amount_needed_with_fees = amount_needed + fee_amount;
|
let amount_needed_with_fees = target_amount + fee_amount;
|
||||||
|
|
||||||
if selected_amount < amount_needed_with_fees {
|
if selected_amount < amount_needed_with_fees {
|
||||||
return Err(Error::InsufficientFunds {
|
return Err(Error::InsufficientFunds {
|
||||||
needed: amount_needed_with_fees,
|
needed: amount_needed_with_fees,
|
||||||
@ -436,8 +429,7 @@ impl<D: Database> CoinSelectionAlgorithm<D> for BranchAndBoundCoinSelection {
|
|||||||
required_utxos: Vec<WeightedUtxo>,
|
required_utxos: Vec<WeightedUtxo>,
|
||||||
optional_utxos: Vec<WeightedUtxo>,
|
optional_utxos: Vec<WeightedUtxo>,
|
||||||
fee_rate: FeeRate,
|
fee_rate: FeeRate,
|
||||||
amount_needed: u64,
|
target_amount: u64,
|
||||||
fee_amount: u64,
|
|
||||||
drain_script: &Script,
|
drain_script: &Script,
|
||||||
) -> Result<CoinSelectionResult, Error> {
|
) -> Result<CoinSelectionResult, Error> {
|
||||||
// Mapping every (UTXO, usize) to an output group
|
// Mapping every (UTXO, usize) to an output group
|
||||||
@ -460,7 +452,6 @@ impl<D: Database> CoinSelectionAlgorithm<D> for BranchAndBoundCoinSelection {
|
|||||||
.iter()
|
.iter()
|
||||||
.fold(0, |acc, x| acc + x.effective_value);
|
.fold(0, |acc, x| acc + x.effective_value);
|
||||||
|
|
||||||
let actual_target = fee_amount + amount_needed;
|
|
||||||
let cost_of_change = self.size_of_change as f32 * fee_rate.as_sat_vb();
|
let cost_of_change = self.size_of_change as f32 * fee_rate.as_sat_vb();
|
||||||
|
|
||||||
let expected = (curr_available_value + curr_value)
|
let expected = (curr_available_value + curr_value)
|
||||||
@ -469,29 +460,28 @@ impl<D: Database> CoinSelectionAlgorithm<D> for BranchAndBoundCoinSelection {
|
|||||||
Error::Generic("Sum of UTXO spendable values does not fit into u64".to_string())
|
Error::Generic("Sum of UTXO spendable values does not fit into u64".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if expected < actual_target {
|
if expected < target_amount {
|
||||||
return Err(Error::InsufficientFunds {
|
return Err(Error::InsufficientFunds {
|
||||||
needed: actual_target,
|
needed: target_amount,
|
||||||
available: expected,
|
available: expected,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let actual_target = actual_target
|
let target_amount = target_amount
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("Bitcoin amount to fit into i64");
|
.expect("Bitcoin amount to fit into i64");
|
||||||
|
|
||||||
if curr_value > actual_target {
|
if curr_value > target_amount {
|
||||||
// remaining_amount can't be negative as that would mean the
|
// remaining_amount can't be negative as that would mean the
|
||||||
// selection wasn't successful
|
// selection wasn't successful
|
||||||
// actual_target = amount_needed + (fee_amount - vin_fees)
|
// target_amount = amount_needed + (fee_amount - vin_fees)
|
||||||
let remaining_amount = (curr_value - actual_target) as u64;
|
let remaining_amount = (curr_value - target_amount) as u64;
|
||||||
|
|
||||||
let excess = decide_change(remaining_amount, fee_rate, drain_script);
|
let excess = decide_change(remaining_amount, fee_rate, drain_script);
|
||||||
|
|
||||||
return Ok(BranchAndBoundCoinSelection::calculate_cs_result(
|
return Ok(BranchAndBoundCoinSelection::calculate_cs_result(
|
||||||
vec![],
|
vec![],
|
||||||
required_utxos,
|
required_utxos,
|
||||||
fee_amount,
|
|
||||||
excess,
|
excess,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -502,8 +492,7 @@ impl<D: Database> CoinSelectionAlgorithm<D> for BranchAndBoundCoinSelection {
|
|||||||
optional_utxos.clone(),
|
optional_utxos.clone(),
|
||||||
curr_value,
|
curr_value,
|
||||||
curr_available_value,
|
curr_available_value,
|
||||||
actual_target,
|
target_amount,
|
||||||
fee_amount,
|
|
||||||
cost_of_change,
|
cost_of_change,
|
||||||
drain_script,
|
drain_script,
|
||||||
fee_rate,
|
fee_rate,
|
||||||
@ -513,8 +502,7 @@ impl<D: Database> CoinSelectionAlgorithm<D> for BranchAndBoundCoinSelection {
|
|||||||
required_utxos,
|
required_utxos,
|
||||||
optional_utxos,
|
optional_utxos,
|
||||||
curr_value,
|
curr_value,
|
||||||
actual_target,
|
target_amount,
|
||||||
fee_amount,
|
|
||||||
drain_script,
|
drain_script,
|
||||||
fee_rate,
|
fee_rate,
|
||||||
)
|
)
|
||||||
@ -532,8 +520,7 @@ impl BranchAndBoundCoinSelection {
|
|||||||
mut optional_utxos: Vec<OutputGroup>,
|
mut optional_utxos: Vec<OutputGroup>,
|
||||||
mut curr_value: i64,
|
mut curr_value: i64,
|
||||||
mut curr_available_value: i64,
|
mut curr_available_value: i64,
|
||||||
actual_target: i64,
|
target_amount: i64,
|
||||||
fee_amount: u64,
|
|
||||||
cost_of_change: f32,
|
cost_of_change: f32,
|
||||||
drain_script: &Script,
|
drain_script: &Script,
|
||||||
fee_rate: FeeRate,
|
fee_rate: FeeRate,
|
||||||
@ -559,11 +546,11 @@ impl BranchAndBoundCoinSelection {
|
|||||||
// Cannot possibly reach target with the amount remaining in the curr_available_value,
|
// Cannot possibly reach target with the amount remaining in the curr_available_value,
|
||||||
// or the selected value is out of range.
|
// or the selected value is out of range.
|
||||||
// Go back and try other branch
|
// Go back and try other branch
|
||||||
if curr_value + curr_available_value < actual_target
|
if curr_value + curr_available_value < target_amount
|
||||||
|| curr_value > actual_target + cost_of_change as i64
|
|| curr_value > target_amount + cost_of_change as i64
|
||||||
{
|
{
|
||||||
backtrack = true;
|
backtrack = true;
|
||||||
} else if curr_value >= actual_target {
|
} else if curr_value >= target_amount {
|
||||||
// Selected value is within range, there's no point in going forward. Start
|
// Selected value is within range, there's no point in going forward. Start
|
||||||
// backtracking
|
// backtracking
|
||||||
backtrack = true;
|
backtrack = true;
|
||||||
@ -576,7 +563,7 @@ impl BranchAndBoundCoinSelection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we found a perfect match, break here
|
// If we found a perfect match, break here
|
||||||
if curr_value == actual_target {
|
if curr_value == target_amount {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -634,15 +621,14 @@ impl BranchAndBoundCoinSelection {
|
|||||||
|
|
||||||
// remaining_amount can't be negative as that would mean the
|
// remaining_amount can't be negative as that would mean the
|
||||||
// selection wasn't successful
|
// selection wasn't successful
|
||||||
// actual_target = amount_needed + (fee_amount - vin_fees)
|
// target_amount = amount_needed + (fee_amount - vin_fees)
|
||||||
let remaining_amount = (selected_amount - actual_target) as u64;
|
let remaining_amount = (selected_amount - target_amount) as u64;
|
||||||
|
|
||||||
let excess = decide_change(remaining_amount, fee_rate, drain_script);
|
let excess = decide_change(remaining_amount, fee_rate, drain_script);
|
||||||
|
|
||||||
Ok(BranchAndBoundCoinSelection::calculate_cs_result(
|
Ok(BranchAndBoundCoinSelection::calculate_cs_result(
|
||||||
selected_utxos,
|
selected_utxos,
|
||||||
required_utxos,
|
required_utxos,
|
||||||
fee_amount,
|
|
||||||
excess,
|
excess,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -653,8 +639,7 @@ impl BranchAndBoundCoinSelection {
|
|||||||
required_utxos: Vec<OutputGroup>,
|
required_utxos: Vec<OutputGroup>,
|
||||||
mut optional_utxos: Vec<OutputGroup>,
|
mut optional_utxos: Vec<OutputGroup>,
|
||||||
curr_value: i64,
|
curr_value: i64,
|
||||||
actual_target: i64,
|
target_amount: i64,
|
||||||
fee_amount: u64,
|
|
||||||
drain_script: &Script,
|
drain_script: &Script,
|
||||||
fee_rate: FeeRate,
|
fee_rate: FeeRate,
|
||||||
) -> CoinSelectionResult {
|
) -> CoinSelectionResult {
|
||||||
@ -670,7 +655,7 @@ impl BranchAndBoundCoinSelection {
|
|||||||
let selected_utxos = optional_utxos.into_iter().fold(
|
let selected_utxos = optional_utxos.into_iter().fold(
|
||||||
(curr_value, vec![]),
|
(curr_value, vec![]),
|
||||||
|(mut amount, mut utxos), utxo| {
|
|(mut amount, mut utxos), utxo| {
|
||||||
if amount >= actual_target {
|
if amount >= target_amount {
|
||||||
(amount, utxos)
|
(amount, utxos)
|
||||||
} else {
|
} else {
|
||||||
amount += utxo.effective_value;
|
amount += utxo.effective_value;
|
||||||
@ -682,27 +667,21 @@ impl BranchAndBoundCoinSelection {
|
|||||||
|
|
||||||
// remaining_amount can't be negative as that would mean the
|
// remaining_amount can't be negative as that would mean the
|
||||||
// selection wasn't successful
|
// selection wasn't successful
|
||||||
// actual_target = amount_needed + (fee_amount - vin_fees)
|
// target_amount = amount_needed + (fee_amount - vin_fees)
|
||||||
let remaining_amount = (selected_utxos.0 - actual_target) as u64;
|
let remaining_amount = (selected_utxos.0 - target_amount) as u64;
|
||||||
|
|
||||||
let excess = decide_change(remaining_amount, fee_rate, drain_script);
|
let excess = decide_change(remaining_amount, fee_rate, drain_script);
|
||||||
|
|
||||||
BranchAndBoundCoinSelection::calculate_cs_result(
|
BranchAndBoundCoinSelection::calculate_cs_result(selected_utxos.1, required_utxos, excess)
|
||||||
selected_utxos.1,
|
|
||||||
required_utxos,
|
|
||||||
fee_amount,
|
|
||||||
excess,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_cs_result(
|
fn calculate_cs_result(
|
||||||
mut selected_utxos: Vec<OutputGroup>,
|
mut selected_utxos: Vec<OutputGroup>,
|
||||||
mut required_utxos: Vec<OutputGroup>,
|
mut required_utxos: Vec<OutputGroup>,
|
||||||
mut fee_amount: u64,
|
|
||||||
excess: Excess,
|
excess: Excess,
|
||||||
) -> CoinSelectionResult {
|
) -> CoinSelectionResult {
|
||||||
selected_utxos.append(&mut required_utxos);
|
selected_utxos.append(&mut required_utxos);
|
||||||
fee_amount += selected_utxos.iter().map(|u| u.fee).sum::<u64>();
|
let fee_amount = selected_utxos.iter().map(|u| u.fee).sum::<u64>();
|
||||||
let selected = selected_utxos
|
let selected = selected_utxos
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|u| u.weighted_utxo.utxo)
|
.map(|u| u.weighted_utxo.utxo)
|
||||||
@ -874,6 +853,7 @@ mod test {
|
|||||||
let utxos = get_test_utxos();
|
let utxos = get_test_utxos();
|
||||||
let database = MemoryDatabase::default();
|
let database = MemoryDatabase::default();
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 250_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let result = LargestFirstCoinSelection::default()
|
let result = LargestFirstCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -881,15 +861,14 @@ mod test {
|
|||||||
utxos,
|
utxos,
|
||||||
vec![],
|
vec![],
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
250_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(result.selected.len(), 3);
|
assert_eq!(result.selected.len(), 3);
|
||||||
assert_eq!(result.selected_amount(), 300_010);
|
assert_eq!(result.selected_amount(), 300_010);
|
||||||
assert_eq!(result.fee_amount, 254)
|
assert_eq!(result.fee_amount, 204)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -897,6 +876,7 @@ mod test {
|
|||||||
let utxos = get_test_utxos();
|
let utxos = get_test_utxos();
|
||||||
let database = MemoryDatabase::default();
|
let database = MemoryDatabase::default();
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 20_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let result = LargestFirstCoinSelection::default()
|
let result = LargestFirstCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -904,15 +884,14 @@ mod test {
|
|||||||
utxos,
|
utxos,
|
||||||
vec![],
|
vec![],
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
20_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(result.selected.len(), 3);
|
assert_eq!(result.selected.len(), 3);
|
||||||
assert_eq!(result.selected_amount(), 300_010);
|
assert_eq!(result.selected_amount(), 300_010);
|
||||||
assert_eq!(result.fee_amount, 254);
|
assert_eq!(result.fee_amount, 204);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -920,6 +899,7 @@ mod test {
|
|||||||
let utxos = get_test_utxos();
|
let utxos = get_test_utxos();
|
||||||
let database = MemoryDatabase::default();
|
let database = MemoryDatabase::default();
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 20_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let result = LargestFirstCoinSelection::default()
|
let result = LargestFirstCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -927,15 +907,14 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
20_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(result.selected.len(), 1);
|
assert_eq!(result.selected.len(), 1);
|
||||||
assert_eq!(result.selected_amount(), 200_000);
|
assert_eq!(result.selected_amount(), 200_000);
|
||||||
assert_eq!(result.fee_amount, 118);
|
assert_eq!(result.fee_amount, 68);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -944,6 +923,7 @@ mod test {
|
|||||||
let utxos = get_test_utxos();
|
let utxos = get_test_utxos();
|
||||||
let database = MemoryDatabase::default();
|
let database = MemoryDatabase::default();
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 500_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
LargestFirstCoinSelection::default()
|
LargestFirstCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -951,8 +931,7 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
500_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -964,6 +943,7 @@ mod test {
|
|||||||
let utxos = get_test_utxos();
|
let utxos = get_test_utxos();
|
||||||
let database = MemoryDatabase::default();
|
let database = MemoryDatabase::default();
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 250_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
LargestFirstCoinSelection::default()
|
LargestFirstCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -971,8 +951,7 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1000.0),
|
FeeRate::from_sat_per_vb(1000.0),
|
||||||
250_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -983,6 +962,7 @@ mod test {
|
|||||||
let mut database = MemoryDatabase::default();
|
let mut database = MemoryDatabase::default();
|
||||||
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
|
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 180_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let result = OldestFirstCoinSelection::default()
|
let result = OldestFirstCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -990,15 +970,14 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
180_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(result.selected.len(), 2);
|
assert_eq!(result.selected.len(), 2);
|
||||||
assert_eq!(result.selected_amount(), 200_000);
|
assert_eq!(result.selected_amount(), 200_000);
|
||||||
assert_eq!(result.fee_amount, 186)
|
assert_eq!(result.fee_amount, 136)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1042,21 +1021,22 @@ mod test {
|
|||||||
database.set_tx(&utxo1_tx_details).unwrap();
|
database.set_tx(&utxo1_tx_details).unwrap();
|
||||||
database.set_tx(&utxo2_tx_details).unwrap();
|
database.set_tx(&utxo2_tx_details).unwrap();
|
||||||
|
|
||||||
|
let target_amount = 180_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let result = OldestFirstCoinSelection::default()
|
let result = OldestFirstCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
&database,
|
&database,
|
||||||
vec![],
|
vec![],
|
||||||
vec![utxo3, utxo1, utxo2],
|
vec![utxo3, utxo1, utxo2],
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
180_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(result.selected.len(), 2);
|
assert_eq!(result.selected.len(), 2);
|
||||||
assert_eq!(result.selected_amount(), 200_000);
|
assert_eq!(result.selected_amount(), 200_000);
|
||||||
assert_eq!(result.fee_amount, 186)
|
assert_eq!(result.fee_amount, 136)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1064,6 +1044,7 @@ mod test {
|
|||||||
let mut database = MemoryDatabase::default();
|
let mut database = MemoryDatabase::default();
|
||||||
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
|
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 20_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let result = OldestFirstCoinSelection::default()
|
let result = OldestFirstCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -1071,15 +1052,14 @@ mod test {
|
|||||||
utxos,
|
utxos,
|
||||||
vec![],
|
vec![],
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
20_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(result.selected.len(), 3);
|
assert_eq!(result.selected.len(), 3);
|
||||||
assert_eq!(result.selected_amount(), 500_000);
|
assert_eq!(result.selected_amount(), 500_000);
|
||||||
assert_eq!(result.fee_amount, 254);
|
assert_eq!(result.fee_amount, 204);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1087,6 +1067,7 @@ mod test {
|
|||||||
let mut database = MemoryDatabase::default();
|
let mut database = MemoryDatabase::default();
|
||||||
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
|
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 20_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let result = OldestFirstCoinSelection::default()
|
let result = OldestFirstCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -1094,15 +1075,14 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
20_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(result.selected.len(), 1);
|
assert_eq!(result.selected.len(), 1);
|
||||||
assert_eq!(result.selected_amount(), 120_000);
|
assert_eq!(result.selected_amount(), 120_000);
|
||||||
assert_eq!(result.fee_amount, 118);
|
assert_eq!(result.fee_amount, 68);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1111,6 +1091,7 @@ mod test {
|
|||||||
let mut database = MemoryDatabase::default();
|
let mut database = MemoryDatabase::default();
|
||||||
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
|
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 600_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
OldestFirstCoinSelection::default()
|
OldestFirstCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -1118,8 +1099,7 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
600_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -1131,8 +1111,7 @@ mod test {
|
|||||||
let mut database = MemoryDatabase::default();
|
let mut database = MemoryDatabase::default();
|
||||||
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
|
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
|
||||||
|
|
||||||
let amount_needed: u64 =
|
let target_amount: u64 = utxos.iter().map(|wu| wu.utxo.txout().value).sum::<u64>() - 50;
|
||||||
utxos.iter().map(|wu| wu.utxo.txout().value).sum::<u64>() - (FEE_AMOUNT + 50);
|
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
|
||||||
OldestFirstCoinSelection::default()
|
OldestFirstCoinSelection::default()
|
||||||
@ -1141,8 +1120,7 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1000.0),
|
FeeRate::from_sat_per_vb(1000.0),
|
||||||
amount_needed,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -1157,21 +1135,22 @@ mod test {
|
|||||||
let database = MemoryDatabase::default();
|
let database = MemoryDatabase::default();
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
|
||||||
|
let target_amount = 250_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let result = BranchAndBoundCoinSelection::default()
|
let result = BranchAndBoundCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
&database,
|
&database,
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
250_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(result.selected.len(), 3);
|
assert_eq!(result.selected.len(), 3);
|
||||||
assert_eq!(result.selected_amount(), 300_000);
|
assert_eq!(result.selected_amount(), 300_000);
|
||||||
assert_eq!(result.fee_amount, 254);
|
assert_eq!(result.fee_amount, 204);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1179,6 +1158,7 @@ mod test {
|
|||||||
let utxos = get_test_utxos();
|
let utxos = get_test_utxos();
|
||||||
let database = MemoryDatabase::default();
|
let database = MemoryDatabase::default();
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 20_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let result = BranchAndBoundCoinSelection::default()
|
let result = BranchAndBoundCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -1186,15 +1166,14 @@ mod test {
|
|||||||
utxos.clone(),
|
utxos.clone(),
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
20_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(result.selected.len(), 3);
|
assert_eq!(result.selected.len(), 3);
|
||||||
assert_eq!(result.selected_amount(), 300_010);
|
assert_eq!(result.selected_amount(), 300_010);
|
||||||
assert_eq!(result.fee_amount, 254);
|
assert_eq!(result.fee_amount, 204);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1202,6 +1181,7 @@ mod test {
|
|||||||
let utxos = get_test_utxos();
|
let utxos = get_test_utxos();
|
||||||
let database = MemoryDatabase::default();
|
let database = MemoryDatabase::default();
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 299756 + FEE_AMOUNT;
|
||||||
|
|
||||||
let result = BranchAndBoundCoinSelection::default()
|
let result = BranchAndBoundCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -1209,15 +1189,14 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
299756,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(result.selected.len(), 3);
|
assert_eq!(result.selected.len(), 3);
|
||||||
assert_eq!(result.selected_amount(), 300010);
|
assert_eq!(result.selected_amount(), 300010);
|
||||||
assert_eq!(result.fee_amount, 254);
|
assert_eq!(result.fee_amount, 204);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1236,21 +1215,22 @@ mod test {
|
|||||||
assert!(amount > 150_000);
|
assert!(amount > 150_000);
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
|
||||||
|
let target_amount = 150_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let result = BranchAndBoundCoinSelection::default()
|
let result = BranchAndBoundCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
&database,
|
&database,
|
||||||
required,
|
required,
|
||||||
optional,
|
optional,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
150_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(result.selected.len(), 3);
|
assert_eq!(result.selected.len(), 3);
|
||||||
assert_eq!(result.selected_amount(), 300_010);
|
assert_eq!(result.selected_amount(), 300_010);
|
||||||
assert!((result.fee_amount as f32 - 254.0).abs() < f32::EPSILON);
|
assert!((result.fee_amount as f32 - 204.0).abs() < f32::EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1259,6 +1239,7 @@ mod test {
|
|||||||
let utxos = get_test_utxos();
|
let utxos = get_test_utxos();
|
||||||
let database = MemoryDatabase::default();
|
let database = MemoryDatabase::default();
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 500_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
BranchAndBoundCoinSelection::default()
|
BranchAndBoundCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -1266,8 +1247,7 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
500_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -1279,6 +1259,7 @@ mod test {
|
|||||||
let utxos = get_test_utxos();
|
let utxos = get_test_utxos();
|
||||||
let database = MemoryDatabase::default();
|
let database = MemoryDatabase::default();
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 250_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
BranchAndBoundCoinSelection::default()
|
BranchAndBoundCoinSelection::default()
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -1286,8 +1267,7 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1000.0),
|
FeeRate::from_sat_per_vb(1000.0),
|
||||||
250_000,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -1298,6 +1278,7 @@ mod test {
|
|||||||
let utxos = get_test_utxos();
|
let utxos = get_test_utxos();
|
||||||
let database = MemoryDatabase::default();
|
let database = MemoryDatabase::default();
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 99932; // first utxo's effective value
|
||||||
|
|
||||||
let result = BranchAndBoundCoinSelection::new(0)
|
let result = BranchAndBoundCoinSelection::new(0)
|
||||||
.coin_select(
|
.coin_select(
|
||||||
@ -1305,8 +1286,7 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb(1.0),
|
||||||
99932, // first utxo's effective value
|
target_amount,
|
||||||
0,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -1314,8 +1294,8 @@ mod test {
|
|||||||
assert_eq!(result.selected.len(), 1);
|
assert_eq!(result.selected.len(), 1);
|
||||||
assert_eq!(result.selected_amount(), 100_000);
|
assert_eq!(result.selected_amount(), 100_000);
|
||||||
let input_size = (TXIN_BASE_WEIGHT + P2WPKH_SATISFACTION_SIZE).vbytes();
|
let input_size = (TXIN_BASE_WEIGHT + P2WPKH_SATISFACTION_SIZE).vbytes();
|
||||||
let epsilon = 0.5;
|
// the final fee rate should be exactly the same as the fee rate given
|
||||||
assert!((1.0 - (result.fee_amount as f32 / input_size as f32)).abs() < epsilon);
|
assert!((1.0 - (result.fee_amount as f32 / input_size as f32)).abs() < f32::EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1335,7 +1315,6 @@ mod test {
|
|||||||
optional_utxos,
|
optional_utxos,
|
||||||
FeeRate::from_sat_per_vb(0.0),
|
FeeRate::from_sat_per_vb(0.0),
|
||||||
target_amount,
|
target_amount,
|
||||||
0,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -1356,17 +1335,15 @@ mod test {
|
|||||||
|
|
||||||
let size_of_change = 31;
|
let size_of_change = 31;
|
||||||
let cost_of_change = size_of_change as f32 * fee_rate.as_sat_vb();
|
let cost_of_change = size_of_change as f32 * fee_rate.as_sat_vb();
|
||||||
|
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
let target_amount = 20_000 + FEE_AMOUNT;
|
||||||
BranchAndBoundCoinSelection::new(size_of_change)
|
BranchAndBoundCoinSelection::new(size_of_change)
|
||||||
.bnb(
|
.bnb(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
0,
|
0,
|
||||||
curr_available_value,
|
curr_available_value,
|
||||||
20_000,
|
target_amount as i64,
|
||||||
FEE_AMOUNT,
|
|
||||||
cost_of_change,
|
cost_of_change,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
fee_rate,
|
fee_rate,
|
||||||
@ -1387,6 +1364,7 @@ mod test {
|
|||||||
|
|
||||||
let size_of_change = 31;
|
let size_of_change = 31;
|
||||||
let cost_of_change = size_of_change as f32 * fee_rate.as_sat_vb();
|
let cost_of_change = size_of_change as f32 * fee_rate.as_sat_vb();
|
||||||
|
let target_amount = 20_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let drain_script = Script::default();
|
let drain_script = Script::default();
|
||||||
|
|
||||||
@ -1396,8 +1374,7 @@ mod test {
|
|||||||
utxos,
|
utxos,
|
||||||
0,
|
0,
|
||||||
curr_available_value,
|
curr_available_value,
|
||||||
20_000,
|
target_amount as i64,
|
||||||
FEE_AMOUNT,
|
|
||||||
cost_of_change,
|
cost_of_change,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
fee_rate,
|
fee_rate,
|
||||||
@ -1434,14 +1411,13 @@ mod test {
|
|||||||
curr_value,
|
curr_value,
|
||||||
curr_available_value,
|
curr_available_value,
|
||||||
target_amount,
|
target_amount,
|
||||||
FEE_AMOUNT,
|
|
||||||
cost_of_change,
|
cost_of_change,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
fee_rate,
|
fee_rate,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(result.selected_amount(), 100_000);
|
assert_eq!(result.selected_amount(), 100_000);
|
||||||
assert_eq!(result.fee_amount, 186);
|
assert_eq!(result.fee_amount, 136);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: bnb() function should be optimized, and this test should be done with more utxos
|
// TODO: bnb() function should be optimized, and this test should be done with more utxos
|
||||||
@ -1475,7 +1451,6 @@ mod test {
|
|||||||
curr_value,
|
curr_value,
|
||||||
curr_available_value,
|
curr_available_value,
|
||||||
target_amount,
|
target_amount,
|
||||||
0,
|
|
||||||
0.0,
|
0.0,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
fee_rate,
|
fee_rate,
|
||||||
@ -1490,7 +1465,7 @@ mod test {
|
|||||||
let seed = [0; 32];
|
let seed = [0; 32];
|
||||||
let mut rng: StdRng = SeedableRng::from_seed(seed);
|
let mut rng: StdRng = SeedableRng::from_seed(seed);
|
||||||
let mut utxos = generate_random_utxos(&mut rng, 300);
|
let mut utxos = generate_random_utxos(&mut rng, 300);
|
||||||
let target_amount = sum_random_utxos(&mut rng, &mut utxos);
|
let target_amount = sum_random_utxos(&mut rng, &mut utxos) + FEE_AMOUNT;
|
||||||
|
|
||||||
let fee_rate = FeeRate::from_sat_per_vb(1.0);
|
let fee_rate = FeeRate::from_sat_per_vb(1.0);
|
||||||
let utxos: Vec<OutputGroup> = utxos
|
let utxos: Vec<OutputGroup> = utxos
|
||||||
@ -1505,12 +1480,11 @@ mod test {
|
|||||||
utxos,
|
utxos,
|
||||||
0,
|
0,
|
||||||
target_amount as i64,
|
target_amount as i64,
|
||||||
FEE_AMOUNT,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
fee_rate,
|
fee_rate,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(result.selected_amount() > target_amount);
|
assert!(result.selected_amount() > target_amount);
|
||||||
assert_eq!(result.fee_amount, (50 + result.selected.len() * 68) as u64);
|
assert_eq!(result.fee_amount, (result.selected.len() * 68) as u64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -802,11 +802,10 @@ where
|
|||||||
required_utxos,
|
required_utxos,
|
||||||
optional_utxos,
|
optional_utxos,
|
||||||
fee_rate,
|
fee_rate,
|
||||||
outgoing,
|
outgoing + fee_amount,
|
||||||
fee_amount,
|
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)?;
|
)?;
|
||||||
let mut fee_amount = coin_selection.fee_amount;
|
fee_amount += coin_selection.fee_amount;
|
||||||
let excess = &coin_selection.excess;
|
let excess = &coin_selection.excess;
|
||||||
|
|
||||||
tx.input = coin_selection
|
tx.input = coin_selection
|
||||||
|
Loading…
x
Reference in New Issue
Block a user