mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
Rework wallet coin selection to select a max of 500 outputs (#265)
This commit is contained in:
parent
c2a95637b3
commit
8269bdd873
2 changed files with 52 additions and 51 deletions
|
@ -218,8 +218,8 @@ fn main() {
|
|||
.help("Coin/Output selection strategy.")
|
||||
.short("s")
|
||||
.long("selection")
|
||||
.possible_values(&["default", "smallest"])
|
||||
.default_value("default")
|
||||
.possible_values(&["all", "smallest"])
|
||||
.default_value("all")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("dest")
|
||||
.help("Send the transaction to the provided server")
|
||||
|
|
|
@ -500,8 +500,8 @@ impl WalletData {
|
|||
}
|
||||
|
||||
/// Select spendable coins from the wallet.
|
||||
/// Default strategy is "aggressive".
|
||||
/// Non-default strategy is "smallest_first".
|
||||
/// Default strategy is to spend the maximum number of outputs (up to max_outputs).
|
||||
/// Alternative strategy is to spend smallest outputs first but only as many as necessary.
|
||||
/// When we introduce additional strategies we should pass something other than a bool in.
|
||||
pub fn select(
|
||||
&self,
|
||||
|
@ -511,31 +511,9 @@ impl WalletData {
|
|||
minimum_confirmations: u64,
|
||||
default_strategy: bool,
|
||||
) -> Vec<OutputData> {
|
||||
if default_strategy {
|
||||
self.select_aggressive(
|
||||
root_key_id,
|
||||
current_height,
|
||||
minimum_confirmations,
|
||||
)
|
||||
} else {
|
||||
self.select_smallest_first(
|
||||
root_key_id,
|
||||
amount,
|
||||
current_height,
|
||||
minimum_confirmations,
|
||||
)
|
||||
}
|
||||
}
|
||||
let max_outputs = 500;
|
||||
|
||||
// Selects the smallest number of outputs after ordering them by value.
|
||||
// Reduces "dust" but leaves larger outputs unspent if possible.
|
||||
fn select_smallest_first(
|
||||
&self,
|
||||
root_key_id: keychain::Identifier,
|
||||
amount: u64,
|
||||
current_height: u64,
|
||||
minimum_confirmations: u64,
|
||||
) -> Vec<OutputData> {
|
||||
// first find all eligible outputs based on number of confirmations
|
||||
let mut eligible = self.outputs
|
||||
.values()
|
||||
.filter(|out| {
|
||||
|
@ -545,34 +523,57 @@ impl WalletData {
|
|||
.cloned()
|
||||
.collect::<Vec<OutputData>>();
|
||||
|
||||
// sort eligible outputs by increasing value
|
||||
eligible.sort_by_key(|out| out.value);
|
||||
|
||||
let mut total_amount = 0;
|
||||
eligible.iter()
|
||||
.take_while(|out| {
|
||||
let res = total_amount < amount;
|
||||
total_amount += out.value;
|
||||
res
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
// use a sliding window to identify potential sets of possible outputs to spend
|
||||
if eligible.len() > max_outputs {
|
||||
for window in eligible.windows(max_outputs) {
|
||||
let eligible = window.iter().cloned().collect::<Vec<_>>();
|
||||
if let Some(outputs) = self.select_from(amount, default_strategy, eligible) {
|
||||
return outputs;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(outputs) = self.select_from(amount, default_strategy, eligible.clone()) {
|
||||
return outputs;
|
||||
}
|
||||
}
|
||||
|
||||
// we failed to find a suitable set of outputs to spend,
|
||||
// so return the largest amount we can so we can provide guidance on what is possible
|
||||
eligible.reverse();
|
||||
eligible.iter().take(max_outputs).cloned().collect()
|
||||
}
|
||||
|
||||
// Selects all eligible outputs to spend to reduce UTXO set as much as possible (the default).
|
||||
fn select_aggressive(
|
||||
// Select the full list of outputs if we are using the default strategy.
|
||||
// Otherwise select just enough outputs to cover the desired amount.
|
||||
fn select_from(
|
||||
&self,
|
||||
root_key_id: keychain::Identifier,
|
||||
current_height: u64,
|
||||
minimum_confirmations: u64,
|
||||
) -> Vec<OutputData> {
|
||||
self.outputs
|
||||
.values()
|
||||
.filter(|out| {
|
||||
out.root_key_id == root_key_id
|
||||
&& out.eligible_to_spend(current_height, minimum_confirmations)
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
amount: u64,
|
||||
select_all: bool,
|
||||
outputs: Vec<OutputData>,
|
||||
) -> Option<Vec<OutputData>> {
|
||||
let total = outputs.iter().fold(0, |acc, x| acc + x.value);
|
||||
if total >= amount {
|
||||
if select_all {
|
||||
return Some(outputs.iter().cloned().collect());
|
||||
} else {
|
||||
let mut selected_amount = 0;
|
||||
return Some(
|
||||
outputs.iter()
|
||||
.take_while(|out| {
|
||||
let res = selected_amount < amount;
|
||||
selected_amount += out.value;
|
||||
res
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Next child index when we want to create a new output.
|
||||
|
|
Loading…
Reference in a new issue