mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 11:31:08 +03:00
add "smallest first" strategy for coin selection (#256)
This commit is contained in:
parent
8f33c7e0fe
commit
04eb400422
3 changed files with 87 additions and 6 deletions
|
@ -209,6 +209,13 @@ fn main() {
|
|||
.long("min_conf")
|
||||
.default_value("1")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("selection_strategy")
|
||||
.help("Coin/Output selection strategy.")
|
||||
.short("s")
|
||||
.long("selection")
|
||||
.possible_values(&["default", "smallest"])
|
||||
.default_value("default")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("dest")
|
||||
.help("Send the transaction to the provided server")
|
||||
.short("d")
|
||||
|
@ -397,9 +404,12 @@ fn wallet_command(wallet_args: &ArgMatches) {
|
|||
.expect("Could not parse amount as a number with optional decimal point.");
|
||||
let minimum_confirmations: u64 = send_args
|
||||
.value_of("minimum_confirmations")
|
||||
.unwrap_or("1")
|
||||
.unwrap()
|
||||
.parse()
|
||||
.expect("Could not parse minimum_confirmations as a whole number.");
|
||||
let selection_strategy = send_args
|
||||
.value_of("selection_strategy")
|
||||
.expect("Selection strategy required");
|
||||
let mut dest = "stdout";
|
||||
if let Some(d) = send_args.value_of("dest") {
|
||||
dest = d;
|
||||
|
@ -410,6 +420,7 @@ fn wallet_command(wallet_args: &ArgMatches) {
|
|||
amount,
|
||||
minimum_confirmations,
|
||||
dest.to_string(),
|
||||
(selection_strategy == "default"),
|
||||
);
|
||||
match result {
|
||||
Ok(_) => {}, //success messaged logged internally
|
||||
|
@ -425,7 +436,7 @@ fn wallet_command(wallet_args: &ArgMatches) {
|
|||
.expect("Could not parse amount as number with optional decimal point.");
|
||||
let minimum_confirmations: u64 = send_args
|
||||
.value_of("minimum_confirmations")
|
||||
.unwrap_or("1")
|
||||
.unwrap()
|
||||
.parse()
|
||||
.expect("Could not parse minimum_confirmations as a whole number.");
|
||||
wallet::issue_burn_tx(&wallet_config, &keychain, amount, minimum_confirmations)
|
||||
|
|
|
@ -36,6 +36,7 @@ pub fn issue_send_tx(
|
|||
amount: u64,
|
||||
minimum_confirmations: u64,
|
||||
dest: String,
|
||||
selection_strategy: bool,
|
||||
) -> Result<(), Error> {
|
||||
checker::refresh_outputs(config, keychain)?;
|
||||
|
||||
|
@ -52,6 +53,7 @@ pub fn issue_send_tx(
|
|||
current_height,
|
||||
minimum_confirmations,
|
||||
lock_height,
|
||||
selection_strategy,
|
||||
)?;
|
||||
|
||||
let partial_tx = build_partial_tx(amount, blind_sum, tx);
|
||||
|
@ -79,12 +81,19 @@ fn build_send_tx(
|
|||
current_height: u64,
|
||||
minimum_confirmations: u64,
|
||||
lock_height: u64,
|
||||
default_strategy: bool,
|
||||
) -> Result<(Transaction, BlindingFactor), Error> {
|
||||
let key_id = keychain.clone().root_key_id();
|
||||
|
||||
// select some spendable coins from the wallet
|
||||
let coins = WalletData::read_wallet(&config.data_file_dir, |wallet_data| {
|
||||
wallet_data.select(key_id.clone(), current_height, minimum_confirmations)
|
||||
wallet_data.select(
|
||||
key_id.clone(),
|
||||
amount,
|
||||
current_height,
|
||||
minimum_confirmations,
|
||||
default_strategy,
|
||||
)
|
||||
})?;
|
||||
|
||||
// build transaction skeleton with inputs and change
|
||||
|
@ -124,9 +133,11 @@ pub fn issue_burn_tx(
|
|||
|
||||
// select some spendable coins from the wallet
|
||||
let coins = WalletData::read_wallet(&config.data_file_dir, |wallet_data| {
|
||||
wallet_data.select(key_id.clone(), current_height, minimum_confirmations)
|
||||
wallet_data.select(key_id.clone(), amount, current_height, minimum_confirmations, false)
|
||||
})?;
|
||||
|
||||
debug!(LOGGER, "selected some coins - {}", coins.len());
|
||||
|
||||
let mut parts = inputs_and_change(&coins, config, keychain, key_id, amount)?;
|
||||
|
||||
// add burn output and fees
|
||||
|
|
|
@ -499,8 +499,67 @@ impl WalletData {
|
|||
self.outputs.get(&key_id.to_hex())
|
||||
}
|
||||
|
||||
/// Select spendable coins from the wallet
|
||||
/// Select spendable coins from the wallet.
|
||||
/// Default strategy is "aggressive".
|
||||
/// Non-default strategy is "smallest_first".
|
||||
/// When we introduce additional strategies we should pass something other than a bool in.
|
||||
pub fn select(
|
||||
&self,
|
||||
root_key_id: keychain::Identifier,
|
||||
amount: u64,
|
||||
current_height: u64,
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 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> {
|
||||
let mut eligible = self.outputs
|
||||
.values()
|
||||
.filter(|out| {
|
||||
out.root_key_id == root_key_id
|
||||
&& out.eligible_to_spend(current_height, minimum_confirmations)
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<OutputData>>();
|
||||
|
||||
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<_>>()
|
||||
}
|
||||
|
||||
// Selects all eligible outputs to spend to reduce UTXO set as much as possible (the default).
|
||||
fn select_aggressive(
|
||||
&self,
|
||||
root_key_id: keychain::Identifier,
|
||||
current_height: u64,
|
||||
|
@ -512,7 +571,7 @@ impl WalletData {
|
|||
out.root_key_id == root_key_id
|
||||
&& out.eligible_to_spend(current_height, minimum_confirmations)
|
||||
})
|
||||
.map(|out| out.clone())
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue