Final Owner API Changes (#32)

* node height return value and documentation

* rustfmt

* add parameter struct for initiate tx functions

* rustfmt

* change tx estimate to use InitTxArgs

* rustfmt

* transaction estimate

* rustfmt

* initiate tx args fixed

* add send args to init

* rustfmt

* last owner api documentation
This commit is contained in:
Yeastplume 2019-03-29 16:00:02 +00:00 committed by GitHub
parent 7b8fe92f53
commit 427f42c552
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 698 additions and 632 deletions

View file

@ -16,7 +16,7 @@
use crate::keychain::Keychain;
use crate::libwallet::slate::Slate;
use crate::libwallet::types::{BlockFees, CbData, NodeClient, WalletBackend};
use crate::libwallet::types::{BlockFees, CbData, InitTxArgs, NodeClient, WalletBackend};
use crate::libwallet::ErrorKind;
use crate::Foreign;
use easy_jsonrpc;
@ -416,16 +416,16 @@ pub fn run_doctest_foreign(
let amount = 60_000_000_000;
let mut w = wallet1.lock();
w.open_with_credentials().unwrap();
let slate = api_impl::owner::initiate_tx(
&mut *w, None, // account
amount, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None, None, true,
)
.unwrap();
let args = InitTxArgs {
src_acct_name: None,
amount,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let slate = api_impl::owner::initiate_tx(&mut *w, args, true).unwrap();
println!("INIT SLATE");
// Spit out slate for input to finalize_tx
println!("{}", serde_json::to_string_pretty(&slate).unwrap());

View file

@ -25,6 +25,7 @@
use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_keychain as keychain;
use grin_wallet_util::grin_util as util;
extern crate grin_wallet_impls as impls;
extern crate grin_wallet_libwallet as libwallet;
extern crate failure_derive;

View file

@ -21,14 +21,15 @@ use std::sync::Arc;
use uuid::Uuid;
use crate::core::core::Transaction;
use crate::impls::{HTTPWalletCommAdapter, KeybaseWalletCommAdapter};
use crate::keychain::{Identifier, Keychain};
use crate::libwallet::api_impl::owner;
use crate::libwallet::slate::Slate;
use crate::libwallet::types::{
AcctPathMapping, NodeClient, OutputCommitMapping, TxEstimation, TxLogEntry, WalletBackend,
WalletInfo,
AcctPathMapping, InitTxArgs, NodeClient, NodeHeightResult, OutputCommitMapping, TxLogEntry,
WalletBackend, WalletInfo,
};
use crate::libwallet::Error;
use crate::libwallet::{Error, ErrorKind};
/// Main interface into all wallet API functions.
/// Wallet APIs are split into two seperate blocks of functionality
@ -431,37 +432,15 @@ where
/// as via file transfer,) the lock call should happen immediately (before the file is sent
/// to the recipient).
///
/// If the `send_args` [`InitTxSendArgs`](../grin_wallet_libwallet/types/struct.InitTxSendArgs.html),
/// of the [`args`](../grin_wallet_libwallet/types/struct.InitTxArgs.html), field is Some, this
/// function will attempt to perform a synchronous send to the recipient specified in the `dest`
/// field according to the `method` field, and will also finalize and post the transaction if
/// the `finalize` field is set.
///
/// # Arguments
/// * `src_acct_name` - The human readable account name from which to draw outputs
/// for the transaction, overriding whatever the active account is as set via the
/// [`set_active_account`](struct.Owner.html#method.set_active_account) method.
/// If None, the transaction will use the active account.
/// * `amount` - The amount to send, in nanogrins. (`1 G = 1_000_000_000nG`)
/// * `minimum_confirmations` - The minimum number of confirmations an output
/// should have in order to be included in the transaction.
/// * `max_outputs` - By default, the wallet selects as many inputs as possible in a
/// transaction, to reduce the Output set and the fees. The wallet will attempt to spend
/// include up to `max_outputs` in a transaction, however if this is not enough to cover
/// the whole amount, the wallet will include more outputs. This parameter should be considered
/// a soft limit.
/// * `num_change_outputs` - The target number of change outputs to create in the transaction.
/// The actual number created will be `num_change_outputs` + whatever remainder is needed.
/// * `selection_strategy_is_use_all` - If `true`, attempt to use up as many outputs as
/// possible to create the transaction, up the 'soft limit' of `max_outputs`. This helps
/// to reduce the size of the UTXO set and the amount of data stored in the wallet, and
/// minimizes fees. This will generally result in many inputs and a large change output(s),
/// usually much larger than the amount being sent. If `false`, the transaction will include
/// as many outputs as are needed to meet the amount, (and no more) starting with the smallest
/// value outputs.
/// * `message` - An optional participant message to include alongside the sender's public
/// ParticipantData within the slate. This message will include a signature created with the
/// sender's private excess value, and will be publically verifiable. Note this message is for
/// the convenience of the participants during the exchange; it is not included in the final
/// transaction sent to the chain. The message will be truncated to 256 characters.
/// Validation of this message is optional.
/// * `target_slate_version` Optionally set the output target slate version (acceptable
/// down to the minimum slate version compatible with the current. If `None` the slate
/// is generated with the latest version.
/// * `args` - [`InitTxArgs`](../grin_wallet_libwallet/types/struct.InitTxArgs.html),
/// transaction initialization arguments. See struct documentation for further detail.
///
/// # Returns
/// * a result containing:
@ -486,111 +465,69 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let amount = 2_000_000_000;
///
/// // Attempt to create a transaction using the 'default' account
/// let args = InitTxArgs {
/// src_acct_name: None,
/// amount: 2_000_000_000,
/// minimum_confirmations: 2,
/// max_outputs: 500,
/// num_change_outputs: 1,
/// selection_strategy_is_use_all: true,
/// message: Some("Have some Grins. Love, Yeastplume".to_owned()),
/// ..Default::default()
/// };
/// let result = api_owner.initiate_tx(
/// None,
/// amount, // amount
/// 10, // minimum confirmations
/// 500, // max outputs
/// 1, // num change outputs
/// true, // select all outputs
/// Some("Have some Grins. Love, Yeastplume".to_owned()),
/// None, // Use the default slate version
/// );
/// args,
/// );
///
/// if let Ok(slate) = result {
/// // Send slate somehow
/// // ...
/// // Lock our outputs if we're happy the slate was (or is being) sent
/// api_owner.tx_lock_outputs(&slate);
/// // Send slate somehow
/// // ...
/// // Lock our outputs if we're happy the slate was (or is being) sent
/// api_owner.tx_lock_outputs(&slate);
/// }
/// ```
pub fn initiate_tx(
&self,
src_acct_name: Option<&str>,
amount: u64,
minimum_confirmations: u64,
max_outputs: usize,
num_change_outputs: usize,
selection_strategy_is_use_all: bool,
message: Option<String>,
target_slate_version: Option<u16>,
) -> Result<Slate, Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let res = owner::initiate_tx(
&mut *w,
src_acct_name,
amount,
minimum_confirmations,
max_outputs,
num_change_outputs,
selection_strategy_is_use_all,
message,
target_slate_version,
self.doctest_mode,
);
w.close()?;
res
}
pub fn initiate_tx(&self, args: InitTxArgs) -> Result<Slate, Error> {
let send_args = args.send_args.clone();
let mut slate = {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let slate = owner::initiate_tx(&mut *w, args, self.doctest_mode)?;
w.close()?;
slate
};
// Helper functionality. If send arguments exist, attempt to send
match send_args {
Some(sa) => {
match sa.method.as_ref() {
"http" => {
slate = HTTPWalletCommAdapter::new().send_tx_sync(&sa.dest, &slate)?
}
"keybase" => {
//TODO: in case of keybase, the response might take 60s and leave the service hanging
slate = KeybaseWalletCommAdapter::new().send_tx_sync(&sa.dest, &slate)?;
}
_ => {
error!("unsupported payment method: {}", sa.method);
return Err(ErrorKind::ClientCallback(
"unsupported payment method".to_owned(),
))?;
}
}
self.tx_lock_outputs(&slate)?;
let slate = match sa.finalize {
true => self.finalize_tx(&slate)?,
false => slate,
};
/// Estimates the amount to be locked and fee for the transaction without creating one.
///
/// # Arguments
/// * As found in [`initiate_tx`](struct.Owner.html#method.initiate_tx) above.
///
/// # Returns
/// * a result containing a
/// [`TxEstimation`](../grin_wallet_libwallet/types/struct.TxEstimation.html)
///
/// # Example
/// Set up as in [new](struct.Owner.html#method.new) method above.
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let amount = 2_000_000_000;
///
/// // Estimate transaction using default account
/// let result = api_owner.estimate_initiate_tx(
/// None,
/// amount, // amount
/// 10, // minimum confirmations
/// 500, // max outputs
/// 1, // num change outputs
/// true, // select all outputs
/// );
///
/// if let Ok(est) = result {
/// // ...
/// }
/// ```
pub fn estimate_initiate_tx(
&self,
src_acct_name: Option<&str>,
amount: u64,
minimum_confirmations: u64,
max_outputs: usize,
num_change_outputs: usize,
selection_strategy_is_use_all: bool,
) -> Result<TxEstimation, Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let res = owner::estimate_initiate_tx(
&mut *w,
src_acct_name,
amount,
minimum_confirmations,
max_outputs,
num_change_outputs,
selection_strategy_is_use_all,
);
w.close()?;
res
if sa.post_tx {
self.post_tx(&slate.tx, sa.fluff)?;
}
Ok(slate)
}
None => Ok(slate),
}
}
/// Locks the outputs associated with the inputs to the transaction in the given
@ -621,19 +558,19 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let amount = 2_000_000_000;
///
/// // Attempt to create a transaction using the 'default' account
/// let args = InitTxArgs {
/// src_acct_name: None,
/// amount: 2_000_000_000,
/// minimum_confirmations: 10,
/// max_outputs: 500,
/// num_change_outputs: 1,
/// selection_strategy_is_use_all: true,
/// message: Some("Remember to lock this when we're happy this is sent".to_owned()),
/// ..Default::default()
/// };
/// let result = api_owner.initiate_tx(
/// None,
/// amount, // amount
/// 10, // minimum confirmations
/// 500, // max outputs
/// 1, // num change outputs
/// true, // select all outputs
/// Some("Remember to lock when we're happy this is sent".to_owned()),
/// None, // Use the default slate version
/// );
/// args,
/// );
///
/// if let Ok(slate) = result {
/// // Send slate somehow
@ -679,19 +616,19 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let amount = 2_000_000_000;
///
/// // Attempt to create a transaction using the 'default' account
/// let args = InitTxArgs {
/// src_acct_name: None,
/// amount: 2_000_000_000,
/// minimum_confirmations: 10,
/// max_outputs: 500,
/// num_change_outputs: 1,
/// selection_strategy_is_use_all: true,
/// message: Some("Finalize this tx now".to_owned()),
/// ..Default::default()
/// };
/// let result = api_owner.initiate_tx(
/// None,
/// amount, // amount
/// 10, // minimum confirmations
/// 500, // max outputs
/// 1, // num change outputs
/// true, // select all outputs
/// Some("Finalize this tx now".to_owned()),
/// None, // Use the default slate version
/// );
/// args,
/// );
///
/// if let Ok(slate) = result {
/// // Send slate somehow
@ -736,19 +673,19 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let amount = 2_000_000_000;
///
/// // Attempt to create a transaction using the 'default' account
/// let args = InitTxArgs {
/// src_acct_name: None,
/// amount: 2_000_000_000,
/// minimum_confirmations: 10,
/// max_outputs: 500,
/// num_change_outputs: 1,
/// selection_strategy_is_use_all: true,
/// message: Some("Post this tx".to_owned()),
/// ..Default::default()
/// };
/// let result = api_owner.initiate_tx(
/// None,
/// amount, // amount
/// 10, // minimum confirmations
/// 500, // max outputs
/// 1, // num change outputs
/// true, // select all outputs
/// Some("Finalize this tx now".to_owned()),
/// None, // Use the default slate version
/// );
/// args,
/// );
///
/// if let Ok(slate) = result {
/// // Send slate somehow
@ -797,19 +734,19 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let amount = 2_000_000_000;
///
/// // Attempt to create a transaction using the 'default' account
/// let args = InitTxArgs {
/// src_acct_name: None,
/// amount: 2_000_000_000,
/// minimum_confirmations: 10,
/// max_outputs: 500,
/// num_change_outputs: 1,
/// selection_strategy_is_use_all: true,
/// message: Some("Cancel this tx".to_owned()),
/// ..Default::default()
/// };
/// let result = api_owner.initiate_tx(
/// None,
/// amount, // amount
/// 10, // minimum confirmations
/// 500, // max outputs
/// 1, // num change outputs
/// true, // select all outputs
/// Some("Cancel this tx".to_owned()),
/// None, // Use the default slate version
/// );
/// args,
/// );
///
/// if let Ok(slate) = result {
/// // Send slate somehow
@ -890,19 +827,19 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let amount = 2_000_000_000;
///
/// // Attempt to create a transaction using the 'default' account
/// let args = InitTxArgs {
/// src_acct_name: None,
/// amount: 2_000_000_000,
/// minimum_confirmations: 10,
/// max_outputs: 500,
/// num_change_outputs: 1,
/// selection_strategy_is_use_all: true,
/// message: Some("Just verify messages".to_owned()),
/// ..Default::default()
/// };
/// let result = api_owner.initiate_tx(
/// None,
/// amount, // amount
/// 10, // minimum confirmations
/// 500, // max outputs
/// 1, // num change outputs
/// true, // select all outputs
/// Some("Finalize this tx now".to_owned()),
/// None, // Use the default slate version
/// );
/// args,
/// );
///
/// if let Ok(slate) = result {
/// // Send slate somehow
@ -919,8 +856,40 @@ where
owner::verify_slate_messages(slate)
}
/// Attempt to restore contents of wallet
/// TODO: Full docs
/// Scans the entire UTXO set from the node, creating outputs for each scanned
/// output that matches the wallet's master seed. This function is intended to be called as part
/// of a recovery process (either from BIP32 phrase or backup seed files,) and will error if the
/// wallet is non-empty, i.e. contains any outputs at all.
///
/// This operation scans the entire chain, and is expected to be time intensive. It is imperative
/// that no other processes should be trying to use the wallet at the same time this function is
/// running.
///
/// A single [TxLogEntry](../grin_wallet_libwallet/types/struct.TxLogEntry.html) is created for
/// all non-coinbase outputs discovered and restored during this process. A separate entry
/// is created for each coinbase output.
///
/// # Arguments
///
/// * None
///
/// # Returns
/// * `Ok(())` if successful
/// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered.
/// # Example
/// Set up as in [`new`](struct.Owner.html#method.new) method above.
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let result = api_owner.restore();
///
/// if let Ok(_) = result {
/// // Wallet outputs should be consistent with what's on chain
/// // ...
/// }
/// ```
pub fn restore(&self) -> Result<(), Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
@ -929,8 +898,52 @@ where
res
}
/// Attempt to check and fix the contents of the wallet
/// TODO: Full docs
/// Scans the entire UTXO set from the node, identify which outputs belong to the given wallet
/// update the wallet state to be consistent with what's currently in the UTXO set.
///
/// This function can be used to repair wallet state, particularly by restoring outputs that may
/// be missing if the wallet owner has cancelled transactions locally that were then successfully
/// posted to the chain.
///
/// This operation scans the entire chain, and is expected to be time intensive. It is imperative
/// that no other processes should be trying to use the wallet at the same time this function is
/// running.
///
/// When an output is found that doesn't exist in the wallet, a corresponding
/// [TxLogEntry](../grin_wallet_libwallet/types/struct.TxLogEntry.html) is created.
///
/// # Arguments
///
/// * `delete_unconfirmed` - if `false`, the check_repair process will be non-destructive, and
/// mostly limited to restoring missing outputs. It will leave unconfirmed transaction logs entries
/// and unconfirmed outputs intact. If `true`, the process will unlock all locked outputs,
/// restore all missing outputs, and mark any outputs that have been marked 'Spent' but are still
/// in the UTXO set as 'Unspent' (as can happen during a fork). It will also attempt to cancel any
/// transaction log entries associated with any locked outputs or outputs incorrectly marked 'Spent'.
/// Note this completely removes all outstanding transactions, so users should be very aware what
/// will happen if this flag is set. Note that if transactions/outputs are removed that later
/// confirm on the chain, another call to this function will restore them.
///
/// # Returns
/// * `Ok(())` if successful
/// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered.
/// # Example
/// Set up as in [`new`](struct.Owner.html#method.new) method above.
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let result = api_owner.check_repair(
/// false,
/// );
///
/// if let Ok(_) = result {
/// // Wallet outputs should be consistent with what's on chain
/// // ...
/// }
/// ```
pub fn check_repair(&self, delete_unconfirmed: bool) -> Result<(), Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
@ -939,9 +952,44 @@ where
res
}
/// Retrieve current height from node
// TODO: Should return u64 as string
pub fn node_height(&self) -> Result<(u64, bool), Error> {
/// Retrieves the last known height known by the wallet. This is determined as follows:
/// * If the wallet can successfully contact its configured node, the reported node
/// height is returned, and the `updated_from_node` field in the response is `true`
/// * If the wallet cannot contact the node, this function returns the maximum height
/// of all outputs contained within the wallet, and the `updated_from_node` fields
/// in the response is set to false.
///
/// Clients should generally ensure the `updated_from_node` field is returned as
/// `true` before assuming the height for any operation.
///
/// # Arguments
///
/// * None
///
/// # Returns
/// * Ok with a [`NodeHeightResult`](../grin_wallet_libwallet/types/struct.NodeHeightResult.html)
/// if successful. If the height result was obtained from the configured node,
/// `updated_from_node` will be set to `true`
/// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered.
///
/// # Example
/// Set up as in [`new`](struct.Owner.html#method.new) method above.
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone());
/// let result = api_owner.node_height();
///
/// if let Ok(node_height_result) = result {
/// if node_height_result.updated_from_node {
/// //we can assume node_height_result.height is relatively safe to use
///
/// }
/// //...
/// }
/// ```
pub fn node_height(&self) -> Result<NodeHeightResult, Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let res = owner::node_height(&mut *w);
@ -970,7 +1018,7 @@ macro_rules! doctest_helper_setup_doc_env {
use api::Owner;
use config::WalletConfig;
use impls::{HTTPNodeClient, LMDBBackend, WalletSeed};
use libwallet::types::WalletBackend;
use libwallet::types::{InitTxArgs, WalletBackend};
let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
let dir = dir

View file

@ -19,8 +19,8 @@ use crate::core::core::Transaction;
use crate::keychain::{Identifier, Keychain};
use crate::libwallet::slate::Slate;
use crate::libwallet::types::{
AcctPathMapping, NodeClient, OutputCommitMapping, TxEstimation, TxLogEntry, WalletBackend,
WalletInfo,
AcctPathMapping, InitTxArgs, NodeClient, NodeHeightResult, OutputCommitMapping, TxLogEntry,
WalletBackend, WalletInfo,
};
use crate::libwallet::ErrorKind;
use crate::Owner;
@ -321,7 +321,19 @@ pub trait OwnerRpc {
{
"jsonrpc": "2.0",
"method": "initiate_tx",
"params": [null, 6000000000, 2, 500, 1, true, "my message", null],
"params": {
"args": {
"src_acct_name": null,
"amount": "6000000000",
"minimum_confirmations": 2,
"max_outputs": 500,
"num_change_outputs": 1,
"selection_strategy_is_use_all": true,
"message": "my message",
"target_slate_version": null,
"send_args": null
}
},
"id": 1
}
# "#
@ -388,62 +400,11 @@ pub trait OwnerRpc {
```
*/
fn initiate_tx(
&self,
src_acct_name: Option<String>,
amount: u64,
minimum_confirmations: u64,
max_outputs: usize,
num_change_outputs: usize,
selection_strategy_is_use_all: bool,
message: Option<String>,
target_slate_version: Option<u16>,
) -> Result<Slate, ErrorKind>;
/**
Networked version of [Owner::estimate_initiate_tx](struct.Owner.html#method.estimate_initiate_tx).
```
# grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!(
# r#"
{
"jsonrpc": "2.0",
"method": "estimate_initiate_tx",
"params": [null, 6000000000, 2, 500, 1, true],
"id": 1
}
# "#
# ,
# r#"
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"Ok": {
"total": "60000000000",
"fee": "8000000"
}
}
}
# "#
# ,4, false, false, false);
```
*/
fn estimate_initiate_tx(
&self,
src_acct_name: Option<String>,
amount: u64,
minimum_confirmations: u64,
max_outputs: usize,
num_change_outputs: usize,
selection_strategy_is_use_all: bool,
) -> Result<TxEstimation, ErrorKind>;
fn initiate_tx(&self, args: InitTxArgs) -> Result<Slate, ErrorKind>;
/**
Networked version of [Owner::tx_lock_outputs](struct.Owner.html#method.tx_lock_outputs).
```
# grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!(
# r#"
@ -1029,17 +990,17 @@ pub trait OwnerRpc {
"id": 1,
"jsonrpc": "2.0",
"result": {
"Ok": [
5,
true
]
"Ok": {
"height": "5",
"updated_from_node": true
}
}
}
# "#
# , 5, false, false, false);
```
*/
fn node_height(&self) -> Result<(u64, bool), ErrorKind>;
fn node_height(&self) -> Result<NodeHeightResult, ErrorKind>;
}
impl<W: ?Sized, C, K> OwnerRpc for Owner<W, C, K>
@ -1087,50 +1048,8 @@ where
.map_err(|e| e.kind())
}
fn initiate_tx(
&self,
src_acct_name: Option<String>,
amount: u64,
minimum_confirmations: u64,
max_outputs: usize,
num_change_outputs: usize,
selection_strategy_is_use_all: bool,
message: Option<String>,
target_slate_version: Option<u16>,
) -> Result<Slate, ErrorKind> {
Owner::initiate_tx(
self,
src_acct_name.as_ref().map(String::as_str),
amount,
minimum_confirmations,
max_outputs,
num_change_outputs,
selection_strategy_is_use_all,
message,
target_slate_version,
)
.map_err(|e| e.kind())
}
fn estimate_initiate_tx(
&self,
src_acct_name: Option<String>,
amount: u64,
minimum_confirmations: u64,
max_outputs: usize,
num_change_outputs: usize,
selection_strategy_is_use_all: bool,
) -> Result<TxEstimation, ErrorKind> {
Owner::estimate_initiate_tx(
self,
src_acct_name.as_ref().map(String::as_str),
amount,
minimum_confirmations,
max_outputs,
num_change_outputs,
selection_strategy_is_use_all,
)
.map_err(|e| e.kind())
fn initiate_tx(&self, args: InitTxArgs) -> Result<Slate, ErrorKind> {
Owner::initiate_tx(self, args).map_err(|e| e.kind())
}
fn finalize_tx(&self, mut slate: Slate) -> Result<Slate, ErrorKind> {
@ -1165,7 +1084,7 @@ where
Owner::check_repair(self, delete_unconfirmed).map_err(|e| e.kind())
}
fn node_height(&self) -> Result<(u64, bool), ErrorKind> {
fn node_height(&self) -> Result<NodeHeightResult, ErrorKind> {
Owner::node_height(self).map_err(|e| e.kind())
}
}
@ -1244,16 +1163,16 @@ pub fn run_doctest_owner(
let amount = 60_000_000_000;
let mut w = wallet1.lock();
w.open_with_credentials().unwrap();
let mut slate = api_impl::owner::initiate_tx(
&mut *w, None, // account
amount, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None, None, true,
)
.unwrap();
let args = InitTxArgs {
src_acct_name: None,
amount,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let mut slate = api_impl::owner::initiate_tx(&mut *w, args, true).unwrap();
{
let mut w2 = wallet2.lock();
w2.open_with_credentials().unwrap();

View file

@ -35,7 +35,7 @@ use crate::impls::{
LMDBBackend, NullWalletCommAdapter,
};
use crate::impls::{HTTPNodeClient, WalletSeed};
use crate::libwallet::types::{NodeClient, WalletInst};
use crate::libwallet::types::{InitTxArgs, NodeClient, WalletInst};
use crate::{controller, display};
/// Arguments common to all wallet commands
@ -247,31 +247,35 @@ pub fn send(
let strategies = vec!["smallest", "all"]
.into_iter()
.map(|strategy| {
let est = api
.estimate_initiate_tx(
None,
args.amount,
args.minimum_confirmations,
args.max_outputs,
args.change_outputs,
strategy == "all",
)
.unwrap();
(strategy, est.total, est.fee)
let init_args = InitTxArgs {
src_acct_name: None,
amount: args.amount,
minimum_confirmations: args.minimum_confirmations,
max_outputs: args.max_outputs as u32,
num_change_outputs: args.change_outputs as u32,
selection_strategy_is_use_all: strategy == "all",
estimate_only: Some(true),
..Default::default()
};
let slate = api.initiate_tx(init_args).unwrap();
(strategy, slate.amount, slate.fee)
})
.collect();
display::estimate(args.amount, strategies, dark_scheme);
} else {
let result = api.initiate_tx(
None,
args.amount,
args.minimum_confirmations,
args.max_outputs,
args.change_outputs,
args.selection_strategy == "all",
args.message.clone(),
args.target_slate_version,
);
let init_args = InitTxArgs {
src_acct_name: None,
amount: args.amount,
minimum_confirmations: args.minimum_confirmations,
max_outputs: args.max_outputs as u32,
num_change_outputs: args.change_outputs as u32,
selection_strategy_is_use_all: args.selection_strategy == "all",
message: args.message.clone(),
target_slate_version: args.target_slate_version,
send_args: None,
..Default::default()
};
let result = api.initiate_tx(init_args);
let mut slate = match result {
Ok(s) => {
info!(
@ -421,9 +425,9 @@ pub fn outputs(
dark_scheme: bool,
) -> Result<(), Error> {
controller::owner_single_use(wallet.clone(), |api| {
let (height, _) = api.node_height()?;
let res = api.node_height()?;
let (validated, outputs) = api.retrieve_outputs(g_args.show_spent, true, None)?;
display::outputs(&g_args.account, height, validated, outputs, dark_scheme)?;
display::outputs(&g_args.account, res.height, validated, outputs, dark_scheme)?;
Ok(())
})?;
Ok(())
@ -441,12 +445,12 @@ pub fn txs(
dark_scheme: bool,
) -> Result<(), Error> {
controller::owner_single_use(wallet.clone(), |api| {
let (height, _) = api.node_height()?;
let res = api.node_height()?;
let (validated, txs) = api.retrieve_txs(true, args.id, None)?;
let include_status = !args.id.is_some();
display::txs(
&g_args.account,
height,
res.height,
validated,
&txs,
include_status,
@ -456,7 +460,7 @@ pub fn txs(
// inputs/outputs and messages
if args.id.is_some() {
let (_, outputs) = api.retrieve_outputs(true, false, args.id)?;
display::outputs(&g_args.account, height, validated, outputs, dark_scheme)?;
display::outputs(&g_args.account, res.height, validated, outputs, dark_scheme)?;
// should only be one here, but just in case
for tx in txs {
display::tx_messages(&tx, dark_scheme)?;

View file

@ -23,7 +23,8 @@ use crate::impls::{FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCo
use crate::keychain::Keychain;
use crate::libwallet::slate::Slate;
use crate::libwallet::types::{
CbData, NodeClient, OutputCommitMapping, SendTXArgs, TxLogEntry, WalletBackend, WalletInfo,
CbData, InitTxArgs, NodeClient, OutputCommitMapping, SendTXArgs, TxLogEntry, WalletBackend,
WalletInfo,
};
use crate::libwallet::{Error, ErrorKind};
use crate::util::to_base64;
@ -289,7 +290,8 @@ where
_req: &Request<Body>,
api: Owner<T, C, K>,
) -> Result<(u64, bool), Error> {
api.node_height()
let res = api.node_height()?;
Ok((res.height, res.updated_from_node))
}
fn handle_get_request(&self, req: &Request<Body>) -> Result<Response<Body>, Error> {
@ -299,7 +301,7 @@ where
match req
.uri()
.path()
.trim_right_matches("/")
.trim_end_matches("/")
.rsplit("/")
.next()
.unwrap()
@ -320,16 +322,19 @@ where
api: Owner<T, C, K>,
) -> Box<dyn Future<Item = Slate, Error = Error> + Send> {
Box::new(parse_body(req).and_then(move |args: SendTXArgs| {
let result = api.initiate_tx(
None,
args.amount,
args.minimum_confirmations,
args.max_outputs,
args.num_change_outputs,
args.selection_strategy_is_use_all,
args.message,
args.target_slate_version,
);
let init_args = InitTxArgs {
src_acct_name: None,
amount: args.amount,
minimum_confirmations: args.minimum_confirmations,
max_outputs: args.max_outputs as u32,
num_change_outputs: args.num_change_outputs as u32,
selection_strategy_is_use_all: args.selection_strategy_is_use_all,
message: args.message.clone(),
target_slate_version: args.target_slate_version,
send_args: None,
..Default::default()
};
let result = api.initiate_tx(init_args);
let mut slate = match result {
Ok(s) => {
info!(
@ -554,7 +559,7 @@ where
match req
.uri()
.path()
.trim_right_matches("/")
.trim_end_matches("/")
.rsplit("/")
.next()
.unwrap()
@ -688,7 +693,7 @@ where
match req
.uri()
.path()
.trim_right_matches("/")
.trim_end_matches("/")
.rsplit("/")
.next()
.unwrap()

View file

@ -26,6 +26,7 @@ use self::core::global::ChainTypes;
use self::keychain::{ExtKeychain, Keychain};
use grin_wallet_libwallet as libwallet;
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use libwallet::types::InitTxArgs;
use std::fs;
use std::thread;
use std::time::Duration;
@ -179,14 +180,16 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
}
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let mut slate = api.initiate_tx(
None, reward, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None, None,
)?;
let args = InitTxArgs {
src_acct_name: None,
amount: reward,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let mut slate = api.initiate_tx(args)?;
slate = client1.send_tx_slate_direct("wallet2", &slate)?;
api.tx_lock_outputs(&slate)?;
slate = api.finalize_tx(&slate)?;

View file

@ -28,7 +28,7 @@ use self::keychain::ExtKeychain;
use grin_wallet_libwallet as libwallet;
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use impls::FileWalletCommAdapter;
use libwallet::types::WalletInst;
use libwallet::types::{InitTxArgs, WalletInst};
use std::fs;
use std::thread;
use std::time::Duration;
@ -178,16 +178,16 @@ fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// perform a transaction, but don't let it finish
wallet::controller::owner_single_use(wallet1.clone(), |api| {
// send to send
let mut slate = api.initiate_tx(
None,
reward * 2, // amount
cm, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None, // optional message
None,
)?;
let args = InitTxArgs {
src_acct_name: None,
amount: reward * 2,
minimum_confirmations: cm,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let mut slate = api.initiate_tx(args)?;
// output tx file
let file_adapter = FileWalletCommAdapter::new();
let send_file = format!("{}/part_tx_1.tx", test_dir);

View file

@ -31,6 +31,8 @@ use std::fs;
use std::thread;
use std::time::Duration;
use grin_wallet_libwallet::types::InitTxArgs;
use serde_json;
fn clean_output_dir(test_dir: &str) {
@ -105,16 +107,17 @@ fn file_exchange_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward);
// send to send
let mut slate = api.initiate_tx(
Some("mining"),
reward * 2, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
Some(message.to_owned()), // optional message
None,
)?;
let args = InitTxArgs {
src_acct_name: Some("mining".to_owned()),
amount: reward * 2,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
message: Some(message.to_owned()),
..Default::default()
};
let mut slate = api.initiate_tx(args)?;
// output tx file
let file_adapter = FileWalletCommAdapter::new();
file_adapter.send_tx_async(&send_file, &mut slate)?;

View file

@ -26,6 +26,7 @@ use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::ExtKeychain;
use self::libwallet::slate::Slate;
use self::libwallet::types::InitTxArgs;
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use impls::FileWalletCommAdapter;
use std::fs;
@ -103,16 +104,16 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward);
// send to send
let mut slate = api.initiate_tx(
Some("mining"),
reward * 2, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None,
None,
)?;
let args = InitTxArgs {
src_acct_name: Some("mining".to_owned()),
amount: reward * 2,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let mut slate = api.initiate_tx(args)?;
// output tx file
let file_adapter = FileWalletCommAdapter::new();
file_adapter.send_tx_async(&send_file, &mut slate)?;
@ -200,16 +201,16 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
// note this will increment the block count as part of the transaction "Posting"
let slate_i = sender_api.initiate_tx(
None,
amount * 2, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None,
None,
)?;
let args = InitTxArgs {
src_acct_name: None,
amount: reward * 2,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.initiate_tx(args)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate)?;
slate = sender_api.finalize_tx(&mut slate)?;

View file

@ -26,7 +26,7 @@ use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::{ExtKeychain, Identifier, Keychain};
use self::libwallet::slate::Slate;
use self::libwallet::types::AcctPathMapping;
use self::libwallet::types::{AcctPathMapping, InitTxArgs};
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use std::fs;
use std::sync::atomic::Ordering;
@ -237,14 +237,16 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
let mut slate = Slate::blank(1);
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
// note this will increment the block count as part of the transaction "Posting"
let slate_i = sender_api.initiate_tx(
None, amount, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None, None,
)?;
let args = InitTxArgs {
src_acct_name: None,
amount: amount,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.initiate_tx(args)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate)?;
slate = sender_api.finalize_tx(&slate)?;
@ -258,16 +260,16 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
// Send some to wallet 3
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
// note this will increment the block count as part of the transaction "Posting"
let slate_i = sender_api.initiate_tx(
None,
amount * 2, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None,
None,
)?;
let args = InitTxArgs {
src_acct_name: None,
amount: amount * 2,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.initiate_tx(args)?;
slate = client1.send_tx_slate_direct("wallet3", &slate_i)?;
sender_api.tx_lock_outputs(&slate)?;
slate = sender_api.finalize_tx(&slate)?;
@ -281,16 +283,16 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
// Wallet3 to wallet 2
wallet::controller::owner_single_use(wallet3.clone(), |sender_api| {
// note this will increment the block count as part of the transaction "Posting"
let slate_i = sender_api.initiate_tx(
None,
amount * 3, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None,
None,
)?;
let args = InitTxArgs {
src_acct_name: None,
amount: amount * 3,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.initiate_tx(args)?;
slate = client3.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate)?;
slate = sender_api.finalize_tx(&slate)?;
@ -310,16 +312,16 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
// Wallet3 to wallet 2 again (to another account)
wallet::controller::owner_single_use(wallet3.clone(), |sender_api| {
// note this will increment the block count as part of the transaction "Posting"
let slate_i = sender_api.initiate_tx(
None,
amount * 3, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None,
None,
)?;
let args = InitTxArgs {
src_acct_name: None,
amount: amount * 3,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.initiate_tx(args)?;
slate = client3.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate)?;
slate = sender_api.finalize_tx(&slate)?;

View file

@ -26,6 +26,7 @@ use self::core::global::ChainTypes;
use self::keychain::ExtKeychain;
use grin_wallet_libwallet as libwallet;
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use libwallet::types::InitTxArgs;
use std::fs;
use std::thread;
use std::time::Duration;
@ -86,16 +87,16 @@ fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward);
// send to send
let mut slate = api.initiate_tx(
Some("mining"),
reward * 2, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None,
None,
)?;
let args = InitTxArgs {
src_acct_name: Some("mining".to_owned()),
amount: reward * 2,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let mut slate = api.initiate_tx(args)?;
api.tx_lock_outputs(&slate)?;
// Send directly to self
wallet::controller::foreign_single_use(wallet1.clone(), |api| {

View file

@ -27,7 +27,7 @@ use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::ExtKeychain;
use self::libwallet::slate::Slate;
use self::libwallet::types::OutputStatus;
use self::libwallet::types::{InitTxArgs, OutputStatus};
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use std::fs;
use std::thread;
@ -100,14 +100,16 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
let mut slate = Slate::blank(1);
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
// note this will increment the block count as part of the transaction "Posting"
let slate_i = sender_api.initiate_tx(
None, amount, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None, None,
)?;
let args = InitTxArgs {
src_acct_name: None,
amount: amount,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.initiate_tx(args)?;
// Check we are creating a tx with the expected lock_height of 0.
// We will check this produces a Plain kernel later.
@ -250,26 +252,32 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
// Estimate fee and locked amount for a transaction
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
let est = sender_api.estimate_initiate_tx(
None,
amount * 2, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
)?;
assert_eq!(est.total, 600_000_000_000);
let init_args = InitTxArgs {
src_acct_name: None,
amount: amount * 2,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
estimate_only: Some(true),
..Default::default()
};
let est = sender_api.initiate_tx(init_args)?;
assert_eq!(est.amount, 600_000_000_000);
assert_eq!(est.fee, 4_000_000);
let est = sender_api.estimate_initiate_tx(
None,
amount * 2, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
false, // select the smallest amount of outputs
)?;
assert_eq!(est.total, 180_000_000_000);
let init_args = InitTxArgs {
src_acct_name: None,
amount: amount * 2,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: false, //select smallest number
estimate_only: Some(true),
..Default::default()
};
let est = sender_api.initiate_tx(init_args)?;
assert_eq!(est.amount, 180_000_000_000);
assert_eq!(est.fee, 6_000_000);
Ok(())
@ -279,16 +287,16 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
// the stored transaction instead
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
// note this will increment the block count as part of the transaction "Posting"
let slate_i = sender_api.initiate_tx(
None,
amount * 2, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None,
None,
)?;
let args = InitTxArgs {
src_acct_name: None,
amount: amount * 2,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.initiate_tx(args)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate)?;
slate = sender_api.finalize_tx(&slate)?;
@ -378,14 +386,17 @@ fn tx_rollback(test_dir: &str) -> Result<(), libwallet::Error> {
let mut slate = Slate::blank(1);
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
// note this will increment the block count as part of the transaction "Posting"
let slate_i = sender_api.initiate_tx(
None, amount, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None, None,
)?;
let args = InitTxArgs {
src_acct_name: None,
amount: amount,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.initiate_tx(args)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate)?;
slate = sender_api.finalize_tx(&slate)?;

View file

@ -23,7 +23,7 @@ use crate::keychain;
use crate::libwallet;
use crate::libwallet::api_impl::{foreign, owner};
use crate::libwallet::types::{
BlockFees, CbData, NodeClient, WalletBackend, WalletInfo, WalletInst,
BlockFees, CbData, InitTxArgs, NodeClient, WalletBackend, WalletInfo, WalletInst,
};
use crate::lmdb_wallet::LMDBBackend;
use crate::util;
@ -196,15 +196,16 @@ where
let slate = {
let mut w = wallet.lock();
w.open_with_credentials()?;
let slate_i = owner::initiate_tx(
&mut *w, None, // account
amount, // amount
2, // minimum confirmations
500, // max outputs
1, // num change outputs
true, // select all outputs
None, None, test_mode,
)?;
let args = InitTxArgs {
src_acct_name: None,
amount,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = owner::initiate_tx(&mut *w, args, test_mode)?;
let slate = client.send_tx_slate_direct(dest, &slate_i)?;
owner::tx_lock_outputs(&mut *w, &slate)?;
let slate = owner::finalize_tx(&mut *w, &slate)?;

View file

@ -25,8 +25,8 @@ use crate::grin_keychain::{Identifier, Keychain};
use crate::internal::{keys, selection, tx, updater};
use crate::slate::Slate;
use crate::types::{
AcctPathMapping, NodeClient, OutputCommitMapping, TxEstimation, TxLogEntry, TxWrapper,
WalletBackend, WalletInfo,
AcctPathMapping, InitTxArgs, NodeClient, NodeHeightResult, OutputCommitMapping, TxLogEntry,
TxWrapper, WalletBackend, WalletInfo,
};
use crate::{Error, ErrorKind};
@ -137,14 +137,7 @@ where
/// Initiate tx as sender
pub fn initiate_tx<T: ?Sized, C, K>(
w: &mut T,
src_acct_name: Option<&str>,
amount: u64,
minimum_confirmations: u64,
max_outputs: usize,
num_change_outputs: usize,
selection_strategy_is_use_all: bool,
message: Option<String>,
target_slate_version: Option<u16>,
args: InitTxArgs,
use_test_rng: bool,
) -> Result<Slate, Error>
where
@ -152,9 +145,9 @@ where
C: NodeClient,
K: Keychain,
{
let parent_key_id = match src_acct_name {
let parent_key_id = match args.src_acct_name {
Some(d) => {
let pm = w.get_acct_path(d.to_owned())?;
let pm = w.get_acct_path(d)?;
match pm {
Some(p) => p.path,
None => w.parent_key_id(),
@ -163,7 +156,7 @@ where
None => w.parent_key_id(),
};
let message = match message {
let message = match args.message {
Some(mut m) => {
m.truncate(USER_MESSAGE_MAX_LEN);
Some(m)
@ -171,15 +164,32 @@ where
None => None,
};
let mut slate = tx::new_tx_slate(&mut *w, amount, 2, use_test_rng)?;
let mut slate = tx::new_tx_slate(&mut *w, args.amount, 2, use_test_rng)?;
// if we just want to estimate, don't save a context, just send the results
// back
if let Some(true) = args.estimate_only {
let (total, fee) = tx::estimate_send_tx(
&mut *w,
args.amount,
args.minimum_confirmations,
args.max_outputs as usize,
args.num_change_outputs as usize,
args.selection_strategy_is_use_all,
&parent_key_id,
)?;
slate.amount = total;
slate.fee = fee;
return Ok(slate);
}
let context = tx::add_inputs_to_slate(
&mut *w,
&mut slate,
minimum_confirmations,
max_outputs,
num_change_outputs,
selection_strategy_is_use_all,
args.minimum_confirmations,
args.max_outputs as usize,
args.num_change_outputs as usize,
args.selection_strategy_is_use_all,
&parent_key_id,
0,
message,
@ -193,49 +203,12 @@ where
batch.save_private_context(slate.id.as_bytes(), &context)?;
batch.commit()?;
}
if let Some(v) = target_slate_version {
if let Some(v) = args.target_slate_version {
slate.version_info.orig_version = v;
}
Ok(slate)
}
/// Estimate
pub fn estimate_initiate_tx<T: ?Sized, C, K>(
w: &mut T,
src_acct_name: Option<&str>,
amount: u64,
minimum_confirmations: u64,
max_outputs: usize,
num_change_outputs: usize,
selection_strategy_is_use_all: bool,
) -> Result<TxEstimation, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
{
let parent_key_id = match src_acct_name {
Some(d) => {
let pm = w.get_acct_path(d.to_owned())?;
match pm {
Some(p) => p.path,
None => w.parent_key_id(),
}
}
None => w.parent_key_id(),
};
let (total, fee) = tx::estimate_send_tx(
&mut *w,
amount,
minimum_confirmations,
max_outputs,
num_change_outputs,
selection_strategy_is_use_all,
&parent_key_id,
)?;
Ok(TxEstimation { total, fee })
}
/// Lock sender outputs
pub fn tx_lock_outputs<T: ?Sized, C, K>(w: &mut T, slate: &Slate) -> Result<(), Error>
where
@ -348,7 +321,7 @@ where
}
/// node height
pub fn node_height<T: ?Sized, C, K>(w: &mut T) -> Result<(u64, bool), Error>
pub fn node_height<T: ?Sized, C, K>(w: &mut T) -> Result<NodeHeightResult, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
@ -356,14 +329,20 @@ where
{
let res = w.w2n_client().get_chain_height();
match res {
Ok(height) => Ok((height, true)),
Ok(height) => Ok(NodeHeightResult {
height,
updated_from_node: true,
}),
Err(_) => {
let outputs = retrieve_outputs(w, true, false, None)?;
let height = match outputs.1.iter().map(|m| m.output.height).max() {
Some(height) => height,
None => 0,
};
Ok((height, false))
Ok(NodeHeightResult {
height,
updated_from_node: false,
})
}
}
}

View file

@ -373,29 +373,6 @@ impl fmt::Display for OutputStatus {
}
}
/// Map Outputdata to commits
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct OutputCommitMapping {
/// Output Data
pub output: OutputData,
/// The commit
#[serde(
serialize_with = "secp_ser::as_hex",
deserialize_with = "secp_ser::commitment_from_hex"
)]
pub commit: pedersen::Commitment,
}
/// Transaction Estimate
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TxEstimation {
/// Total amount to be locked
#[serde(with = "secp_ser::string_or_u64")]
pub total: u64,
/// Transaction Fee
#[serde(with = "secp_ser::string_or_u64")]
pub fee: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
/// Holds the context for a single aggsig transaction
pub struct Context {
@ -545,37 +522,6 @@ impl<'de> serde::de::Visitor<'de> for BlockIdentifierVisitor {
}
}
/// Fees in block to use for coinbase amount calculation
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlockFees {
/// fees
#[serde(with = "secp_ser::string_or_u64")]
pub fees: u64,
/// height
#[serde(with = "secp_ser::string_or_u64")]
pub height: u64,
/// key id
pub key_id: Option<Identifier>,
}
impl BlockFees {
/// return key id
pub fn key_id(&self) -> Option<Identifier> {
self.key_id.clone()
}
}
/// Response to build a coinbase output.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CbData {
/// Output
pub output: Output,
/// Kernel
pub kernel: TxKernel,
/// Key Id
pub key_id: Option<Identifier>,
}
/// a contained wallet info struct, so automated tests can parse wallet info
/// can add more fields here over time as needed
#[derive(Serialize, Eq, PartialEq, Deserialize, Debug, Clone)]
@ -752,7 +698,11 @@ pub struct TxWrapper {
pub tx_hex: String,
}
// Types to facilitate API arguments and serialization
/// Send TX API Args
// TODO: This is here to ensure the legacy V1 API remains intact
// remove this when v1 api is removed
#[derive(Clone, Serialize, Deserialize)]
pub struct SendTXArgs {
/// amount to send
@ -774,3 +724,141 @@ pub struct SendTXArgs {
/// Optional slate version to target when sending
pub target_slate_version: Option<u16>,
}
/// V2 Init / Send TX API Args
#[derive(Clone, Serialize, Deserialize)]
pub struct InitTxArgs {
/// The human readable account name from which to draw outputs
/// for the transaction, overriding whatever the active account is as set via the
/// [`set_active_account`](../grin_wallet_api/owner/struct.Owner.html#method.set_active_account) method.
pub src_acct_name: Option<String>,
#[serde(with = "secp_ser::string_or_u64")]
/// The amount to send, in nanogrins. (`1 G = 1_000_000_000nG`)
pub amount: u64,
#[serde(with = "secp_ser::string_or_u64")]
/// The minimum number of confirmations an output
/// should have in order to be included in the transaction.
pub minimum_confirmations: u64,
/// By default, the wallet selects as many inputs as possible in a
/// transaction, to reduce the Output set and the fees. The wallet will attempt to spend
/// include up to `max_outputs` in a transaction, however if this is not enough to cover
/// the whole amount, the wallet will include more outputs. This parameter should be considered
/// a soft limit.
pub max_outputs: u32,
/// The target number of change outputs to create in the transaction.
/// The actual number created will be `num_change_outputs` + whatever remainder is needed.
pub num_change_outputs: u32,
/// If `true`, attempt to use up as many outputs as
/// possible to create the transaction, up the 'soft limit' of `max_outputs`. This helps
/// to reduce the size of the UTXO set and the amount of data stored in the wallet, and
/// minimizes fees. This will generally result in many inputs and a large change output(s),
/// usually much larger than the amount being sent. If `false`, the transaction will include
/// as many outputs as are needed to meet the amount, (and no more) starting with the smallest
/// value outputs.
pub selection_strategy_is_use_all: bool,
/// An optional participant message to include alongside the sender's public
/// ParticipantData within the slate. This message will include a signature created with the
/// sender's private excess value, and will be publically verifiable. Note this message is for
/// the convenience of the participants during the exchange; it is not included in the final
/// transaction sent to the chain. The message will be truncated to 256 characters.
pub message: Option<String>,
/// Optionally set the output target slate version (acceptable
/// down to the minimum slate version compatible with the current. If `None` the slate
/// is generated with the latest version.
pub target_slate_version: Option<u16>,
/// If true, just return an estimate of the resulting slate, containing fees and amounts
/// locked without actually locking outputs or creating the transaction. Note if this is set to
/// 'true', the amount field in the slate will contain the total amount locked, not the provided
/// transaction amount
pub estimate_only: Option<bool>,
/// Sender arguments. If present, the underlying function will also attempt to send the
/// transaction to a destination and optionally finalize the result
pub send_args: Option<InitTxSendArgs>,
}
/// Send TX API Args, for convenience functionality that inits the transaction and sends
/// in one go
#[derive(Clone, Serialize, Deserialize)]
pub struct InitTxSendArgs {
/// The transaction method. Can currently be 'http' or 'keybase'.
pub method: String,
/// The destination, contents will depend on the particular method
pub dest: String,
/// Whether to finalize the result immediately if the send was successful
pub finalize: bool,
/// Whether to post the transasction if the send and finalize were successful
pub post_tx: bool,
/// Whether to use dandelion when posting. If false, skip the dandelion relay
pub fluff: bool,
}
impl Default for InitTxArgs {
fn default() -> InitTxArgs {
InitTxArgs {
src_acct_name: None,
amount: 0,
minimum_confirmations: 10,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
message: None,
target_slate_version: None,
estimate_only: Some(false),
send_args: None,
}
}
}
/// Fees in block to use for coinbase amount calculation
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlockFees {
/// fees
#[serde(with = "secp_ser::string_or_u64")]
pub fees: u64,
/// height
#[serde(with = "secp_ser::string_or_u64")]
pub height: u64,
/// key id
pub key_id: Option<Identifier>,
}
impl BlockFees {
/// return key id
pub fn key_id(&self) -> Option<Identifier> {
self.key_id.clone()
}
}
/// Response to build a coinbase output.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CbData {
/// Output
pub output: Output,
/// Kernel
pub kernel: TxKernel,
/// Key Id
pub key_id: Option<Identifier>,
}
/// Map Outputdata to commits
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct OutputCommitMapping {
/// Output Data
pub output: OutputData,
/// The commit
#[serde(
serialize_with = "secp_ser::as_hex",
deserialize_with = "secp_ser::commitment_from_hex"
)]
pub commit: pedersen::Commitment,
}
/// Node height result
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct NodeHeightResult {
/// Last known height
#[serde(with = "secp_ser::string_or_u64")]
pub height: u64,
/// Whether this height was updated from the node
pub updated_from_node: bool,
}