Cleanup of wallet owner API, rustdoc documentation (#2036)

* doc start

* rustfmt

* tightening up wallet owner API

* rustfmt

* remove burn tx functions

* more tx api documentation

* rustfmt
This commit is contained in:
Yeastplume
2018-11-28 15:31:23 +00:00
committed by GitHub
parent 2a77207713
commit fdb0197ef9
13 changed files with 415 additions and 289 deletions
+1 -1
View File
@@ -973,7 +973,7 @@ fn replicate_tx_fluff_failure() {
slate = client1_w.send_tx_sync(dest, &slate)?;
api.finalize_tx(&mut slate)?;
api.tx_lock_outputs(&slate, lock_fn)?;
api.post_tx(&slate, false)?;
api.post_tx(&slate.tx, false)?;
Ok(())
}).unwrap();
+29 -42
View File
@@ -15,10 +15,14 @@
use clap::ArgMatches;
use rpassword;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::thread;
use std::time::Duration;
use serde_json as json;
use api::TLSConfig;
use config::GlobalWalletConfig;
use core::{core, global};
@@ -328,7 +332,7 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
} else {
let label = create.unwrap();
let res = controller::owner_single_use(wallet, |api| {
api.new_account_path(label)?;
api.create_account_path(label)?;
thread::sleep(Duration::from_millis(200));
println!("Account: '{}' Created!", label);
Ok(())
@@ -460,7 +464,7 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
api.tx_lock_outputs(&slate, lock_fn)?;
}
if adapter.supports_sync() {
let result = api.post_tx(&slate, fluff);
let result = api.post_tx(&slate.tx, fluff);
match result {
Ok(_) => {
info!("Tx sent",);
@@ -513,7 +517,7 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
let mut slate = adapter.receive_tx_async(tx_file)?;
let _ = api.finalize_tx(&mut slate).expect("Finalize failed");
let result = api.post_tx(&slate, fluff);
let result = api.post_tx(&slate.tx, fluff);
match result {
Ok(_) => {
info!("Transaction sent successfully, check the wallet again for confirmation.");
@@ -525,24 +529,6 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
}
}
}
("burn", Some(send_args)) => {
let amount = send_args
.value_of("amount")
.expect("Amount to burn required");
let amount = core::amount_from_hr_string(amount)
.expect("Could not parse amount as number with optional decimal point.");
let minimum_confirmations: u64 = send_args
.value_of("minimum_confirmations")
.unwrap()
.parse()
.expect("Could not parse minimum_confirmations as a whole number.");
let max_outputs = 500;
api.issue_burn_tx(amount, minimum_confirmations, max_outputs)
.unwrap_or_else(|e| {
panic!("Error burning tx: {:?} Config: {:?}", e, wallet_config)
});
Ok(())
}
("info", Some(args)) => {
let minimum_confirmations: u64 = args
.value_of("minimum_confirmations")
@@ -651,32 +637,33 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
let dump_file = repost_args.value_of("dumpfile");
let fluff = repost_args.is_present("fluff");
let (_, txs) = api.retrieve_txs(true, Some(tx_id), None)?;
let stored_tx = txs[0].get_stored_tx();
if stored_tx.is_none() {
println!(
"Transaction with id {} does not have transaction data. Not reposting.",
tx_id
);
std::process::exit(0);
}
match dump_file {
None => {
let result = api.post_stored_tx(tx_id, fluff);
match result {
Ok(_) => {
info!("Reposted transaction at {}", tx_id);
Ok(())
}
Err(e) => {
error!("Transaction reposting failed: {}", e);
Err(e)
}
if txs[0].confirmed {
println!("Transaction with id {} is confirmed. Not reposting.", tx_id);
std::process::exit(0);
}
api.post_tx(&stored_tx.unwrap(), fluff)?;
info!("Reposted transaction at {}", tx_id);
println!("Reposted transaction at {}", tx_id);
Ok(())
}
Some(f) => {
let result = api.dump_stored_tx(tx_id, true, f);
match result {
Ok(_) => {
warn!("Dumped transaction data for tx {} to {}", tx_id, f);
Ok(())
}
Err(e) => {
error!("Transaction reposting failed: {}", e);
Err(e)
}
}
let mut tx_file = File::create(f)?;
tx_file.write_all(json::to_string(&stored_tx).unwrap().as_bytes())?;
tx_file.sync_all()?;
info!("Dumped transaction data for tx {} to {}", tx_id, f);
println!("Dumped transaction data for tx {} to {}", tx_id, f);
Ok(())
}
}
}
+9 -25
View File
@@ -222,31 +222,31 @@ where
api.retrieve_txs(update_from_node, tx_id, tx_slate_id)
}
fn dump_stored_tx(
fn retrieve_stored_tx(
&self,
req: &Request<Body>,
api: APIOwner<T, C, K>,
) -> Result<Transaction, Error> {
) -> Result<(bool, Option<Transaction>), Error> {
let params = parse_params(req);
if let Some(id_string) = params.get("id") {
match id_string[0].parse() {
Ok(id) => match api.dump_stored_tx(id, false, "") {
Ok(tx) => Ok(tx),
Ok(id) => match api.retrieve_txs(true, Some(id), None) {
Ok((_, txs)) => Ok((txs[0].confirmed, txs[0].get_stored_tx())),
Err(e) => {
error!("dump_stored_tx: failed with error: {}", e);
error!("retrieve_stored_tx: failed with error: {}", e);
Err(e)
}
},
Err(e) => {
error!("dump_stored_tx: could not parse id: {}", e);
error!("retrieve_stored_tx: could not parse id: {}", e);
Err(ErrorKind::TransactionDumpError(
"dump_stored_tx: cannot dump transaction. Could not parse id in request.",
"retrieve_stored_tx: cannot dump transaction. Could not parse id in request.",
).into())
}
}
} else {
Err(ErrorKind::TransactionDumpError(
"dump_stored_tx: Cannot dump transaction. Missing id param in request.",
"retrieve_stored_tx: Cannot retrieve transaction. Missing id param in request.",
).into())
}
}
@@ -292,7 +292,7 @@ where
"retrieve_summary_info" => json_response(&self.retrieve_summary_info(req, api)?),
"node_height" => json_response(&self.node_height(req, api)?),
"retrieve_txs" => json_response(&self.retrieve_txs(req, api)?),
"dump_stored_tx" => json_response(&self.dump_stored_tx(req, api)?),
"retrieve_stored_tx" => json_response(&self.retrieve_stored_tx(req, api)?),
_ => response(StatusCode::BAD_REQUEST, ""),
})
}
@@ -440,18 +440,6 @@ where
)
}
fn issue_burn_tx(
&self,
_req: Request<Body>,
mut api: APIOwner<T, C, K>,
) -> Box<Future<Item = (), Error = Error> + Send> {
// TODO: Args
Box::new(match api.issue_burn_tx(60, 10, 1000) {
Ok(_) => ok(()),
Err(e) => err(e),
})
}
fn handle_post_request(&self, req: Request<Body>) -> WalletResponseFuture {
let api = APIOwner::new(self.wallet.clone());
match req
@@ -478,10 +466,6 @@ where
self.post_tx(req, api)
.and_then(|_| ok(response(StatusCode::OK, ""))),
),
"issue_burn_tx" => Box::new(
self.issue_burn_tx(req, api)
.and_then(|_| ok(response(StatusCode::OK, ""))),
),
_ => Box::new(err(ErrorKind::GenericError(
"Unknown error handling post request".to_owned(),
).into())),
+329 -130
View File
@@ -12,20 +12,28 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Wrappers around library functions, intended to split functions
//! into external and internal APIs (i.e. functions for the local wallet
//! vs. functions to interact with someone else)
//! Still experimental, not sure this is the best way to do this
//! Main interface into all wallet API functions.
//! Wallet APIs are split into two seperate blocks of functionality
//! called the 'Owner' and 'Foreign' APIs:
//! * The 'Owner' API is intended to expose methods that are to be
//! used by the wallet owner only. It is vital that this API is not
//! exposed to anyone other than the owner of the wallet (i.e. the
//! person with access to the seed and password.
//! * The 'Foreign' API contains methods that other wallets will
//! use to interact with the owner's wallet. This API can be exposed
//! to the outside world, with the consideration as to how that can
//! be done securely up to the implementor.
//!
//! Methods in both APIs are intended to be 'single use', that is to say each
//! method will 'open' the wallet (load the keychain with its master seed), perform
//! its operation, then 'close' the wallet (unloading references to the keychain and master
//! seed).
use std::fs::File;
use std::io::Write;
use std::marker::PhantomData;
use std::sync::Arc;
use util::Mutex;
use uuid::Uuid;
use serde_json as json;
use core::core::hash::Hashed;
use core::core::Transaction;
use core::ser;
@@ -40,16 +48,15 @@ use libwallet::{Error, ErrorKind};
use util;
use util::secp::pedersen;
/// Wrapper around internal API functions, containing a reference to
/// the wallet/keychain that they're acting upon
/// Functions intended for use by the owner (e.g. master seed holder) of the wallet.
pub struct APIOwner<W: ?Sized, C, K>
where
W: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
{
/// Wallet, contains its keychain (TODO: Split these up into 2 traits
/// perhaps)
/// A reference-counted mutex to an implementation of the
/// [WalletBackend](../types/trait.WalletBackend.html) trait.
pub wallet: Arc<Mutex<W>>,
phantom: PhantomData<K>,
phantom_c: PhantomData<C>,
@@ -61,7 +68,52 @@ where
C: NodeClient,
K: Keychain,
{
/// Create new API instance
/// Create a new API instance with the given wallet instance. All subsequent
/// API calls will operate on this instance of the wallet.
///
/// Each method will call the [WalletBackend](../types/trait.WalletBackend.html)'s
/// [open_with_credentials](../types/trait.WalletBackend.html#tymethod.open_with_credentials)
/// (initialising a keychain with the master seed,) perform its operation, then close the keychain
/// with a call to [close](../types/trait.WalletBackend.html#tymethod.close)
///
/// # Arguments
/// * `wallet_in` - A reference-counted mutex containing an implementation of the
/// [WalletBackend](../types/trait.WalletBackend.html) trait.
///
/// # Returns
/// * An instance of the OwnerAPI holding a reference to the provided wallet
///
/// # Example
/// ```
/// # extern crate grin_wallet as wallet;
/// # extern crate grin_keychain as keychain;
/// # extern crate grin_util as util;
///
/// use std::sync::Arc;
/// use util::Mutex;
///
/// use keychain::ExtKeychain;
/// use wallet::libwallet::api::APIOwner;
///
/// // These contain sample implementations of each part needed for a wallet
/// use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
///
/// let mut wallet_config = WalletConfig::default();
///
/// // A NodeClient must first be created to handle communication between
/// // the wallet and the node.
///
/// let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// Arc::new(Mutex::new(
/// LMDBBackend::new(wallet_config.clone(), "", node_client).unwrap()
/// ));
///
/// let api_owner = APIOwner::new(wallet.clone());
/// // .. perform wallet operations
///
/// ```
pub fn new(wallet_in: Arc<Mutex<W>>) -> Self {
APIOwner {
wallet: wallet_in,
@@ -70,9 +122,214 @@ where
}
}
/// Attempt to update and retrieve outputs
/// Return (whether the outputs were validated against a node, OutputData)
/// if tx_id is some then only retrieve outputs for associated transaction
/// Returns a list of accounts stored in the wallet (i.e. mappings between
/// user-specified labels and BIP32 derivation paths.
///
/// # Returns
/// * Result Containing:
/// * A Vector of [AcctPathMapping](../types/struct.AcctPathMapping.html) data
/// * or [libwallet::Error](../struct.Error.html) if an error is encountered.
///
/// # Remarks
///
/// * A wallet should always have the path with the label 'default' path defined,
/// with path m/0/0
/// * This method does not need to use the wallet seed or keychain.
///
/// # Example
/// Set up as in [new](struct.APIOwner.html#method.new) method above.
/// ```
/// # extern crate grin_wallet as wallet;
/// # extern crate grin_keychain as keychain;
/// # extern crate grin_util as util;
/// # use std::sync::Arc;
/// # use util::Mutex;
/// # use keychain::ExtKeychain;
/// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default();
/// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new(
/// # LMDBBackend::new(wallet_config.clone(), "", node_client).unwrap()
/// # ));
///
/// let api_owner = APIOwner::new(wallet.clone());
///
/// let result = api_owner.accounts();
///
/// if let Ok(accts) = result {
/// //...
/// }
/// ```
pub fn accounts(&self) -> Result<Vec<AcctPathMapping>, Error> {
let mut w = self.wallet.lock();
keys::accounts(&mut *w)
}
/// Creates a new 'account', which is a mapping of a user-specified
/// label to a BIP32 path
///
/// # Arguments
/// * `label` - A human readable label to which to map the new BIP32 Path
///
/// # Returns
/// * Result Containing:
/// * A [Keychain Identifier](#) for the new path
/// * or [libwallet::Error](../struct.Error.html) if an error is encountered.
///
/// # Remarks
///
/// * Wallets should be initialised with the 'default' path mapped to `m/0/0`
/// * Each call to this function will increment the first element of the path
/// so the first call will create an account at `m/1/0` and the second at
/// `m/2/0` etc. . .
/// * The account path is used throughout as the parent key for most key-derivation
/// operations. See [set_active_account](struct.APIOwner.html#method.set_active_account) for
/// further details.
///
/// * This function does not need to use the root wallet seed or keychain.
///
/// # Example
/// Set up as in [new](struct.APIOwner.html#method.new) method above.
/// ```
/// # extern crate grin_wallet as wallet;
/// # extern crate grin_keychain as keychain;
/// # extern crate grin_util as util;
/// # use std::sync::Arc;
/// # use util::Mutex;
/// # use keychain::ExtKeychain;
/// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default();
/// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new(
/// # LMDBBackend::new(wallet_config.clone(), "", node_client).unwrap()
/// # ));
///
/// let api_owner = APIOwner::new(wallet.clone());
///
/// let result = api_owner.create_account_path("account1");
///
/// if let Ok(identifier) = result {
/// //...
/// }
/// ```
pub fn create_account_path(&self, label: &str) -> Result<Identifier, Error> {
let mut w = self.wallet.lock();
keys::new_acct_path(&mut *w, label)
}
/// Sets the wallet's currently active account. This sets the
/// BIP32 parent path used for most key-derivation operations.
///
/// # Arguments
/// * `label` - The human readable label for the account. Accounts can be retrieved via
/// the [account](struct.APIOwner.html#method.accounts) method
/// # Returns
/// * Result Containing:
/// * `Ok(())` if the path was correctly set
/// * or [libwallet::Error](../struct.Error.html) if an error is encountered.
///
/// # Remarks
///
/// * Wallet parent paths are 2 path elements long, e.g. `m/0/0` is the path
/// labelled 'default'. Keys derived from this parent path are 3 elements long,
/// e.g. the secret keys derived from the `m/0/0` path will be at paths `m/0/0/0`,
/// `m/0/0/1` etc...
///
/// * This function does not need to use the root wallet seed or keychain.
///
/// # Example
/// Set up as in [new](struct.APIOwner.html#method.new) method above.
/// ```
/// # extern crate grin_wallet as wallet;
/// # extern crate grin_keychain as keychain;
/// # extern crate grin_util as util;
/// # use std::sync::Arc;
/// # use util::Mutex;
/// # use keychain::ExtKeychain;
/// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default();
/// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new(
/// # LMDBBackend::new(wallet_config.clone(), "", node_client).unwrap()
/// # ));
///
/// let api_owner = APIOwner::new(wallet.clone());
///
/// let result = api_owner.create_account_path("account1");
///
/// if let Ok(identifier) = result {
/// // set the account active
/// let result2 = api_owner.set_active_account("account1");
/// }
/// ```
pub fn set_active_account(&self, label: &str) -> Result<(), Error> {
let mut w = self.wallet.lock();
w.set_parent_key_id_by_name(label)?;
Ok(())
}
/// Returns a list of outputs from the active account in the wallet.
///
/// # Arguments
/// * `include_spent` - If `true`, outputs that have been marked as 'spent'
/// in the wallet will be returned. If `false`, spent outputs will omitted
/// from the results.
/// * `refresh_from_node` - If true, the wallet will attempt to contact
/// a node (via the [NodeClient](../types/trait.NodeClient.html)
/// provided during wallet instantiation). If `false`, the results will
/// contain output information that may be out-of-date (from the last time
/// the wallet's output set was refreshed against the node).
/// * `tx_id` - If `Some(i)`, only return the outputs associated with
/// the transaction log entry of id `i`.
///
/// # Returns
/// * (`bool`, `Vec<OutputData, Commitment>`) - A tuple:
/// * The first `bool` element indicates whether the data was successfully
/// refreshed from the node (note this may be false even if the `refresh_from_node`
/// argument was set to `true`.
/// * The second element contains the result set, of which each element is
/// a mapping between the wallet's internal [OutputData](../types/struct.OutputData.html)
/// and the Output commitment as identified in the chain's UTXO set
///
/// # Example
/// Set up as in [new](struct.APIOwner.html#method.new) method above.
/// ```
/// # extern crate grin_wallet as wallet;
/// # extern crate grin_keychain as keychain;
/// # extern crate grin_util as util;
/// # use std::sync::Arc;
/// # use util::Mutex;
/// # use keychain::ExtKeychain;
/// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default();
/// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new(
/// # LMDBBackend::new(wallet_config.clone(), "", node_client).unwrap()
/// # ));
///
/// let api_owner = APIOwner::new(wallet.clone());
/// let show_spent = false;
/// let update_from_node = true;
/// let tx_id = None;
///
/// let result = api_owner.retrieve_outputs(show_spent, update_from_node, tx_id);
///
/// if let Ok((was_updated, output_mapping)) = result {
/// //...
/// }
/// ```
pub fn retrieve_outputs(
&self,
include_spent: bool,
@@ -97,8 +354,59 @@ where
res
}
/// Attempt to update outputs and retrieve transactions
/// Return (whether the outputs were validated against a node, OutputData)
/// Returns a list of [Transaction Log Entries](../types/struct.TxLogEntry.html)
/// from the active account in the wallet.
///
/// # Arguments
/// * `refresh_from_node` - If true, the wallet will attempt to contact
/// a node (via the [NodeClient](../types/trait.NodeClient.html)
/// provided during wallet instantiation). If `false`, the results will
/// contain transaction information that may be out-of-date (from the last time
/// the wallet's output set was refreshed against the node).
/// * `tx_id` - If `Some(i)`, only return the transactions associated with
/// the transaction log entry of id `i`.
/// * `tx_slate_id` - If `Some(uuid)`, only return transactions associated with
/// the given [Slate](../../libtx/slate/struct.Slate.html) uuid.
///
/// # Returns
/// * (`bool`, `Vec<[TxLogEntry](../types/struct.TxLogEntry.html)>`) - A tuple:
/// * The first `bool` element indicates whether the data was successfully
/// refreshed from the node (note this may be false even if the `refresh_from_node`
/// argument was set to `true`.
/// * The second element contains the set of retrieved
/// [TxLogEntries](../types/struct/TxLogEntry.html)
///
/// # Example
/// Set up as in [new](struct.APIOwner.html#method.new) method above.
/// ```
/// # extern crate grin_wallet as wallet;
/// # extern crate grin_keychain as keychain;
/// # extern crate grin_util as util;
/// # use std::sync::Arc;
/// # use util::Mutex;
/// # use keychain::ExtKeychain;
/// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default();
/// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new(
/// # LMDBBackend::new(wallet_config.clone(), "", node_client).unwrap()
/// # ));
///
/// let api_owner = APIOwner::new(wallet.clone());
/// let update_from_node = true;
/// let tx_id = None;
/// let tx_slate_id = None;
///
/// // Return all TxLogEntries
/// let result = api_owner.retrieve_txs(update_from_node, tx_id, tx_slate_id);
///
/// if let Ok((was_updated, tx_log_entries)) = result {
/// //...
/// }
/// ```
pub fn retrieve_txs(
&self,
refresh_from_node: bool,
@@ -145,18 +453,6 @@ where
res
}
/// Return list of existing account -> Path mappings
pub fn accounts(&mut self) -> Result<Vec<AcctPathMapping>, Error> {
let mut w = self.wallet.lock();
keys::accounts(&mut *w)
}
/// Create a new account path
pub fn new_account_path(&mut self, label: &str) -> Result<Identifier, Error> {
let mut w = self.wallet.lock();
keys::new_acct_path(&mut *w, label)
}
/// Creates a new partial transaction for the given amount
pub fn initiate_tx(
&mut self,
@@ -260,33 +556,9 @@ where
Ok(())
}
/// Issue a burn TX
pub fn issue_burn_tx(
&mut self,
amount: u64,
minimum_confirmations: u64,
max_outputs: usize,
) -> Result<(), Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let parent_key_id = w.parent_key_id();
let tx_burn = tx::issue_burn_tx(
&mut *w,
amount,
minimum_confirmations,
max_outputs,
&parent_key_id,
)?;
let tx_hex = util::to_hex(ser::ser_vec(&tx_burn).unwrap());
w.w2n_client()
.post_tx(&TxWrapper { tx_hex: tx_hex }, false)?;
w.close()?;
Ok(())
}
/// Posts a transaction to the chain
pub fn post_tx(&self, slate: &Slate, fluff: bool) -> Result<(), Error> {
let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap());
pub fn post_tx(&self, tx: &Transaction, fluff: bool) -> Result<(), Error> {
let tx_hex = util::to_hex(ser::ser_vec(tx).unwrap());
let client = {
let mut w = self.wallet.lock();
w.w2n_client().clone()
@@ -298,86 +570,13 @@ where
} else {
debug!(
"api: post_tx: successfully posted tx: {}, fluff? {}",
slate.tx.hash(),
tx.hash(),
fluff
);
Ok(())
}
}
/// Writes stored transaction data to a given file
pub fn dump_stored_tx(
&self,
tx_id: u32,
write_to_disk: bool,
dest: &str,
) -> Result<Transaction, Error> {
let (confirmed, tx_hex) = {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let parent_key_id = w.parent_key_id();
let res = tx::retrieve_tx_hex(&mut *w, &parent_key_id, tx_id)?;
w.close()?;
res
};
if confirmed {
warn!(
"api: dump_stored_tx: transaction at {} is already confirmed.",
tx_id
);
}
if tx_hex.is_none() {
error!(
"api: dump_stored_tx: completed transaction at {} does not exist.",
tx_id
);
return Err(ErrorKind::TransactionBuildingNotCompleted(tx_id))?;
}
let tx_bin = util::from_hex(tx_hex.unwrap()).unwrap();
let tx = ser::deserialize::<Transaction>(&mut &tx_bin[..])?;
if write_to_disk {
let mut tx_file = File::create(dest)?;
tx_file.write_all(json::to_string(&tx).unwrap().as_bytes())?;
tx_file.sync_all()?;
}
Ok(tx)
}
/// (Re)Posts a transaction that's already been stored to the chain
pub fn post_stored_tx(&self, tx_id: u32, fluff: bool) -> Result<(), Error> {
let client;
let (confirmed, tx_hex) = {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let parent_key_id = w.parent_key_id();
client = w.w2n_client().clone();
let res = tx::retrieve_tx_hex(&mut *w, &parent_key_id, tx_id)?;
w.close()?;
res
};
if confirmed {
error!(
"api: repost_tx: transaction at {} is confirmed. NOT resending.",
tx_id
);
return Err(ErrorKind::TransactionAlreadyConfirmed)?;
}
if tx_hex.is_none() {
error!(
"api: repost_tx: completed transaction at {} does not exist.",
tx_id
);
return Err(ErrorKind::TransactionBuildingNotCompleted(tx_id))?;
}
client.post_tx(
&TxWrapper {
tx_hex: tx_hex.unwrap(),
},
fluff,
)?;
Ok(())
}
/// Attempt to restore contents of wallet
pub fn restore(&mut self) -> Result<(), Error> {
let mut w = self.wallet.lock();
+1 -57
View File
@@ -14,16 +14,12 @@
//! Transaction building functions
use std::sync::Arc;
use util::{self, RwLock};
use util;
use uuid::Uuid;
use core::core::verifier_cache::LruVerifierCache;
use core::core::Transaction;
use core::ser;
use keychain::{Identifier, Keychain};
use libtx::slate::Slate;
use libtx::{build, tx_fee};
use libwallet::internal::{selection, updater};
use libwallet::types::{Context, NodeClient, TxLogEntryType, WalletBackend};
use libwallet::{Error, ErrorKind};
@@ -231,58 +227,6 @@ where
Ok(())
}
/// Issue a burn tx
pub fn issue_burn_tx<T: ?Sized, C, K>(
wallet: &mut T,
amount: u64,
minimum_confirmations: u64,
max_outputs: usize,
parent_key_id: &Identifier,
) -> Result<Transaction, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
{
// TODO
// let keychain = &Keychain::burn_enabled(wallet.keychain(),
// &Identifier::zero());
let keychain = wallet.keychain().clone();
let current_height = wallet.w2n_client().get_chain_height()?;
let _ = updater::refresh_outputs(wallet, parent_key_id);
// select some spendable coins from the wallet
let (_, coins) = selection::select_coins(
wallet,
amount,
current_height,
minimum_confirmations,
max_outputs,
false,
parent_key_id,
);
debug!("selected some coins - {}", coins.len());
let fee = tx_fee(coins.len(), 2, 1, None);
let num_change_outputs = 1;
let (mut parts, _) =
selection::inputs_and_change(&coins, wallet, amount, fee, num_change_outputs)?;
//TODO: If we end up using this, create change output here
// add burn output and fees
parts.push(build::output(amount - fee, Identifier::zero()));
// finalize the burn transaction and send
let tx_burn = build::transaction(parts, &keychain)?;
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
tx_burn.validate(verifier_cache)?;
Ok(tx_burn)
}
#[cfg(test)]
mod test {
use keychain::{ExtKeychain, ExtKeychainPath, Keychain};
+13
View File
@@ -26,6 +26,7 @@ use failure::ResultExt;
use uuid::Uuid;
use core::core::hash::Hash;
use core::core::Transaction;
use core::ser;
use keychain::{Identifier, Keychain};
@@ -33,6 +34,7 @@ use keychain::{Identifier, Keychain};
use libtx::aggsig;
use libwallet::error::{Error, ErrorKind};
use util;
use util::secp::key::{PublicKey, SecretKey};
use util::secp::{self, pedersen, Secp256k1};
@@ -639,6 +641,17 @@ impl TxLogEntry {
pub fn update_confirmation_ts(&mut self) {
self.confirmation_ts = Some(Utc::now());
}
/// Retrieve the stored transaction, if any
pub fn get_stored_tx(&self) -> Option<Transaction> {
match self.tx_hex.as_ref() {
None => None,
Some(t) => {
let tx_bin = util::from_hex(t.clone()).unwrap();
Some(ser::deserialize::<Transaction>(&mut &tx_bin[..]).unwrap())
}
}
}
}
/// Map of named accounts to BIP32 paths
+6 -6
View File
@@ -86,21 +86,21 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// add some accounts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let new_path = api.new_account_path("account1").unwrap();
let new_path = api.create_account_path("account1").unwrap();
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 1, 0, 0, 0));
let new_path = api.new_account_path("account2").unwrap();
let new_path = api.create_account_path("account2").unwrap();
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 2, 0, 0, 0));
let new_path = api.new_account_path("account3").unwrap();
let new_path = api.create_account_path("account3").unwrap();
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 3, 0, 0, 0));
// trying to add same label again should fail
let res = api.new_account_path("account1");
let res = api.create_account_path("account1");
assert!(res.is_err());
Ok(())
})?;
// add account to wallet 2
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let new_path = api.new_account_path("listener_account").unwrap();
let new_path = api.create_account_path("listener_account").unwrap();
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 1, 0, 0, 0));
Ok(())
})?;
@@ -194,7 +194,7 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
slate = client1.send_tx_slate_direct("wallet2", &slate)?;
api.tx_lock_outputs(&slate, lock_fn)?;
api.finalize_tx(&mut slate)?;
api.post_tx(&slate, false)?;
api.post_tx(&slate.tx, false)?;
Ok(())
})?;
+5 -5
View File
@@ -376,11 +376,11 @@ impl WalletCommAdapter for LocalWalletClient {
fn listen(
&self,
params: HashMap<String, String>,
config: WalletConfig,
passphrase: &str,
account: &str,
node_api_secret: Option<String>,
_params: HashMap<String, String>,
_config: WalletConfig,
_passphrase: &str,
_account: &str,
_node_api_secret: Option<String>,
) -> Result<(), libwallet::Error> {
unimplemented!();
}
+5 -5
View File
@@ -74,15 +74,15 @@ fn file_exchange_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// add some accounts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.new_account_path("mining")?;
api.new_account_path("listener")?;
api.create_account_path("mining")?;
api.create_account_path("listener")?;
Ok(())
})?;
// add some accounts
wallet::controller::owner_single_use(wallet2.clone(), |api| {
api.new_account_path("account1")?;
api.new_account_path("account2")?;
api.create_account_path("account1")?;
api.create_account_path("account2")?;
Ok(())
})?;
@@ -141,7 +141,7 @@ fn file_exchange_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
let adapter = FileWalletCommAdapter::new();
let mut slate = adapter.receive_tx_async(&receive_file)?;
api.finalize_tx(&mut slate)?;
api.post_tx(&slate, false);
api.post_tx(&slate.tx, false)?;
bh += 1;
Ok(())
})?;
+6 -6
View File
@@ -75,15 +75,15 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// add some accounts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.new_account_path("mining")?;
api.new_account_path("listener")?;
api.create_account_path("mining")?;
api.create_account_path("listener")?;
Ok(())
})?;
// add some accounts
wallet::controller::owner_single_use(wallet2.clone(), |api| {
api.new_account_path("account1")?;
api.new_account_path("account2")?;
api.create_account_path("account1")?;
api.create_account_path("account2")?;
Ok(())
})?;
@@ -155,7 +155,7 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// Now repost from cached
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (_, txs) = api.retrieve_txs(true, None, Some(slate.id))?;
api.post_stored_tx(txs[0].id, false)?;
api.post_tx(&txs[0].get_stored_tx().unwrap(), false)?;
bh += 1;
Ok(())
})?;
@@ -220,7 +220,7 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// Now repost from cached
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (_, txs) = api.retrieve_txs(true, None, Some(slate.id))?;
api.post_stored_tx(txs[0].id, false)?;
api.post_tx(&txs[0].get_stored_tx().unwrap(), false)?;
bh += 1;
Ok(())
})?;
+6 -6
View File
@@ -198,8 +198,8 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
// wallet 2 will use another account
wallet::controller::owner_single_use(wallet2.clone(), |api| {
api.new_account_path("account1")?;
api.new_account_path("account2")?;
api.create_account_path("account1")?;
api.create_account_path("account2")?;
Ok(())
})?;
@@ -240,7 +240,7 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, lock_fn)?;
sender_api.finalize_tx(&mut slate)?;
sender_api.post_tx(&slate, false)?;
sender_api.post_tx(&slate.tx, false)?;
Ok(())
})?;
@@ -261,7 +261,7 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
slate = client1.send_tx_slate_direct("wallet3", &slate_i)?;
sender_api.tx_lock_outputs(&slate, lock_fn)?;
sender_api.finalize_tx(&mut slate)?;
sender_api.post_tx(&slate, false)?;
sender_api.post_tx(&slate.tx, false)?;
Ok(())
})?;
@@ -282,7 +282,7 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
slate = client3.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, lock_fn)?;
sender_api.finalize_tx(&mut slate)?;
sender_api.post_tx(&slate, false)?;
sender_api.post_tx(&slate.tx, false)?;
Ok(())
})?;
@@ -309,7 +309,7 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
slate = client3.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, lock_fn)?;
sender_api.finalize_tx(&mut slate)?;
sender_api.post_tx(&slate, false)?;
sender_api.post_tx(&slate.tx, false)?;
Ok(())
})?;
+3 -3
View File
@@ -72,8 +72,8 @@ fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// add some accounts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.new_account_path("mining")?;
api.new_account_path("listener")?;
api.create_account_path("mining")?;
api.create_account_path("listener")?;
Ok(())
})?;
@@ -109,7 +109,7 @@ fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
})?;
api.finalize_tx(&mut slate)?;
api.tx_lock_outputs(&slate, lock_fn)?;
api.post_tx(&slate, false)?; // mines a block
api.post_tx(&slate.tx, false)?; // mines a block
bh += 1;
Ok(())
})?;
+2 -3
View File
@@ -157,7 +157,7 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
// post transaction
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.post_tx(&slate, false)?;
api.post_tx(&slate.tx, false)?;
Ok(())
})?;
@@ -255,13 +255,12 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
let (refreshed, _wallet1_info) = sender_api.retrieve_summary_info(true, 1)?;
assert!(refreshed);
let (_, txs) = sender_api.retrieve_txs(true, None, None)?;
// find the transaction
let tx = txs
.iter()
.find(|t| t.tx_slate_id == Some(slate.id))
.unwrap();
sender_api.post_stored_tx(tx.id, false)?;
sender_api.post_tx(&tx.get_stored_tx().unwrap(), false)?;
let (_, wallet1_info) = sender_api.retrieve_summary_info(true, 1)?;
// should be mined now
assert_eq!(