mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
Refactor tx lib to be more Sender-Recipient agnostic (#2502)
* move slate initialisation outside of selection function * refactor internal tx lib to support recipient (or anyone else) first models * rustfmt * rework init api function * rustfmt * refactor wallet lock closures to a defined fnmut type * rustfmt * comments for clarification
This commit is contained in:
parent
7646e71810
commit
130675e017
4 changed files with 187 additions and 192 deletions
|
@ -41,8 +41,8 @@ use crate::core::ser;
|
||||||
use crate::keychain::{Identifier, Keychain};
|
use crate::keychain::{Identifier, Keychain};
|
||||||
use crate::libwallet::internal::{keys, tx, updater};
|
use crate::libwallet::internal::{keys, tx, updater};
|
||||||
use crate::libwallet::types::{
|
use crate::libwallet::types::{
|
||||||
AcctPathMapping, BlockFees, CbData, NodeClient, OutputData, TxLogEntry, TxLogEntryType,
|
AcctPathMapping, BlockFees, CbData, NodeClient, OutputData, OutputLockFn, TxLogEntry,
|
||||||
TxWrapper, WalletBackend, WalletInfo,
|
TxLogEntryType, TxWrapper, WalletBackend, WalletInfo,
|
||||||
};
|
};
|
||||||
use crate::libwallet::{Error, ErrorKind};
|
use crate::libwallet::{Error, ErrorKind};
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
@ -623,13 +623,7 @@ where
|
||||||
num_change_outputs: usize,
|
num_change_outputs: usize,
|
||||||
selection_strategy_is_use_all: bool,
|
selection_strategy_is_use_all: bool,
|
||||||
message: Option<String>,
|
message: Option<String>,
|
||||||
) -> Result<
|
) -> Result<(Slate, OutputLockFn<W, C, K>), Error> {
|
||||||
(
|
|
||||||
Slate,
|
|
||||||
impl FnOnce(&mut W, &Transaction) -> Result<(), Error>,
|
|
||||||
),
|
|
||||||
Error,
|
|
||||||
> {
|
|
||||||
let mut w = self.wallet.lock();
|
let mut w = self.wallet.lock();
|
||||||
w.open_with_credentials()?;
|
w.open_with_credentials()?;
|
||||||
let parent_key_id = match src_acct_name {
|
let parent_key_id = match src_acct_name {
|
||||||
|
@ -651,14 +645,17 @@ where
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (slate, context, lock_fn) = tx::create_send_tx(
|
let mut slate = tx::new_tx_slate(&mut *w, amount, 2)?;
|
||||||
|
|
||||||
|
let (context, lock_fn) = tx::add_inputs_to_slate(
|
||||||
&mut *w,
|
&mut *w,
|
||||||
amount,
|
&mut slate,
|
||||||
minimum_confirmations,
|
minimum_confirmations,
|
||||||
max_outputs,
|
max_outputs,
|
||||||
num_change_outputs,
|
num_change_outputs,
|
||||||
selection_strategy_is_use_all,
|
selection_strategy_is_use_all,
|
||||||
&parent_key_id,
|
&parent_key_id,
|
||||||
|
0,
|
||||||
message,
|
message,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -678,11 +675,11 @@ where
|
||||||
pub fn tx_lock_outputs(
|
pub fn tx_lock_outputs(
|
||||||
&mut self,
|
&mut self,
|
||||||
slate: &Slate,
|
slate: &Slate,
|
||||||
lock_fn: impl FnOnce(&mut W, &Transaction) -> Result<(), Error>,
|
mut lock_fn: OutputLockFn<W, C, K>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut w = self.wallet.lock();
|
let mut w = self.wallet.lock();
|
||||||
w.open_with_credentials()?;
|
w.open_with_credentials()?;
|
||||||
lock_fn(&mut *w, &slate.tx)?;
|
lock_fn(&mut *w, &slate.tx, PhantomData, PhantomData)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,7 +691,7 @@ where
|
||||||
let mut w = self.wallet.lock();
|
let mut w = self.wallet.lock();
|
||||||
w.open_with_credentials()?;
|
w.open_with_credentials()?;
|
||||||
let context = w.get_private_context(slate.id.as_bytes())?;
|
let context = w.get_private_context(slate.id.as_bytes())?;
|
||||||
tx::complete_tx(&mut *w, slate, &context)?;
|
tx::complete_tx(&mut *w, slate, 0, &context)?;
|
||||||
tx::update_stored_tx(&mut *w, slate)?;
|
tx::update_stored_tx(&mut *w, slate)?;
|
||||||
tx::update_message(&mut *w, slate)?;
|
tx::update_message(&mut *w, slate)?;
|
||||||
{
|
{
|
||||||
|
@ -893,18 +890,11 @@ where
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = tx::receive_tx(&mut *w, slate, &parent_key_id, message);
|
let (_, mut create_fn) =
|
||||||
|
tx::add_output_to_slate(&mut *w, slate, &parent_key_id, 1, message)?;
|
||||||
|
create_fn(&mut *w, &slate.tx, PhantomData, PhantomData)?;
|
||||||
|
tx::update_message(&mut *w, slate)?;
|
||||||
w.close()?;
|
w.close()?;
|
||||||
|
Ok(())
|
||||||
if let Err(e) = res {
|
|
||||||
error!("api: receive_tx: failed with error: {}", e);
|
|
||||||
Err(e)
|
|
||||||
} else {
|
|
||||||
debug!(
|
|
||||||
"api: receive_tx: successfully received tx: {}",
|
|
||||||
slate.tx.hash()
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,60 +20,43 @@ use crate::keychain::{Identifier, Keychain};
|
||||||
use crate::libwallet::error::{Error, ErrorKind};
|
use crate::libwallet::error::{Error, ErrorKind};
|
||||||
use crate::libwallet::internal::keys;
|
use crate::libwallet::internal::keys;
|
||||||
use crate::libwallet::types::*;
|
use crate::libwallet::types::*;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
/// Initialize a transaction on the sender side, returns a corresponding
|
/// Initialize a transaction on the sender side, returns a corresponding
|
||||||
/// libwallet transaction slate with the appropriate inputs selected,
|
/// libwallet transaction slate with the appropriate inputs selected,
|
||||||
/// and saves the private wallet identifiers of our selected outputs
|
/// and saves the private wallet identifiers of our selected outputs
|
||||||
/// into our transaction context
|
/// into our transaction context
|
||||||
|
|
||||||
pub fn build_send_tx_slate<T: ?Sized, C, K>(
|
pub fn build_send_tx<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
num_participants: usize,
|
slate: &mut Slate,
|
||||||
amount: u64,
|
|
||||||
current_height: u64,
|
|
||||||
minimum_confirmations: u64,
|
minimum_confirmations: u64,
|
||||||
lock_height: u64,
|
|
||||||
max_outputs: usize,
|
max_outputs: usize,
|
||||||
change_outputs: usize,
|
change_outputs: usize,
|
||||||
selection_strategy_is_use_all: bool,
|
selection_strategy_is_use_all: bool,
|
||||||
parent_key_id: Identifier,
|
parent_key_id: Identifier,
|
||||||
) -> Result<
|
) -> Result<(Context, OutputLockFn<T, C, K>), Error>
|
||||||
(
|
|
||||||
Slate,
|
|
||||||
Context,
|
|
||||||
impl FnOnce(&mut T, &Transaction) -> Result<(), Error>,
|
|
||||||
),
|
|
||||||
Error,
|
|
||||||
>
|
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: NodeClient,
|
C: NodeClient,
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let (elems, inputs, change_amounts_derivations, amount, fee) = select_send_tx(
|
let (elems, inputs, change_amounts_derivations, fee) = select_send_tx(
|
||||||
wallet,
|
wallet,
|
||||||
amount,
|
slate.amount,
|
||||||
current_height,
|
slate.height,
|
||||||
minimum_confirmations,
|
minimum_confirmations,
|
||||||
lock_height,
|
slate.lock_height,
|
||||||
max_outputs,
|
max_outputs,
|
||||||
change_outputs,
|
change_outputs,
|
||||||
selection_strategy_is_use_all,
|
selection_strategy_is_use_all,
|
||||||
&parent_key_id,
|
&parent_key_id,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Create public slate
|
|
||||||
let mut slate = Slate::blank(num_participants);
|
|
||||||
slate.amount = amount;
|
|
||||||
slate.height = current_height;
|
|
||||||
slate.lock_height = lock_height;
|
|
||||||
slate.fee = fee;
|
slate.fee = fee;
|
||||||
let slate_id = slate.id.clone();
|
|
||||||
|
|
||||||
let keychain = wallet.keychain().clone();
|
let keychain = wallet.keychain().clone();
|
||||||
|
|
||||||
let blinding = slate.add_transaction_elements(&keychain, elems)?;
|
let blinding = slate.add_transaction_elements(&keychain, elems)?;
|
||||||
|
|
||||||
// Create our own private context
|
// Create our own private context
|
||||||
|
@ -98,79 +81,80 @@ where
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let lock_inputs = context.get_inputs().clone();
|
let lock_inputs_in = context.get_inputs().clone();
|
||||||
let _lock_outputs = context.get_outputs().clone();
|
let _lock_outputs = context.get_outputs().clone();
|
||||||
let messages = Some(slate.participant_messages());
|
let messages_in = Some(slate.participant_messages());
|
||||||
|
let slate_id_in = slate.id.clone();
|
||||||
|
let height_in = slate.height;
|
||||||
|
|
||||||
// Return a closure to acquire wallet lock and lock the coins being spent
|
// Return a closure to acquire wallet lock and lock the coins being spent
|
||||||
// so we avoid accidental double spend attempt.
|
// so we avoid accidental double spend attempt.
|
||||||
let update_sender_wallet_fn = move |wallet: &mut T, tx: &Transaction| {
|
let update_sender_wallet_fn =
|
||||||
let tx_entry = {
|
move |wallet: &mut T, tx: &Transaction, _: PhantomData<C>, _: PhantomData<K>| {
|
||||||
let mut batch = wallet.batch()?;
|
let tx_entry = {
|
||||||
let log_id = batch.next_tx_log_id(&parent_key_id)?;
|
// These ensure the closure remains FnMut
|
||||||
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxSent, log_id);
|
let lock_inputs = lock_inputs_in.clone();
|
||||||
t.tx_slate_id = Some(slate_id);
|
let messages = messages_in.clone();
|
||||||
let filename = format!("{}.grintx", slate_id);
|
let slate_id = slate_id_in.clone();
|
||||||
t.stored_tx = Some(filename);
|
let height = height_in.clone();
|
||||||
t.fee = Some(fee);
|
let mut batch = wallet.batch()?;
|
||||||
let mut amount_debited = 0;
|
let log_id = batch.next_tx_log_id(&parent_key_id)?;
|
||||||
t.num_inputs = lock_inputs.len();
|
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxSent, log_id);
|
||||||
for id in lock_inputs {
|
t.tx_slate_id = Some(slate_id.clone());
|
||||||
let mut coin = batch.get(&id.0, &id.1).unwrap();
|
let filename = format!("{}.grintx", slate_id);
|
||||||
coin.tx_log_entry = Some(log_id);
|
t.stored_tx = Some(filename);
|
||||||
amount_debited = amount_debited + coin.value;
|
t.fee = Some(fee);
|
||||||
batch.lock_output(&mut coin)?;
|
let mut amount_debited = 0;
|
||||||
}
|
t.num_inputs = lock_inputs.len();
|
||||||
|
for id in lock_inputs {
|
||||||
|
let mut coin = batch.get(&id.0, &id.1).unwrap();
|
||||||
|
coin.tx_log_entry = Some(log_id);
|
||||||
|
amount_debited = amount_debited + coin.value;
|
||||||
|
batch.lock_output(&mut coin)?;
|
||||||
|
}
|
||||||
|
|
||||||
t.amount_debited = amount_debited;
|
t.amount_debited = amount_debited;
|
||||||
t.messages = messages;
|
t.messages = messages;
|
||||||
|
|
||||||
// write the output representing our change
|
// write the output representing our change
|
||||||
for (change_amount, id, _) in &change_amounts_derivations {
|
for (change_amount, id, _) in &change_amounts_derivations {
|
||||||
t.num_outputs += 1;
|
t.num_outputs += 1;
|
||||||
t.amount_credited += change_amount;
|
t.amount_credited += change_amount;
|
||||||
let commit = commits.get(&id).unwrap().clone();
|
let commit = commits.get(&id).unwrap().clone();
|
||||||
batch.save(OutputData {
|
batch.save(OutputData {
|
||||||
root_key_id: parent_key_id.clone(),
|
root_key_id: parent_key_id.clone(),
|
||||||
key_id: id.clone(),
|
key_id: id.clone(),
|
||||||
n_child: id.to_path().last_path_index(),
|
n_child: id.to_path().last_path_index(),
|
||||||
commit: commit,
|
commit: commit,
|
||||||
mmr_index: None,
|
mmr_index: None,
|
||||||
value: change_amount.clone(),
|
value: change_amount.clone(),
|
||||||
status: OutputStatus::Unconfirmed,
|
status: OutputStatus::Unconfirmed,
|
||||||
height: current_height,
|
height: height,
|
||||||
lock_height: 0,
|
lock_height: 0,
|
||||||
is_coinbase: false,
|
is_coinbase: false,
|
||||||
tx_log_entry: Some(log_id),
|
tx_log_entry: Some(log_id),
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
batch.save_tx_log_entry(t.clone(), &parent_key_id)?;
|
batch.save_tx_log_entry(t.clone(), &parent_key_id)?;
|
||||||
batch.commit()?;
|
batch.commit()?;
|
||||||
t
|
t
|
||||||
|
};
|
||||||
|
wallet.store_tx(&format!("{}", tx_entry.tx_slate_id.unwrap()), tx)?;
|
||||||
|
Ok(())
|
||||||
};
|
};
|
||||||
wallet.store_tx(&format!("{}", tx_entry.tx_slate_id.unwrap()), tx)?;
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((slate, context, update_sender_wallet_fn))
|
Ok((context, Box::new(update_sender_wallet_fn)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new output in the wallet for the recipient,
|
/// Creates a new output in the wallet for the recipient,
|
||||||
/// returning the key of the fresh output and a closure
|
/// returning the key of the fresh output and a closure
|
||||||
/// that actually performs the addition of the output to the
|
/// that actually performs the addition of the output to the
|
||||||
/// wallet
|
/// wallet
|
||||||
pub fn build_recipient_output_with_slate<T: ?Sized, C, K>(
|
pub fn build_recipient_output<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
slate: &mut Slate,
|
slate: &mut Slate,
|
||||||
parent_key_id: Identifier,
|
parent_key_id: Identifier,
|
||||||
) -> Result<
|
) -> Result<(Identifier, Context, OutputLockFn<T, C, K>), Error>
|
||||||
(
|
|
||||||
Identifier,
|
|
||||||
Context,
|
|
||||||
impl FnOnce(&mut T) -> Result<(), Error>,
|
|
||||||
),
|
|
||||||
Error,
|
|
||||||
>
|
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: NodeClient,
|
C: NodeClient,
|
||||||
|
@ -197,37 +181,42 @@ where
|
||||||
);
|
);
|
||||||
|
|
||||||
context.add_output(&key_id, &None);
|
context.add_output(&key_id, &None);
|
||||||
let messages = Some(slate.participant_messages());
|
let messages_in = Some(slate.participant_messages());
|
||||||
|
|
||||||
// Create closure that adds the output to recipient's wallet
|
// Create closure that adds the output to recipient's wallet
|
||||||
// (up to the caller to decide when to do)
|
// (up to the caller to decide when to do)
|
||||||
let wallet_add_fn = move |wallet: &mut T| {
|
let wallet_add_fn =
|
||||||
let commit = wallet.calc_commit_for_cache(amount, &key_id_inner)?;
|
move |wallet: &mut T, _tx: &Transaction, _: PhantomData<C>, _: PhantomData<K>| {
|
||||||
let mut batch = wallet.batch()?;
|
// Ensure closure remains FnMut
|
||||||
let log_id = batch.next_tx_log_id(&parent_key_id)?;
|
let messages = messages_in.clone();
|
||||||
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxReceived, log_id);
|
let commit = wallet.calc_commit_for_cache(amount, &key_id_inner)?;
|
||||||
t.tx_slate_id = Some(slate_id);
|
let mut batch = wallet.batch()?;
|
||||||
t.amount_credited = amount;
|
let log_id = batch.next_tx_log_id(&parent_key_id)?;
|
||||||
t.num_outputs = 1;
|
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxReceived, log_id);
|
||||||
t.messages = messages;
|
t.tx_slate_id = Some(slate_id);
|
||||||
batch.save(OutputData {
|
t.amount_credited = amount;
|
||||||
root_key_id: parent_key_id.clone(),
|
t.num_outputs = 1;
|
||||||
key_id: key_id_inner.clone(),
|
t.messages = messages;
|
||||||
mmr_index: None,
|
batch.save(OutputData {
|
||||||
n_child: key_id_inner.to_path().last_path_index(),
|
root_key_id: parent_key_id.clone(),
|
||||||
commit: commit,
|
key_id: key_id_inner.clone(),
|
||||||
value: amount,
|
mmr_index: None,
|
||||||
status: OutputStatus::Unconfirmed,
|
n_child: key_id_inner.to_path().last_path_index(),
|
||||||
height: height,
|
commit: commit,
|
||||||
lock_height: 0,
|
value: amount,
|
||||||
is_coinbase: false,
|
status: OutputStatus::Unconfirmed,
|
||||||
tx_log_entry: Some(log_id),
|
height: height,
|
||||||
})?;
|
lock_height: 0,
|
||||||
batch.save_tx_log_entry(t, &parent_key_id)?;
|
is_coinbase: false,
|
||||||
batch.commit()?;
|
tx_log_entry: Some(log_id),
|
||||||
Ok(())
|
})?;
|
||||||
};
|
batch.save_tx_log_entry(t, &parent_key_id)?;
|
||||||
Ok((key_id, context, wallet_add_fn))
|
batch.commit()?;
|
||||||
|
//TODO: Check whether we want to call this
|
||||||
|
//wallet.store_tx(&format!("{}", t.tx_slate_id.unwrap()), tx)?;
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
Ok((key_id, context, Box::new(wallet_add_fn)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a transaction to send to someone from the HD seed associated with the
|
/// Builds a transaction to send to someone from the HD seed associated with the
|
||||||
|
@ -248,7 +237,6 @@ pub fn select_send_tx<T: ?Sized, C, K>(
|
||||||
Vec<Box<build::Append<K>>>,
|
Vec<Box<build::Append<K>>>,
|
||||||
Vec<OutputData>,
|
Vec<OutputData>,
|
||||||
Vec<(u64, Identifier, Option<u64>)>, // change amounts and derivations
|
Vec<(u64, Identifier, Option<u64>)>, // change amounts and derivations
|
||||||
u64, // amount
|
|
||||||
u64, // fee
|
u64, // fee
|
||||||
),
|
),
|
||||||
Error,
|
Error,
|
||||||
|
@ -345,7 +333,7 @@ where
|
||||||
// on tx being sent (based on current chain height via api).
|
// on tx being sent (based on current chain height via api).
|
||||||
parts.push(build::with_lock_height(lock_height));
|
parts.push(build::with_lock_height(lock_height));
|
||||||
|
|
||||||
Ok((parts, coins, change_amounts_derivations, amount, fee))
|
Ok((parts, coins, change_amounts_derivations, fee))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Selects inputs and change for a transaction
|
/// Selects inputs and change for a transaction
|
||||||
|
|
|
@ -16,80 +16,52 @@
|
||||||
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::core::core::Transaction;
|
|
||||||
use crate::core::libtx::slate::Slate;
|
use crate::core::libtx::slate::Slate;
|
||||||
use crate::keychain::{Identifier, Keychain};
|
use crate::keychain::{Identifier, Keychain};
|
||||||
use crate::libwallet::internal::{selection, updater};
|
use crate::libwallet::internal::{selection, updater};
|
||||||
use crate::libwallet::types::{Context, NodeClient, TxLogEntryType, WalletBackend};
|
use crate::libwallet::types::{Context, NodeClient, OutputLockFn, TxLogEntryType, WalletBackend};
|
||||||
use crate::libwallet::{Error, ErrorKind};
|
use crate::libwallet::{Error, ErrorKind};
|
||||||
|
|
||||||
/// Receive a transaction, modifying the slate accordingly (which can then be
|
/// Creates a new slate for a transaction, can be called by anyone involved in
|
||||||
/// sent back to sender for posting)
|
/// the transaction (sender(s), receiver(s))
|
||||||
pub fn receive_tx<T: ?Sized, C, K>(
|
pub fn new_tx_slate<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
slate: &mut Slate,
|
amount: u64,
|
||||||
parent_key_id: &Identifier,
|
num_participants: usize,
|
||||||
message: Option<String>,
|
) -> Result<Slate, Error>
|
||||||
) -> Result<(), Error>
|
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: NodeClient,
|
C: NodeClient,
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// create an output using the amount in the slate
|
let current_height = wallet.w2n_client().get_chain_height()?;
|
||||||
let (_, mut context, receiver_create_fn) =
|
let mut slate = Slate::blank(num_participants);
|
||||||
selection::build_recipient_output_with_slate(wallet, slate, parent_key_id.clone())?;
|
slate.amount = amount;
|
||||||
// fill public keys
|
slate.height = current_height;
|
||||||
let _ = slate.fill_round_1(
|
slate.lock_height = current_height;
|
||||||
wallet.keychain(),
|
Ok(slate)
|
||||||
&mut context.sec_key,
|
|
||||||
&context.sec_nonce,
|
|
||||||
1,
|
|
||||||
message,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// perform partial sig
|
|
||||||
let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 1)?;
|
|
||||||
|
|
||||||
// Save output in wallet
|
|
||||||
let _ = receiver_create_fn(wallet);
|
|
||||||
|
|
||||||
update_message(wallet, slate)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Issue a new transaction to the provided sender by spending some of our
|
/// Add inputs to the slate (effectively becoming the sender)
|
||||||
/// wallet
|
pub fn add_inputs_to_slate<T: ?Sized, C, K>(
|
||||||
pub fn create_send_tx<T: ?Sized, C, K>(
|
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
amount: u64,
|
slate: &mut Slate,
|
||||||
minimum_confirmations: u64,
|
minimum_confirmations: u64,
|
||||||
max_outputs: usize,
|
max_outputs: usize,
|
||||||
num_change_outputs: usize,
|
num_change_outputs: usize,
|
||||||
selection_strategy_is_use_all: bool,
|
selection_strategy_is_use_all: bool,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
|
participant_id: usize,
|
||||||
message: Option<String>,
|
message: Option<String>,
|
||||||
) -> Result<
|
) -> Result<(Context, OutputLockFn<T, C, K>), Error>
|
||||||
(
|
|
||||||
Slate,
|
|
||||||
Context,
|
|
||||||
impl FnOnce(&mut T, &Transaction) -> Result<(), Error>,
|
|
||||||
),
|
|
||||||
Error,
|
|
||||||
>
|
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: NodeClient,
|
C: NodeClient,
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// Get lock height
|
// sender should always refresh outputs
|
||||||
let current_height = wallet.w2n_client().get_chain_height()?;
|
|
||||||
// ensure outputs we're selecting are up to date
|
|
||||||
updater::refresh_outputs(wallet, parent_key_id, false)?;
|
updater::refresh_outputs(wallet, parent_key_id, false)?;
|
||||||
|
|
||||||
let lock_height = current_height;
|
|
||||||
|
|
||||||
// Sender selects outputs into a new slate and save our corresponding keys in
|
// Sender selects outputs into a new slate and save our corresponding keys in
|
||||||
// a transaction context. The secret key in our transaction context will be
|
// a transaction context. The secret key in our transaction context will be
|
||||||
// randomly selected. This returns the public slate, and a closure that locks
|
// randomly selected. This returns the public slate, and a closure that locks
|
||||||
|
@ -97,13 +69,10 @@ where
|
||||||
// according to plan
|
// according to plan
|
||||||
// This function is just a big helper to do all of that, in theory
|
// This function is just a big helper to do all of that, in theory
|
||||||
// this process can be split up in any way
|
// this process can be split up in any way
|
||||||
let (mut slate, mut context, sender_lock_fn) = selection::build_send_tx_slate(
|
let (mut context, sender_lock_fn) = selection::build_send_tx(
|
||||||
wallet,
|
wallet,
|
||||||
2,
|
slate,
|
||||||
amount,
|
|
||||||
current_height,
|
|
||||||
minimum_confirmations,
|
minimum_confirmations,
|
||||||
lock_height,
|
|
||||||
max_outputs,
|
max_outputs,
|
||||||
num_change_outputs,
|
num_change_outputs,
|
||||||
selection_strategy_is_use_all,
|
selection_strategy_is_use_all,
|
||||||
|
@ -117,17 +86,55 @@ where
|
||||||
wallet.keychain(),
|
wallet.keychain(),
|
||||||
&mut context.sec_key,
|
&mut context.sec_key,
|
||||||
&context.sec_nonce,
|
&context.sec_nonce,
|
||||||
0,
|
participant_id,
|
||||||
message,
|
message,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok((slate, context, sender_lock_fn))
|
Ok((context, sender_lock_fn))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add outputs to the slate, becoming the recipient
|
||||||
|
pub fn add_output_to_slate<T: ?Sized, C, K>(
|
||||||
|
wallet: &mut T,
|
||||||
|
slate: &mut Slate,
|
||||||
|
parent_key_id: &Identifier,
|
||||||
|
participant_id: usize,
|
||||||
|
message: Option<String>,
|
||||||
|
) -> Result<(Context, OutputLockFn<T, C, K>), Error>
|
||||||
|
where
|
||||||
|
T: WalletBackend<C, K>,
|
||||||
|
C: NodeClient,
|
||||||
|
K: Keychain,
|
||||||
|
{
|
||||||
|
// create an output using the amount in the slate
|
||||||
|
let (_, mut context, create_fn) =
|
||||||
|
selection::build_recipient_output(wallet, slate, parent_key_id.clone())?;
|
||||||
|
|
||||||
|
// fill public keys
|
||||||
|
let _ = slate.fill_round_1(
|
||||||
|
wallet.keychain(),
|
||||||
|
&mut context.sec_key,
|
||||||
|
&context.sec_nonce,
|
||||||
|
1,
|
||||||
|
message,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// perform partial sig
|
||||||
|
let _ = slate.fill_round_2(
|
||||||
|
wallet.keychain(),
|
||||||
|
&context.sec_key,
|
||||||
|
&context.sec_nonce,
|
||||||
|
participant_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok((context, create_fn))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete a transaction as the sender
|
/// Complete a transaction as the sender
|
||||||
pub fn complete_tx<T: ?Sized, C, K>(
|
pub fn complete_tx<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
slate: &mut Slate,
|
slate: &mut Slate,
|
||||||
|
participant_id: usize,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
|
@ -135,7 +142,12 @@ where
|
||||||
C: NodeClient,
|
C: NodeClient,
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 0)?;
|
let _ = slate.fill_round_2(
|
||||||
|
wallet.keychain(),
|
||||||
|
&context.sec_key,
|
||||||
|
&context.sec_nonce,
|
||||||
|
participant_id,
|
||||||
|
)?;
|
||||||
// Final transaction can be built by anyone at this stage
|
// Final transaction can be built by anyone at this stage
|
||||||
let res = slate.finalize(wallet.keychain());
|
let res = slate.finalize(wallet.keychain());
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
|
|
|
@ -30,8 +30,13 @@ use serde;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
/// Lock function type
|
||||||
|
pub type OutputLockFn<T, C, K> =
|
||||||
|
Box<dyn FnMut(&mut T, &Transaction, PhantomData<C>, PhantomData<K>) -> Result<(), Error>>;
|
||||||
|
|
||||||
/// Combined trait to allow dynamic wallet dispatch
|
/// Combined trait to allow dynamic wallet dispatch
|
||||||
pub trait WalletInst<C, K>: WalletBackend<C, K> + Send + Sync + 'static
|
pub trait WalletInst<C, K>: WalletBackend<C, K> + Send + Sync + 'static
|
||||||
where
|
where
|
||||||
|
|
Loading…
Reference in a new issue