mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
wallet can now optionally spend zero-confirmation txs (#188)
* wallet can now optionally spend zero-confirmation txs * add rule to get_mineable_transactions based on total pool size
This commit is contained in:
parent
bab7bd7060
commit
4d7b46b0b9
8 changed files with 162 additions and 58 deletions
|
@ -260,6 +260,20 @@ impl DirectedGraph {
|
||||||
pub fn get_roots(&self) -> Vec<core::hash::Hash> {
|
pub fn get_roots(&self) -> Vec<core::hash::Hash> {
|
||||||
self.roots.iter().map(|x| x.transaction_hash).collect()
|
self.roots.iter().map(|x| x.transaction_hash).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get list of all vertices in this graph including the roots
|
||||||
|
pub fn get_vertices(&self) -> Vec<core::hash::Hash> {
|
||||||
|
let mut hashes = self.roots
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.transaction_hash)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let non_root_hashes = self.vertices
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.transaction_hash)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
hashes.extend(&non_root_hashes);
|
||||||
|
return hashes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Using transaction merkle_inputs_outputs to calculate a deterministic hash;
|
/// Using transaction merkle_inputs_outputs to calculate a deterministic hash;
|
||||||
|
|
|
@ -304,13 +304,19 @@ impl Pool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simplest possible implementation: just return the roots
|
/// Currently a single rule for miner preference -
|
||||||
|
/// return all txs if less than num_to_fetch txs in the entire pool
|
||||||
|
/// otherwise return num_to_fetch of just the roots
|
||||||
pub fn get_mineable_transactions(&self, num_to_fetch: u32) -> Vec<hash::Hash> {
|
pub fn get_mineable_transactions(&self, num_to_fetch: u32) -> Vec<hash::Hash> {
|
||||||
|
if self.graph.len_vertices() <= num_to_fetch as usize {
|
||||||
|
self.graph.get_vertices()
|
||||||
|
} else {
|
||||||
let mut roots = self.graph.get_roots();
|
let mut roots = self.graph.get_roots();
|
||||||
roots.truncate(num_to_fetch as usize);
|
roots.truncate(num_to_fetch as usize);
|
||||||
roots
|
roots
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TransactionGraphContainer for Pool {
|
impl TransactionGraphContainer for Pool {
|
||||||
fn get_graph(&self) -> &graph::DirectedGraph {
|
fn get_graph(&self) -> &graph::DirectedGraph {
|
||||||
|
|
|
@ -200,6 +200,12 @@ fn main() {
|
||||||
.arg(Arg::with_name("amount")
|
.arg(Arg::with_name("amount")
|
||||||
.help("Amount to send in the smallest denomination")
|
.help("Amount to send in the smallest denomination")
|
||||||
.index(1))
|
.index(1))
|
||||||
|
.arg(Arg::with_name("minimum_confirmations")
|
||||||
|
.help("Minimum number of confirmations required for an output to be spendable.")
|
||||||
|
.short("c")
|
||||||
|
.long("min_conf")
|
||||||
|
.default_value("1")
|
||||||
|
.takes_value(true))
|
||||||
.arg(Arg::with_name("dest")
|
.arg(Arg::with_name("dest")
|
||||||
.help("Send the transaction to the provided server")
|
.help("Send the transaction to the provided server")
|
||||||
.short("d")
|
.short("d")
|
||||||
|
@ -212,7 +218,13 @@ fn main() {
|
||||||
transactions.")
|
transactions.")
|
||||||
.arg(Arg::with_name("amount")
|
.arg(Arg::with_name("amount")
|
||||||
.help("Amount to burn in the smallest denomination")
|
.help("Amount to burn in the smallest denomination")
|
||||||
.index(1)))
|
.index(1))
|
||||||
|
.arg(Arg::with_name("minimum_confirmations")
|
||||||
|
.help("Minimum number of confirmations required for an output to be spendable.")
|
||||||
|
.short("c")
|
||||||
|
.long("min_conf")
|
||||||
|
.default_value("1")
|
||||||
|
.takes_value(true)))
|
||||||
|
|
||||||
.subcommand(SubCommand::with_name("info")
|
.subcommand(SubCommand::with_name("info")
|
||||||
.about("basic wallet info (outputs)")))
|
.about("basic wallet info (outputs)")))
|
||||||
|
@ -380,11 +392,22 @@ fn wallet_command(wallet_args: &ArgMatches) {
|
||||||
.expect("Amount to send required")
|
.expect("Amount to send required")
|
||||||
.parse()
|
.parse()
|
||||||
.expect("Could not parse amount as a whole number.");
|
.expect("Could not parse amount as a whole number.");
|
||||||
|
let minimum_confirmations: u64 = send_args
|
||||||
|
.value_of("minimum_confirmations")
|
||||||
|
.unwrap_or("1")
|
||||||
|
.parse()
|
||||||
|
.expect("Could not parse minimum_confirmations as a whole number.");
|
||||||
let mut dest = "stdout";
|
let mut dest = "stdout";
|
||||||
if let Some(d) = send_args.value_of("dest") {
|
if let Some(d) = send_args.value_of("dest") {
|
||||||
dest = d;
|
dest = d;
|
||||||
}
|
}
|
||||||
wallet::issue_send_tx(&wallet_config, &keychain, amount, dest.to_string()).unwrap();
|
wallet::issue_send_tx(
|
||||||
|
&wallet_config,
|
||||||
|
&keychain,
|
||||||
|
amount,
|
||||||
|
minimum_confirmations,
|
||||||
|
dest.to_string()
|
||||||
|
).unwrap();
|
||||||
}
|
}
|
||||||
("burn", Some(send_args)) => {
|
("burn", Some(send_args)) => {
|
||||||
let amount = send_args
|
let amount = send_args
|
||||||
|
@ -392,7 +415,17 @@ fn wallet_command(wallet_args: &ArgMatches) {
|
||||||
.expect("Amount to burn required")
|
.expect("Amount to burn required")
|
||||||
.parse()
|
.parse()
|
||||||
.expect("Could not parse amount as a whole number.");
|
.expect("Could not parse amount as a whole number.");
|
||||||
wallet::issue_burn_tx(&wallet_config, &keychain, amount).unwrap();
|
let minimum_confirmations: u64 = send_args
|
||||||
|
.value_of("minimum_confirmations")
|
||||||
|
.unwrap_or("1")
|
||||||
|
.parse()
|
||||||
|
.expect("Could not parse minimum_confirmations as a whole number.");
|
||||||
|
wallet::issue_burn_tx(
|
||||||
|
&wallet_config,
|
||||||
|
&keychain,
|
||||||
|
amount,
|
||||||
|
minimum_confirmations,
|
||||||
|
).unwrap();
|
||||||
}
|
}
|
||||||
("info", Some(_)) => {
|
("info", Some(_)) => {
|
||||||
wallet::show_info(&wallet_config, &keychain);
|
wallet::show_info(&wallet_config, &keychain);
|
||||||
|
|
|
@ -20,16 +20,12 @@ use types::*;
|
||||||
use keychain::Keychain;
|
use keychain::Keychain;
|
||||||
use util;
|
use util;
|
||||||
|
|
||||||
fn refresh_output(out: &mut OutputData, api_out: Option<api::Output>, tip: &api::Tip) {
|
fn refresh_output(out: &mut OutputData, api_out: Option<api::Output>) {
|
||||||
if let Some(api_out) = api_out {
|
if let Some(api_out) = api_out {
|
||||||
out.height = api_out.height;
|
out.height = api_out.height;
|
||||||
out.lock_height = api_out.lock_height;
|
out.lock_height = api_out.lock_height;
|
||||||
|
|
||||||
if out.status == OutputStatus::Locked {
|
if out.status != OutputStatus::Locked {
|
||||||
// leave it Locked locally for now
|
|
||||||
} else if api_out.lock_height > tip.height {
|
|
||||||
out.status = OutputStatus::Immature;
|
|
||||||
} else {
|
|
||||||
out.status = OutputStatus::Unspent;
|
out.status = OutputStatus::Unspent;
|
||||||
}
|
}
|
||||||
} else if vec![OutputStatus::Unspent, OutputStatus::Locked].contains(&out.status) {
|
} else if vec![OutputStatus::Unspent, OutputStatus::Locked].contains(&out.status) {
|
||||||
|
@ -39,9 +35,10 @@ fn refresh_output(out: &mut OutputData, api_out: Option<api::Output>, tip: &api:
|
||||||
|
|
||||||
/// Goes through the list of outputs that haven't been spent yet and check
|
/// Goes through the list of outputs that haven't been spent yet and check
|
||||||
/// with a node whether their status has changed.
|
/// with a node whether their status has changed.
|
||||||
pub fn refresh_outputs(config: &WalletConfig, keychain: &Keychain) -> Result<(), Error> {
|
pub fn refresh_outputs(
|
||||||
let tip = get_tip_from_node(config)?;
|
config: &WalletConfig,
|
||||||
|
keychain: &Keychain,
|
||||||
|
) -> Result<(), Error> {
|
||||||
WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
|
WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
|
||||||
// check each output that's not spent
|
// check each output that's not spent
|
||||||
for mut out in wallet_data.outputs.values_mut().filter(|out| {
|
for mut out in wallet_data.outputs.values_mut().filter(|out| {
|
||||||
|
@ -50,7 +47,7 @@ pub fn refresh_outputs(config: &WalletConfig, keychain: &Keychain) -> Result<(),
|
||||||
{
|
{
|
||||||
// TODO check the pool for unconfirmed
|
// TODO check the pool for unconfirmed
|
||||||
match get_output_from_node(config, keychain, out.value, out.n_child) {
|
match get_output_from_node(config, keychain, out.value, out.n_child) {
|
||||||
Ok(api_out) => refresh_output(&mut out, api_out, &tip),
|
Ok(api_out) => refresh_output(&mut out, api_out),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// TODO find error with connection and return
|
// TODO find error with connection and return
|
||||||
// error!(LOGGER, "Error contacting server node at {}. Is it running?",
|
// error!(LOGGER, "Error contacting server node at {}. Is it running?",
|
||||||
|
|
|
@ -23,8 +23,23 @@ pub fn show_info(config: &WalletConfig, keychain: &Keychain) {
|
||||||
// operate within a lock on wallet data
|
// operate within a lock on wallet data
|
||||||
let _ = WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
|
let _ = WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
|
||||||
|
|
||||||
|
// get the current height via the api
|
||||||
|
// if we cannot get the current height use the max height known to the wallet
|
||||||
|
let current_height = match checker::get_tip_from_node(config) {
|
||||||
|
Ok(tip) => tip.height,
|
||||||
|
Err(_) => {
|
||||||
|
match wallet_data.outputs.values().map(|out| out.height).max() {
|
||||||
|
Some(height) => height,
|
||||||
|
None => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// need to specify a default value here somehow
|
||||||
|
let minimum_confirmations = 1;
|
||||||
|
|
||||||
println!("Outputs - ");
|
println!("Outputs - ");
|
||||||
println!("key_id, height, lock_height, status, zero_ok, value");
|
println!("key_id, height, lock_height, status, spendable?, coinbase?, value");
|
||||||
println!("----------------------------------");
|
println!("----------------------------------");
|
||||||
|
|
||||||
let mut outputs = wallet_data
|
let mut outputs = wallet_data
|
||||||
|
@ -35,13 +50,14 @@ pub fn show_info(config: &WalletConfig, keychain: &Keychain) {
|
||||||
outputs.sort_by_key(|out| out.n_child);
|
outputs.sort_by_key(|out| out.n_child);
|
||||||
for out in outputs {
|
for out in outputs {
|
||||||
println!(
|
println!(
|
||||||
"{}, {}, {}, {:?}, {}, {}",
|
"{}, {}, {}, {:?}, {}, {}, {}",
|
||||||
out.key_id,
|
out.key_id,
|
||||||
out.height,
|
out.height,
|
||||||
out.lock_height,
|
out.lock_height,
|
||||||
out.status,
|
out.status,
|
||||||
out.zero_ok,
|
out.eligible_to_spend(current_height, minimum_confirmations),
|
||||||
out.value
|
out.is_coinbase,
|
||||||
|
out.value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -172,10 +172,11 @@ impl ApiEndpoint for WalletReceiver {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a coinbase output and the corresponding kernel
|
/// Build a coinbase output and the corresponding kernel
|
||||||
fn receive_coinbase(config: &WalletConfig,
|
fn receive_coinbase(
|
||||||
|
config: &WalletConfig,
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
block_fees: &BlockFees)
|
block_fees: &BlockFees
|
||||||
-> Result<(Output, TxKernel, BlockFees), Error> {
|
) -> Result<(Output, TxKernel, BlockFees), Error> {
|
||||||
let root_key_id = keychain.root_key_id();
|
let root_key_id = keychain.root_key_id();
|
||||||
|
|
||||||
// operate within a lock on wallet data
|
// operate within a lock on wallet data
|
||||||
|
@ -205,7 +206,7 @@ fn receive_coinbase(config: &WalletConfig,
|
||||||
status: OutputStatus::Unconfirmed,
|
status: OutputStatus::Unconfirmed,
|
||||||
height: 0,
|
height: 0,
|
||||||
lock_height: 0,
|
lock_height: 0,
|
||||||
zero_ok: false,
|
is_coinbase: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
|
@ -280,7 +281,7 @@ fn receive_transaction(
|
||||||
status: OutputStatus::Unconfirmed,
|
status: OutputStatus::Unconfirmed,
|
||||||
height: 0,
|
height: 0,
|
||||||
lock_height: 0,
|
lock_height: 0,
|
||||||
zero_ok: false,
|
is_coinbase: false,
|
||||||
});
|
});
|
||||||
debug!(
|
debug!(
|
||||||
LOGGER,
|
LOGGER,
|
||||||
|
|
|
@ -31,14 +31,25 @@ pub fn issue_send_tx(
|
||||||
config: &WalletConfig,
|
config: &WalletConfig,
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
|
minimum_confirmations: u64,
|
||||||
dest: String,
|
dest: String,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
checker::refresh_outputs(config, keychain)?;
|
checker::refresh_outputs(config, keychain)?;
|
||||||
|
|
||||||
let chain_tip = checker::get_tip_from_node(config)?;
|
let chain_tip = checker::get_tip_from_node(config)?;
|
||||||
|
let current_height = chain_tip.height;
|
||||||
|
|
||||||
|
// proof of concept - set lock_height on the tx
|
||||||
let lock_height = chain_tip.height;
|
let lock_height = chain_tip.height;
|
||||||
|
|
||||||
let (tx, blind_sum) = build_send_tx(config, keychain, amount, lock_height)?;
|
let (tx, blind_sum) = build_send_tx(
|
||||||
|
config,
|
||||||
|
keychain,
|
||||||
|
amount,
|
||||||
|
current_height,
|
||||||
|
minimum_confirmations,
|
||||||
|
lock_height,
|
||||||
|
)?;
|
||||||
let json_tx = partial_tx_to_json(amount, blind_sum, tx);
|
let json_tx = partial_tx_to_json(amount, blind_sum, tx);
|
||||||
|
|
||||||
if dest == "stdout" {
|
if dest == "stdout" {
|
||||||
|
@ -64,6 +75,8 @@ fn build_send_tx(
|
||||||
config: &WalletConfig,
|
config: &WalletConfig,
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
|
current_height: u64,
|
||||||
|
minimum_confirmations: u64,
|
||||||
lock_height: u64,
|
lock_height: u64,
|
||||||
) -> Result<(Transaction, BlindingFactor), Error> {
|
) -> Result<(Transaction, BlindingFactor), Error> {
|
||||||
let key_id = keychain.clone().root_key_id();
|
let key_id = keychain.clone().root_key_id();
|
||||||
|
@ -71,8 +84,8 @@ fn build_send_tx(
|
||||||
// operate within a lock on wallet data
|
// operate within a lock on wallet data
|
||||||
WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
|
WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
|
||||||
|
|
||||||
// select some suitable outputs to spend from our local wallet
|
// select some spendable coins from our local wallet
|
||||||
let (coins, _) = wallet_data.select(key_id.clone(), u64::max_value());
|
let coins = wallet_data.select(key_id.clone(), current_height, minimum_confirmations);
|
||||||
|
|
||||||
// build transaction skeleton with inputs and change
|
// build transaction skeleton with inputs and change
|
||||||
// TODO - should probably also check we are sending enough to cover the fees + non-zero output
|
// TODO - should probably also check we are sending enough to cover the fees + non-zero output
|
||||||
|
@ -89,9 +102,17 @@ fn build_send_tx(
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn issue_burn_tx(config: &WalletConfig, keychain: &Keychain, amount: u64) -> Result<(), Error> {
|
pub fn issue_burn_tx(
|
||||||
|
config: &WalletConfig,
|
||||||
|
keychain: &Keychain,
|
||||||
|
amount: u64,
|
||||||
|
minimum_confirmations: u64,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let keychain = &Keychain::burn_enabled(keychain, &Identifier::zero());
|
let keychain = &Keychain::burn_enabled(keychain, &Identifier::zero());
|
||||||
|
|
||||||
|
let chain_tip = checker::get_tip_from_node(config)?;
|
||||||
|
let current_height = chain_tip.height;
|
||||||
|
|
||||||
let _ = checker::refresh_outputs(config, keychain);
|
let _ = checker::refresh_outputs(config, keychain);
|
||||||
|
|
||||||
let key_id = keychain.root_key_id();
|
let key_id = keychain.root_key_id();
|
||||||
|
@ -99,8 +120,8 @@ pub fn issue_burn_tx(config: &WalletConfig, keychain: &Keychain, amount: u64) ->
|
||||||
// operate within a lock on wallet data
|
// operate within a lock on wallet data
|
||||||
WalletData::with_wallet(&config.data_file_dir, |mut wallet_data| {
|
WalletData::with_wallet(&config.data_file_dir, |mut wallet_data| {
|
||||||
|
|
||||||
// select all suitable outputs by passing largest amount
|
// select some spendable coins from the wallet
|
||||||
let (coins, _) = wallet_data.select(key_id.clone(), u64::max_value());
|
let coins = wallet_data.select(key_id.clone(), current_height, minimum_confirmations);
|
||||||
|
|
||||||
// build transaction skeleton with inputs and change
|
// build transaction skeleton with inputs and change
|
||||||
let mut parts = inputs_and_change(&coins, keychain, key_id, &mut wallet_data, amount)?;
|
let mut parts = inputs_and_change(&coins, keychain, key_id, &mut wallet_data, amount)?;
|
||||||
|
@ -170,7 +191,7 @@ fn inputs_and_change(
|
||||||
status: OutputStatus::Unconfirmed,
|
status: OutputStatus::Unconfirmed,
|
||||||
height: 0,
|
height: 0,
|
||||||
lock_height: 0,
|
lock_height: 0,
|
||||||
zero_ok: true,
|
is_coinbase: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// now lock the ouputs we're spending so we avoid accidental double spend attempt
|
// now lock the ouputs we're spending so we avoid accidental double spend attempt
|
||||||
|
|
|
@ -132,7 +132,6 @@ impl Default for WalletConfig {
|
||||||
pub enum OutputStatus {
|
pub enum OutputStatus {
|
||||||
Unconfirmed,
|
Unconfirmed,
|
||||||
Unspent,
|
Unspent,
|
||||||
Immature,
|
|
||||||
Locked,
|
Locked,
|
||||||
Spent,
|
Spent,
|
||||||
}
|
}
|
||||||
|
@ -142,7 +141,6 @@ impl fmt::Display for OutputStatus {
|
||||||
match *self {
|
match *self {
|
||||||
OutputStatus::Unconfirmed => write!(f, "Unconfirmed"),
|
OutputStatus::Unconfirmed => write!(f, "Unconfirmed"),
|
||||||
OutputStatus::Unspent => write!(f, "Unspent"),
|
OutputStatus::Unspent => write!(f, "Unspent"),
|
||||||
OutputStatus::Immature => write!(f, "Immature"),
|
|
||||||
OutputStatus::Locked => write!(f, "Locked"),
|
OutputStatus::Locked => write!(f, "Locked"),
|
||||||
OutputStatus::Spent => write!(f, "Spent"),
|
OutputStatus::Spent => write!(f, "Spent"),
|
||||||
}
|
}
|
||||||
|
@ -168,8 +166,8 @@ pub struct OutputData {
|
||||||
pub height: u64,
|
pub height: u64,
|
||||||
/// Height we are locked until
|
/// Height we are locked until
|
||||||
pub lock_height: u64,
|
pub lock_height: u64,
|
||||||
/// Can we spend with zero confirmations? (Did it originate from us, change output etc.)
|
/// Is this a coinbase output? Is it subject to coinbase locktime?
|
||||||
pub zero_ok: bool,
|
pub is_coinbase: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputData {
|
impl OutputData {
|
||||||
|
@ -177,6 +175,29 @@ impl OutputData {
|
||||||
fn lock(&mut self) {
|
fn lock(&mut self) {
|
||||||
self.status = OutputStatus::Locked;
|
self.status = OutputStatus::Locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn eligible_to_spend(
|
||||||
|
&self,
|
||||||
|
current_height: u64,
|
||||||
|
minimum_confirmations: u64
|
||||||
|
) -> bool {
|
||||||
|
if [
|
||||||
|
OutputStatus::Spent,
|
||||||
|
OutputStatus::Locked,
|
||||||
|
].contains(&self.status) {
|
||||||
|
return false;
|
||||||
|
} else if self.status == OutputStatus::Unconfirmed && self.is_coinbase {
|
||||||
|
return false;
|
||||||
|
} else if self.lock_height > current_height {
|
||||||
|
return false;
|
||||||
|
} else if self.status == OutputStatus::Unspent && self.height + minimum_confirmations <= current_height {
|
||||||
|
return true;
|
||||||
|
} else if self.status == OutputStatus::Unconfirmed && minimum_confirmations == 0 {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wallet information tracking all our outputs. Based on HD derivation and
|
/// Wallet information tracking all our outputs. Based on HD derivation and
|
||||||
|
@ -313,27 +334,22 @@ impl WalletData {
|
||||||
self.outputs.get(&key_id.to_hex())
|
self.outputs.get(&key_id.to_hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Select a subset of unspent outputs to spend in a transaction
|
/// Select spendable coins from the wallet
|
||||||
/// transferring the provided amount.
|
pub fn select(
|
||||||
pub fn select(&self, root_key_id: keychain::Identifier, amount: u64) -> (Vec<OutputData>, i64) {
|
&self,
|
||||||
let mut to_spend = vec![];
|
root_key_id: keychain::Identifier,
|
||||||
let mut input_total = 0;
|
current_height: u64,
|
||||||
|
minimum_confirmations: u64,
|
||||||
|
) -> Vec<OutputData> {
|
||||||
|
|
||||||
for out in self.outputs.values() {
|
self.outputs
|
||||||
if out.root_key_id == root_key_id
|
.values()
|
||||||
&& (out.status == OutputStatus::Unspent)
|
.filter(|out| {
|
||||||
// the following will let us spend zero confirmation change outputs
|
out.root_key_id == root_key_id
|
||||||
// || (out.status == OutputStatus::Unconfirmed && out.zero_ok))
|
&& out.eligible_to_spend(current_height, minimum_confirmations)
|
||||||
{
|
})
|
||||||
to_spend.push(out.clone());
|
.map(|out| out.clone())
|
||||||
input_total += out.value;
|
.collect()
|
||||||
if input_total >= amount {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO - clean up our handling of i64 vs u64 so we are consistent
|
|
||||||
(to_spend, (input_total as i64) - (amount as i64))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Next child index when we want to create a new output.
|
/// Next child index when we want to create a new output.
|
||||||
|
|
Loading…
Reference in a new issue