Store XORed wallet seed in-mem ()

* experimental xor token work

* rustfmt

* test implementation of build_coinbase_t function

* rustfmt

* add separate foreign_rpc_s interface for secure functions

* rustfmt

* rustfmt

* fix http scheme to allow https as well

* add tokenized owner API, modify all functions to use token

* rustfmt

* fix for api doctests, tests passing up to api crate

* rustfmt

* controller crate compilation

* rustfmt

* controller tests passing and modified some to use masked keychains

* rustfmt

* fix wallet tests

* rustfmt

* build from github

* rustfmt
This commit is contained in:
Yeastplume 2019-08-06 12:50:41 +01:00 committed by GitHub
parent 5cdd412205
commit f8c316a351
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 3671 additions and 1250 deletions

808
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,8 @@ failure = "0.1"
failure_derive = "0.1"
log = "0.4"
uuid = { version = "0.7", features = ["serde", "v4"] }
serde = "1"
serde_derive = "1"
serde_json = "1"
easy-jsonrpc = "0.5.1"
chrono = { version = "0.4.4", features = ["serde"] }

View file

@ -20,6 +20,7 @@ use crate::libwallet::{
BlockFees, CbData, Error, NodeClient, NodeVersionInfo, Slate, VersionInfo, WalletInst,
WalletLCProvider,
};
use crate::util::secp::key::SecretKey;
use crate::util::Mutex;
use std::sync::Arc;
@ -67,6 +68,8 @@ where
pub doctest_mode: bool,
/// foreign check middleware
middleware: Option<ForeignCheckMiddleware>,
/// Stored keychain mask (in case the stored wallet seed is tokenized)
keychain_mask: Option<SecretKey>,
}
impl<'a, L, C, K> Foreign<'a, L, C, K>
@ -86,6 +89,9 @@ where
/// # Arguments
/// * `wallet_in` - A reference-counted mutex containing an implementation of the
/// [`WalletBackend`](../grin_wallet_libwallet/types/trait.WalletBackend.html) trait.
/// * `keychain_mask` - Mask value stored internally to use when calling a wallet
/// whose seed has been XORed with a token value (such as when running the foreign
/// and owner listeners in the same instance)
/// * middleware - Option middleware which containts the NodeVersionInfo and can call
/// a predefined function with the slate to check if the operation should continue
///
@ -142,24 +148,26 @@ where
///
/// // Wallet must be opened with the password (TBD)
/// let pw = ZeroingString::from("wallet_password");
/// lc.open_wallet(None, pw);
/// lc.open_wallet(None, pw, false, false);
///
/// // All wallet functions operate on an Arc::Mutex to allow multithreading where needed
/// let mut wallet = Arc::new(Mutex::new(wallet));
///
/// let api_foreign = Foreign::new(wallet.clone(), None);
/// let api_foreign = Foreign::new(wallet.clone(), None, None);
/// // .. perform wallet operations
///
/// ```
pub fn new(
wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<SecretKey>,
middleware: Option<ForeignCheckMiddleware>,
) -> Self {
Foreign {
wallet_inst,
doctest_mode: false,
middleware,
keychain_mask,
}
}
@ -173,7 +181,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_foreign = Foreign::new(wallet.clone(), None);
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
///
/// let version_info = api_foreign.check_version();
/// // check and proceed accordingly
@ -225,7 +233,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_foreign = Foreign::new(wallet.clone(), None);
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
///
/// let block_fees = BlockFees {
/// fees: 800000,
@ -252,7 +260,12 @@ where
None,
)?;
}
foreign::build_coinbase(&mut **w, block_fees, self.doctest_mode)
foreign::build_coinbase(
&mut **w,
(&self.keychain_mask).as_ref(),
block_fees,
self.doctest_mode,
)
}
/// Verifies all messages in the slate match their public keys.
@ -276,7 +289,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_foreign = Foreign::new(wallet.clone(), None);
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
///
/// # let slate = Slate::blank(2);
/// // Receive a slate via some means
@ -349,7 +362,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_foreign = Foreign::new(wallet.clone(), None);
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
/// # let slate = Slate::blank(2);
///
/// // . . .
@ -377,7 +390,14 @@ where
Some(slate),
)?;
}
foreign::receive_tx(&mut **w, slate, dest_acct_name, message, self.doctest_mode)
foreign::receive_tx(
&mut **w,
(&self.keychain_mask).as_ref(),
slate,
dest_acct_name,
message,
self.doctest_mode,
)
}
/// Finalizes an invoice transaction initiated by this wallet's Owner api.
@ -408,7 +428,7 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let mut api_foreign = Foreign::new(wallet.clone(), None);
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
///
/// // . . .
/// // Issue the invoice tx via the owner API
@ -416,7 +436,7 @@ where
/// amount: 10_000_000_000,
/// ..Default::default()
/// };
/// let result = api_owner.issue_invoice_tx(args);
/// let result = api_owner.issue_invoice_tx(None, args);
///
/// // If result okay, send to payer, who will apply the transaction via their
/// // owner API, then send back the slate
@ -437,7 +457,7 @@ where
Some(slate),
)?;
}
foreign::finalize_invoice_tx(&mut **w, slate)
foreign::finalize_invoice_tx(&mut **w, (&self.keychain_mask).as_ref(), slate)
}
}
@ -487,7 +507,7 @@ macro_rules! doctest_helper_setup_doc_env_foreign {
>;
let lc = wallet.lc_provider().unwrap();
lc.set_wallet_directory(&wallet_config.data_file_dir);
lc.open_wallet(None, pw);
lc.open_wallet(None, pw, false, false);
let mut $wallet = Arc::new(Mutex::new(wallet));
};
}

View file

@ -58,13 +58,13 @@ pub trait ForeignRpc {
}
}
# "#
# , 0, false, false);
# ,false, 0, false, false);
```
*/
fn check_version(&self) -> Result<VersionInfo, ErrorKind>;
/**
Networked version of [Foreign::build_coinbase](struct.Foreign.html#method.build_coinbase).
Networked Legacy (non-secure token) version of [Foreign::build_coinbase](struct.Foreign.html#method.build_coinbase).
# Json rpc example
@ -108,9 +108,10 @@ pub trait ForeignRpc {
}
}
# "#
# , 4, false, false);
# ,false, 4, false, false);
```
*/
fn build_coinbase(&self, block_fees: &BlockFees) -> Result<CbData, ErrorKind>;
/**
@ -188,7 +189,7 @@ pub trait ForeignRpc {
}
}
# "#
# ,1 ,false, false);
# ,false, 1 ,false, false);
```
*/
fn verify_slate_messages(&self, slate: &Slate) -> Result<(), ErrorKind>;
@ -341,7 +342,7 @@ pub trait ForeignRpc {
}
}
# "#
# , 5, true, false);
# ,false, 5, true, false);
```
*/
fn receive_tx(
@ -509,7 +510,7 @@ pub trait ForeignRpc {
}
}
# "#
# , 5, false, true);
# ,false, 5, false, true);
```
*/
fn finalize_invoice_tx(&self, slate: &Slate) -> Result<Slate, ErrorKind>;
@ -571,6 +572,7 @@ fn test_check_middleware(
pub fn run_doctest_foreign(
request: serde_json::Value,
test_dir: &str,
use_token: bool,
blocks_to_mine: u64,
init_tx: bool,
init_invoice_tx: bool,
@ -622,10 +624,21 @@ pub fn run_doctest_foreign(
lc.set_wallet_directory(&format!("{}/wallet1", test_dir));
lc.create_wallet(None, Some(rec_phrase_1), 32, empty_string.clone())
.unwrap();
lc.open_wallet(None, empty_string.clone()).unwrap();
let mask1 = lc
.open_wallet(None, empty_string.clone(), use_token, true)
.unwrap();
let wallet1 = Arc::new(Mutex::new(wallet1));
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
if mask1.is_some() {
println!("WALLET 1 MASK: {:?}", mask1.clone().unwrap());
}
wallet_proxy.add_wallet(
"wallet1",
client1.get_send_instance(),
wallet1.clone(),
mask1.clone(),
);
let rec_phrase_2 = util::ZeroingString::from(
"hour kingdom ripple lunch razor inquiry coyote clay stamp mean \
@ -646,10 +659,17 @@ pub fn run_doctest_foreign(
lc.set_wallet_directory(&format!("{}/wallet2", test_dir));
lc.create_wallet(None, Some(rec_phrase_2), 32, empty_string.clone())
.unwrap();
lc.open_wallet(None, empty_string.clone()).unwrap();
let mask2 = lc
.open_wallet(None, empty_string.clone(), use_token, true)
.unwrap();
let wallet2 = Arc::new(Mutex::new(wallet2));
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
wallet_proxy.add_wallet(
"wallet2",
client2.get_send_instance(),
wallet2.clone(),
mask2.clone(),
);
// Set the wallet proxy listener running
thread::spawn(move || {
@ -660,12 +680,18 @@ pub fn run_doctest_foreign(
// Mine a few blocks to wallet 1 so there's something to send
for _ in 0..blocks_to_mine {
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 1 as usize, false);
let _ = test_framework::award_blocks_to_wallet(
&chain,
wallet1.clone(),
(&mask1).as_ref(),
1 as usize,
false,
);
//update local outputs after each block, so transaction IDs stay consistent
let mut w_lock = wallet1.lock();
let w = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
let (wallet_refreshed, _) =
api_impl::owner::retrieve_summary_info(&mut **w, true, 1).unwrap();
api_impl::owner::retrieve_summary_info(&mut **w, (&mask1).as_ref(), true, 1).unwrap();
assert!(wallet_refreshed);
}
@ -678,7 +704,7 @@ pub fn run_doctest_foreign(
amount,
..Default::default()
};
api_impl::owner::issue_invoice_tx(&mut **w, args, true).unwrap()
api_impl::owner::issue_invoice_tx(&mut **w, (&mask2).as_ref(), args, true).unwrap()
};
slate = {
let mut w_lock = wallet1.lock();
@ -692,7 +718,8 @@ pub fn run_doctest_foreign(
selection_strategy_is_use_all: true,
..Default::default()
};
api_impl::owner::process_invoice_tx(&mut **w, &slate, args, true).unwrap()
api_impl::owner::process_invoice_tx(&mut **w, (&mask1).as_ref(), &slate, args, true)
.unwrap()
};
println!("INIT INVOICE SLATE");
// Spit out slate for input to finalize_invoice_tx
@ -712,15 +739,15 @@ pub fn run_doctest_foreign(
selection_strategy_is_use_all: true,
..Default::default()
};
let slate = api_impl::owner::init_send_tx(&mut **w, args, true).unwrap();
let slate = api_impl::owner::init_send_tx(&mut **w, (&mask1).as_ref(), args, true).unwrap();
println!("INIT SLATE");
// Spit out slate for input to finalize_tx
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
}
let mut api_foreign = match init_invoice_tx {
false => Foreign::new(wallet1, Some(test_check_middleware)),
true => Foreign::new(wallet2, Some(test_check_middleware)),
false => Foreign::new(wallet1, mask1, Some(test_check_middleware)),
true => Foreign::new(wallet2, mask2, Some(test_check_middleware)),
};
api_foreign.doctest_mode = true;
let foreign_api = &api_foreign as &dyn ForeignRpc;
@ -730,7 +757,7 @@ pub fn run_doctest_foreign(
#[doc(hidden)]
#[macro_export]
macro_rules! doctest_helper_json_rpc_foreign_assert_response {
($request:expr, $expected_response:expr, $blocks_to_mine:expr, $init_tx:expr, $init_invoice_tx:expr) => {
($request:expr, $expected_response:expr, $use_token:expr, $blocks_to_mine:expr, $init_tx:expr, $init_invoice_tx:expr) => {
// create temporary wallet, run jsonrpc request on owner api of wallet, delete wallet, return
// json response.
// In order to prevent leaking tempdirs, This function should not panic.
@ -752,6 +779,7 @@ macro_rules! doctest_helper_json_rpc_foreign_assert_response {
let response = run_doctest_foreign(
request_val,
dir,
$use_token,
$blocks_to_mine,
$init_tx,
$init_invoice_tx,

View file

@ -29,6 +29,8 @@ extern crate grin_wallet_impls as impls;
extern crate grin_wallet_libwallet as libwallet;
extern crate failure_derive;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
@ -39,12 +41,25 @@ mod foreign_rpc;
mod owner;
mod owner_rpc;
mod owner_rpc_s;
pub use crate::foreign::{Foreign, ForeignCheckMiddleware, ForeignCheckMiddlewareFn};
pub use crate::foreign_rpc::ForeignRpc;
pub use crate::owner::Owner;
pub use crate::owner_rpc::OwnerRpc;
pub use crate::owner_rpc_s::OwnerRpcS;
pub use crate::foreign_rpc::foreign_rpc as foreign_rpc_client;
pub use crate::foreign_rpc::run_doctest_foreign;
pub use crate::owner_rpc::run_doctest_owner;
use grin_wallet_util::grin_core::libtx::secp_ser;
use util::secp::key::SecretKey;
/// Wrapper for API Tokens
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(transparent)]
pub struct Token {
#[serde(with = "secp_ser::option_seckey_serde")]
keychain_mask: Option<SecretKey>,
}

View file

@ -26,6 +26,7 @@ use crate::libwallet::{
NodeHeightResult, OutputCommitMapping, Slate, TxLogEntry, WalletInfo, WalletInst,
WalletLCProvider,
};
use crate::util::secp::key::SecretKey;
use crate::util::Mutex;
use std::sync::Arc;
@ -126,7 +127,7 @@ where
///
/// // Wallet must be opened with the password (TBD)
/// let pw = ZeroingString::from("wallet_password");
/// lc.open_wallet(None, pw);
/// lc.open_wallet(None, pw, false, false);
///
/// // All wallet functions operate on an Arc::Mutex to allow multithreading where needed
/// let mut wallet = Arc::new(Mutex::new(wallet));
@ -145,6 +146,9 @@ where
/// Returns a list of accounts stored in the wallet (i.e. mappings between
/// user-specified labels and BIP32 derivation paths.
/// # Arguments
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
///
/// # Returns
/// * Result Containing:
@ -164,16 +168,21 @@ where
///
/// let api_owner = Owner::new(wallet.clone());
///
/// let result = api_owner.accounts();
/// let result = api_owner.accounts(None);
///
/// if let Ok(accts) = result {
/// //...
/// }
/// ```
pub fn accounts(&self) -> Result<Vec<AcctPathMapping>, Error> {
pub fn accounts(
&self,
keychain_mask: Option<&SecretKey>,
) -> Result<Vec<AcctPathMapping>, Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
// Test keychain mask, to keep API consistent
let _ = w.keychain(keychain_mask)?;
owner::accounts(&mut **w)
}
@ -181,6 +190,9 @@ where
/// label to a BIP32 path
///
/// # Arguments
///
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `label` - A human readable label to which to map the new BIP32 Path
///
/// # Returns
@ -207,23 +219,29 @@ where
///
/// let api_owner = Owner::new(wallet.clone());
///
/// let result = api_owner.create_account_path("account1");
/// let result = api_owner.create_account_path(None, "account1");
///
/// if let Ok(identifier) = result {
/// //...
/// }
/// ```
pub fn create_account_path(&self, label: &str) -> Result<Identifier, Error> {
pub fn create_account_path(
&self,
keychain_mask: Option<&SecretKey>,
label: &str,
) -> Result<Identifier, Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::create_account_path(&mut **w, label)
owner::create_account_path(&mut **w, keychain_mask, label)
}
/// Sets the wallet's currently active account. This sets the
/// BIP32 parent path used for most key-derivation operations.
///
/// # Arguments
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `label` - The human readable label for the account. Accounts can be retrieved via
/// the [`account`](struct.Owner.html#method.accounts) method
///
@ -248,23 +266,31 @@ where
///
/// let api_owner = Owner::new(wallet.clone());
///
/// let result = api_owner.create_account_path("account1");
/// let result = api_owner.create_account_path(None, "account1");
///
/// if let Ok(identifier) = result {
/// // set the account active
/// let result2 = api_owner.set_active_account("account1");
/// let result2 = api_owner.set_active_account(None, "account1");
/// }
/// ```
pub fn set_active_account(&self, label: &str) -> Result<(), Error> {
pub fn set_active_account(
&self,
keychain_mask: Option<&SecretKey>,
label: &str,
) -> Result<(), Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
// Test keychain mask, to keep API consistent
let _ = w.keychain(keychain_mask)?;
owner::set_active_account(&mut **w, label)
}
/// Returns a list of outputs from the active account in the wallet.
///
/// # Arguments
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `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.
@ -297,7 +323,7 @@ where
/// let update_from_node = true;
/// let tx_id = None;
///
/// let result = api_owner.retrieve_outputs(show_spent, update_from_node, tx_id);
/// let result = api_owner.retrieve_outputs(None, show_spent, update_from_node, tx_id);
///
/// if let Ok((was_updated, output_mappings)) = result {
/// //...
@ -306,19 +332,28 @@ where
pub fn retrieve_outputs(
&self,
keychain_mask: Option<&SecretKey>,
include_spent: bool,
refresh_from_node: bool,
tx_id: Option<u32>,
) -> Result<(bool, Vec<OutputCommitMapping>), Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::retrieve_outputs(&mut **w, include_spent, refresh_from_node, tx_id)
owner::retrieve_outputs(
&mut **w,
keychain_mask,
include_spent,
refresh_from_node,
tx_id,
)
}
/// Returns a list of [Transaction Log Entries](../grin_wallet_libwallet/types/struct.TxLogEntry.html)
/// from the active account in the wallet.
///
/// # Arguments
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `refresh_from_node` - If true, the wallet will attempt to contact
/// a node (via the [`NodeClient`](../grin_wallet_libwallet/types/trait.NodeClient.html)
/// provided during wallet instantiation). If `false`, the results will
@ -348,7 +383,7 @@ where
/// let tx_slate_id = None;
///
/// // Return all TxLogEntries
/// let result = api_owner.retrieve_txs(update_from_node, tx_id, tx_slate_id);
/// let result = api_owner.retrieve_txs(None, update_from_node, tx_id, tx_slate_id);
///
/// if let Ok((was_updated, tx_log_entries)) = result {
/// //...
@ -357,13 +392,20 @@ where
pub fn retrieve_txs(
&self,
keychain_mask: Option<&SecretKey>,
refresh_from_node: bool,
tx_id: Option<u32>,
tx_slate_id: Option<Uuid>,
) -> Result<(bool, Vec<TxLogEntry>), Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
let mut res = owner::retrieve_txs(&mut **w, refresh_from_node, tx_id, tx_slate_id)?;
let mut res = owner::retrieve_txs(
&mut **w,
keychain_mask,
refresh_from_node,
tx_id,
tx_slate_id,
)?;
if self.doctest_mode {
res.1 = res
.1
@ -381,6 +423,8 @@ where
/// Returns summary information from the active account in the wallet.
///
/// # Arguments
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `refresh_from_node` - If true, the wallet will attempt to contact
/// a node (via the [`NodeClient`](../grin_wallet_libwallet/types/trait.NodeClient.html)
/// provided during wallet instantiation). If `false`, the results will
@ -406,7 +450,7 @@ where
/// let minimum_confirmations=10;
///
/// // Return summary info for active account
/// let result = api_owner.retrieve_summary_info(update_from_node, minimum_confirmations);
/// let result = api_owner.retrieve_summary_info(None, update_from_node, minimum_confirmations);
///
/// if let Ok((was_updated, summary_info)) = result {
/// //...
@ -415,12 +459,18 @@ where
pub fn retrieve_summary_info(
&self,
keychain_mask: Option<&SecretKey>,
refresh_from_node: bool,
minimum_confirmations: u64,
) -> Result<(bool, WalletInfo), Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::retrieve_summary_info(&mut **w, refresh_from_node, minimum_confirmations)
owner::retrieve_summary_info(
&mut **w,
keychain_mask,
refresh_from_node,
minimum_confirmations,
)
}
/// Initiates a new transaction as the sender, creating a new
@ -447,6 +497,8 @@ where
/// the `finalize` field is set.
///
/// # Arguments
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `args` - [`InitTxArgs`](../grin_wallet_libwallet/types/struct.InitTxArgs.html),
/// transaction initialization arguments. See struct documentation for further detail.
///
@ -485,6 +537,7 @@ where
/// ..Default::default()
/// };
/// let result = api_owner.init_send_tx(
/// None,
/// args,
/// );
///
@ -492,16 +545,20 @@ where
/// // Send slate somehow
/// // ...
/// // Lock our outputs if we're happy the slate was (or is being) sent
/// api_owner.tx_lock_outputs(&slate, 0);
/// api_owner.tx_lock_outputs(None, &slate, 0);
/// }
/// ```
pub fn init_send_tx(&self, args: InitTxArgs) -> Result<Slate, Error> {
pub fn init_send_tx(
&self,
keychain_mask: Option<&SecretKey>,
args: InitTxArgs,
) -> Result<Slate, Error> {
let send_args = args.send_args.clone();
let mut slate = {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::init_send_tx(&mut **w, args, self.doctest_mode)?
owner::init_send_tx(&mut **w, keychain_mask, args, self.doctest_mode)?
};
// Helper functionality. If send arguments exist, attempt to send
match send_args {
@ -520,14 +577,14 @@ where
let comm_adapter = create_sender(&sa.method, &sa.dest)
.map_err(|e| ErrorKind::GenericError(format!("{}", e)))?;
slate = comm_adapter.send_tx(&slate)?;
self.tx_lock_outputs(&slate, 0)?;
self.tx_lock_outputs(keychain_mask, &slate, 0)?;
let slate = match sa.finalize {
true => self.finalize_tx(&slate)?,
true => self.finalize_tx(keychain_mask, &slate)?,
false => slate,
};
if sa.post_tx {
self.post_tx(&slate.tx, sa.fluff)?;
self.post_tx(keychain_mask, &slate.tx, sa.fluff)?;
}
Ok(slate)
}
@ -542,6 +599,8 @@ where
/// via the [Foreign API's `finalize_invoice_tx`](struct.Foreign.html#method.finalize_invoice_tx) method.
///
/// # Arguments
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `args` - [`IssueInvoiceTxArgs`](../grin_wallet_libwallet/types/struct.IssueInvoiceTxArgs.html),
/// invoice transaction initialization arguments. See struct documentation for further detail.
///
@ -561,17 +620,21 @@ where
/// amount: 60_000_000_000,
/// ..Default::default()
/// };
/// let result = api_owner.issue_invoice_tx(args);
/// let result = api_owner.issue_invoice_tx(None, args);
///
/// if let Ok(slate) = result {
/// // if okay, send to the payer to add their inputs
/// // . . .
/// }
/// ```
pub fn issue_invoice_tx(&self, args: IssueInvoiceTxArgs) -> Result<Slate, Error> {
pub fn issue_invoice_tx(
&self,
keychain_mask: Option<&SecretKey>,
args: IssueInvoiceTxArgs,
) -> Result<Slate, Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::issue_invoice_tx(&mut **w, args, self.doctest_mode)
owner::issue_invoice_tx(&mut **w, keychain_mask, args, self.doctest_mode)
}
/// Processes an invoice tranaction created by another party, essentially
@ -589,6 +652,8 @@ where
/// via the [`get_stored_tx`](struct.Owner.html#method.get_stored_tx) function.
///
/// # Arguments
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `slate` - The transaction [`Slate`](../grin_wallet_libwallet/slate/struct.Slate.html). The
/// payer should have filled in round 1 and 2.
/// * `args` - [`InitTxArgs`](../grin_wallet_libwallet/types/struct.InitTxArgs.html),
@ -619,7 +684,7 @@ where
/// ..Default::default()
/// };
///
/// let result = api_owner.process_invoice_tx(&slate, args);
/// let result = api_owner.process_invoice_tx(None, &slate, args);
///
/// if let Ok(slate) = result {
/// // If result okay, send back to the invoicer
@ -627,10 +692,15 @@ where
/// }
/// ```
pub fn process_invoice_tx(&self, slate: &Slate, args: InitTxArgs) -> Result<Slate, Error> {
pub fn process_invoice_tx(
&self,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
args: InitTxArgs,
) -> Result<Slate, Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::process_invoice_tx(&mut **w, slate, args, self.doctest_mode)
owner::process_invoice_tx(&mut **w, keychain_mask, slate, args, self.doctest_mode)
}
/// Locks the outputs associated with the inputs to the transaction in the given
@ -647,6 +717,8 @@ where
/// and return them to the `Unspent` state.
///
/// # Arguments
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `slate` - The transaction [`Slate`](../grin_wallet_libwallet/slate/struct.Slate.html). All
/// * `participant_id` - The participant id, generally 0 for the party putting in funds, 1 for the
/// party receiving.
@ -674,6 +746,7 @@ where
/// ..Default::default()
/// };
/// let result = api_owner.init_send_tx(
/// None,
/// args,
/// );
///
@ -681,14 +754,19 @@ where
/// // Send slate somehow
/// // ...
/// // Lock our outputs if we're happy the slate was (or is being) sent
/// api_owner.tx_lock_outputs(&slate, 0);
/// api_owner.tx_lock_outputs(None, &slate, 0);
/// }
/// ```
pub fn tx_lock_outputs(&self, slate: &Slate, participant_id: usize) -> Result<(), Error> {
pub fn tx_lock_outputs(
&self,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
participant_id: usize,
) -> Result<(), Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::tx_lock_outputs(&mut **w, slate, participant_id)
owner::tx_lock_outputs(&mut **w, keychain_mask, slate, participant_id)
}
/// Finalizes a transaction, after all parties
@ -704,6 +782,8 @@ where
/// via the [`get_stored_tx`](struct.Owner.html#method.get_stored_tx) function.
///
/// # Arguments
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `slate` - The transaction [`Slate`](../grin_wallet_libwallet/slate/struct.Slate.html). All
/// participants must have filled in both rounds, and the sender should have locked their
/// outputs (via the [`tx_lock_outputs`](struct.Owner.html#method.tx_lock_outputs) function).
@ -730,6 +810,7 @@ where
/// ..Default::default()
/// };
/// let result = api_owner.init_send_tx(
/// None,
/// args,
/// );
///
@ -737,27 +818,32 @@ where
/// // Send slate somehow
/// // ...
/// // Lock our outputs if we're happy the slate was (or is being) sent
/// let res = api_owner.tx_lock_outputs(&slate, 0);
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
/// //
/// // Retrieve slate back from recipient
/// //
/// let res = api_owner.finalize_tx(&slate);
/// let res = api_owner.finalize_tx(None, &slate);
/// }
/// ```
pub fn finalize_tx(&self, slate: &Slate) -> Result<Slate, Error> {
pub fn finalize_tx(
&self,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
) -> Result<Slate, Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::finalize_tx(&mut **w, &slate)
owner::finalize_tx(&mut **w, keychain_mask, &slate)
}
/// Posts a completed transaction to the listening node for validation and inclusion in a block
/// for mining.
///
/// # Arguments
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `tx` - A completed [`Transaction`](../grin_core/core/transaction/struct.Transaction.html),
/// typically the `tx` field in the transaction [`Slate`](../grin_wallet_libwallet/slate/struct.Slate.html).
///
/// * `fluff` - Instruct the node whether to use the Dandelion protocol when posting the
/// transaction. If `true`, the node should skip the Dandelion phase and broadcast the
/// transaction to all peers immediately. If `false`, the node will follow dandelion logic and
@ -784,6 +870,7 @@ where
/// ..Default::default()
/// };
/// let result = api_owner.init_send_tx(
/// None,
/// args,
/// );
///
@ -791,19 +878,26 @@ where
/// // Send slate somehow
/// // ...
/// // Lock our outputs if we're happy the slate was (or is being) sent
/// let res = api_owner.tx_lock_outputs(&slate, 0);
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
/// //
/// // Retrieve slate back from recipient
/// //
/// let res = api_owner.finalize_tx(&slate);
/// let res = api_owner.post_tx(&slate.tx, true);
/// let res = api_owner.finalize_tx(None, &slate);
/// let res = api_owner.post_tx(None, &slate.tx, true);
/// }
/// ```
pub fn post_tx(&self, tx: &Transaction, fluff: bool) -> Result<(), Error> {
pub fn post_tx(
&self,
keychain_mask: Option<&SecretKey>,
tx: &Transaction,
fluff: bool,
) -> Result<(), Error> {
let client = {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
// Test keychain mask, to keep API consistent
let _ = w.keychain(keychain_mask)?;
w.w2n_client().clone()
};
owner::post_tx(&client, tx, fluff)
@ -820,6 +914,8 @@ where
///
/// # Arguments
///
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `tx_id` - If present, cancel by the [`TxLogEntry`](../grin_wallet_libwallet/types/struct.TxLogEntry.html) id
/// for the transaction.
///
@ -846,6 +942,7 @@ where
/// ..Default::default()
/// };
/// let result = api_owner.init_send_tx(
/// None,
/// args,
/// );
///
@ -853,18 +950,23 @@ where
/// // Send slate somehow
/// // ...
/// // Lock our outputs if we're happy the slate was (or is being) sent
/// let res = api_owner.tx_lock_outputs(&slate, 0);
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
/// //
/// // We didn't get the slate back, or something else went wrong
/// //
/// let res = api_owner.cancel_tx(None, Some(slate.id.clone()));
/// let res = api_owner.cancel_tx(None, None, Some(slate.id.clone()));
/// }
/// ```
pub fn cancel_tx(&self, tx_id: Option<u32>, tx_slate_id: Option<Uuid>) -> Result<(), Error> {
pub fn cancel_tx(
&self,
keychain_mask: Option<&SecretKey>,
tx_id: Option<u32>,
tx_slate_id: Option<Uuid>,
) -> Result<(), Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::cancel_tx(&mut **w, tx_id, tx_slate_id)
owner::cancel_tx(&mut **w, keychain_mask, tx_id, tx_slate_id)
}
/// Retrieves the stored transaction associated with a TxLogEntry. Can be used even after the
@ -872,6 +974,8 @@ where
///
/// # Arguments
///
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `tx_log_entry` - A [`TxLogEntry`](../grin_wallet_libwallet/types/struct.TxLogEntry.html)
///
/// # Returns
@ -890,18 +994,24 @@ where
/// let tx_slate_id = None;
///
/// // Return all TxLogEntries
/// let result = api_owner.retrieve_txs(update_from_node, tx_id, tx_slate_id);
/// let result = api_owner.retrieve_txs(None, update_from_node, tx_id, tx_slate_id);
///
/// if let Ok((was_updated, tx_log_entries)) = result {
/// let stored_tx = api_owner.get_stored_tx(&tx_log_entries[0]).unwrap();
/// let stored_tx = api_owner.get_stored_tx(None, &tx_log_entries[0]).unwrap();
/// //...
/// }
/// ```
// TODO: Should be accepting an id, not an entire entry struct
pub fn get_stored_tx(&self, tx_log_entry: &TxLogEntry) -> Result<Option<Transaction>, Error> {
pub fn get_stored_tx(
&self,
keychain_mask: Option<&SecretKey>,
tx_log_entry: &TxLogEntry,
) -> Result<Option<Transaction>, Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
// Test keychain mask, to keep API consistent
let _ = w.keychain(keychain_mask)?;
owner::get_stored_tx(&**w, tx_log_entry)
}
@ -915,6 +1025,8 @@ where
///
/// # Arguments
///
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `slate` - The transaction [`Slate`](../grin_wallet_libwallet/slate/struct.Slate.html).
///
/// # Returns
@ -938,6 +1050,7 @@ where
/// ..Default::default()
/// };
/// let result = api_owner.init_send_tx(
/// None,
/// args,
/// );
///
@ -945,14 +1058,24 @@ where
/// // Send slate somehow
/// // ...
/// // Lock our outputs if we're happy the slate was (or is being) sent
/// let res = api_owner.tx_lock_outputs(&slate, 0);
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
/// //
/// // Retrieve slate back from recipient
/// //
/// let res = api_owner.verify_slate_messages(&slate);
/// let res = api_owner.verify_slate_messages(None, &slate);
/// }
/// ```
pub fn verify_slate_messages(&self, slate: &Slate) -> Result<(), Error> {
pub fn verify_slate_messages(
&self,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
) -> Result<(), Error> {
{
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
// Test keychain mask, to keep API consistent
let _ = w.keychain(keychain_mask)?;
}
owner::verify_slate_messages(slate)
}
@ -971,7 +1094,8 @@ where
///
/// # Arguments
///
/// * None
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
///
/// # Returns
/// * `Ok(())` if successful
@ -983,17 +1107,17 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let result = api_owner.restore();
/// let result = api_owner.restore(None);
///
/// if let Ok(_) = result {
/// // Wallet outputs should be consistent with what's on chain
/// // ...
/// }
/// ```
pub fn restore(&self) -> Result<(), Error> {
pub fn restore(&self, keychain_mask: Option<&SecretKey>) -> Result<(), Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
let res = owner::restore(&mut **w);
let res = owner::restore(&mut **w, keychain_mask);
res
}
@ -1013,6 +1137,8 @@ where
///
/// # Arguments
///
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
/// * `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,
@ -1034,6 +1160,7 @@ where
///
/// let mut api_owner = Owner::new(wallet.clone());
/// let result = api_owner.check_repair(
/// None,
/// false,
/// );
///
@ -1043,10 +1170,14 @@ where
/// }
/// ```
pub fn check_repair(&self, delete_unconfirmed: bool) -> Result<(), Error> {
pub fn check_repair(
&self,
keychain_mask: Option<&SecretKey>,
delete_unconfirmed: bool,
) -> Result<(), Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::check_repair(&mut **w, delete_unconfirmed)
owner::check_repair(&mut **w, keychain_mask, delete_unconfirmed)
}
/// Retrieves the last known height known by the wallet. This is determined as follows:
@ -1061,7 +1192,8 @@ where
///
/// # Arguments
///
/// * None
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// being used.
///
/// # Returns
/// * Ok with a [`NodeHeightResult`](../grin_wallet_libwallet/types/struct.NodeHeightResult.html)
@ -1075,7 +1207,7 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone());
/// let result = api_owner.node_height();
/// let result = api_owner.node_height(None);
///
/// if let Ok(node_height_result) = result {
/// if node_height_result.updated_from_node {
@ -1086,10 +1218,15 @@ where
/// }
/// ```
pub fn node_height(&self) -> Result<NodeHeightResult, Error> {
pub fn node_height(
&self,
keychain_mask: Option<&SecretKey>,
) -> Result<NodeHeightResult, Error> {
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::node_height(&mut **w)
// Test keychain mask, to keep API consistent
let _ = w.keychain(keychain_mask)?;
owner::node_height(&mut **w, keychain_mask)
}
}
@ -1139,7 +1276,7 @@ macro_rules! doctest_helper_setup_doc_env {
>;
let lc = wallet.lc_provider().unwrap();
lc.set_wallet_directory(&wallet_config.data_file_dir);
lc.open_wallet(None, pw);
lc.open_wallet(None, pw, false, false);
let mut $wallet = Arc::new(Mutex::new(wallet));
};
}

View file

@ -23,7 +23,7 @@ use crate::libwallet::{
WalletLCProvider,
};
use crate::util::Mutex;
use crate::Owner;
use crate::{Owner, OwnerRpcS};
use easy_jsonrpc;
use std::sync::Arc;
@ -63,7 +63,7 @@ pub trait OwnerRpc {
"id": 1
}
# "#
# , 4, false, false, false);
# , false, 4, false, false, false);
```
*/
fn accounts(&self) -> Result<Vec<AcctPathMapping>, ErrorKind>;
@ -93,7 +93,7 @@ pub trait OwnerRpc {
"id": 1
}
# "#
# ,4, false, false, false);
# ,false, 4, false, false, false);
```
*/
fn create_account_path(&self, label: &String) -> Result<Identifier, ErrorKind>;
@ -123,7 +123,7 @@ pub trait OwnerRpc {
"id": 1
}
# "#
# , 4, false, false, false);
# , false, 4, false, false, false);
```
*/
fn set_active_account(&self, label: &String) -> Result<(), ErrorKind>;
@ -189,7 +189,7 @@ pub trait OwnerRpc {
}
}
# "#
# , 2, false, false, false);
# , false, 2, false, false, false);
```
*/
fn retrieve_outputs(
@ -260,7 +260,7 @@ pub trait OwnerRpc {
}
}
# "#
# , 2, false, false, false);
# , false, 2, false, false, false);
```
*/
@ -306,7 +306,7 @@ pub trait OwnerRpc {
}
}
# "#
# ,4, false, false, false);
# ,false, 4, false, false, false);
```
*/
@ -400,7 +400,7 @@ pub trait OwnerRpc {
}
}
# "#
# ,4, false, false, false);
# ,false, 4, false, false, false);
```
*/
@ -480,7 +480,7 @@ pub trait OwnerRpc {
}
}
# "#
# ,4, false, false, false);
# ,false, 4, false, false, false);
```
*/
@ -628,7 +628,7 @@ pub trait OwnerRpc {
}
}
# "#
# ,4, false, false, false);
# ,false, 4, false, false, false);
```
*/
@ -712,7 +712,7 @@ pub trait OwnerRpc {
}
}
# "#
# ,5 ,true, false, false);
# ,false, 5 ,true, false, false);
```
*/
@ -879,7 +879,7 @@ pub trait OwnerRpc {
}
}
# "#
# , 5, true, true, false);
# , false, 5, true, true, false);
```
*/
fn finalize_tx(&self, slate: VersionedSlate) -> Result<VersionedSlate, ErrorKind>;
@ -945,7 +945,7 @@ pub trait OwnerRpc {
}
}
# "#
# , 5, true, true, true);
# , false, 5, true, true, true);
```
*/
@ -975,7 +975,7 @@ pub trait OwnerRpc {
}
}
# "#
# , 5, true, true, false);
# , false, 5, true, true, false);
```
*/
fn cancel_tx(&self, tx_id: Option<u32>, tx_slate_id: Option<Uuid>) -> Result<(), ErrorKind>;
@ -1070,7 +1070,7 @@ pub trait OwnerRpc {
}
}
# "#
# , 5, true, true, false);
# , false, 5, true, true, false);
```
*/
fn get_stored_tx(&self, tx: &TxLogEntry) -> Result<Option<Transaction>, ErrorKind>;
@ -1148,7 +1148,7 @@ pub trait OwnerRpc {
}
}
# "#
# ,5 ,true, false, false);
# ,false, 5 ,true, false, false);
```
*/
fn verify_slate_messages(&self, slate: VersionedSlate) -> Result<(), ErrorKind>;
@ -1177,7 +1177,7 @@ pub trait OwnerRpc {
}
}
# "#
# , 1, false, false, false);
# , false, 1, false, false, false);
```
*/
fn restore(&self) -> Result<(), ErrorKind>;
@ -1206,7 +1206,7 @@ pub trait OwnerRpc {
}
}
# "#
# , 1, false, false, false);
# , false, 1, false, false, false);
```
*/
fn check_repair(&self, delete_unconfirmed: bool) -> Result<(), ErrorKind>;
@ -1238,7 +1238,7 @@ pub trait OwnerRpc {
}
}
# "#
# , 5, false, false, false);
# , false, 5, false, false, false);
```
*/
fn node_height(&self) -> Result<NodeHeightResult, ErrorKind>;
@ -1251,15 +1251,15 @@ where
K: Keychain + 'a,
{
fn accounts(&self) -> Result<Vec<AcctPathMapping>, ErrorKind> {
Owner::accounts(self).map_err(|e| e.kind())
Owner::accounts(self, None).map_err(|e| e.kind())
}
fn create_account_path(&self, label: &String) -> Result<Identifier, ErrorKind> {
Owner::create_account_path(self, label).map_err(|e| e.kind())
Owner::create_account_path(self, None, label).map_err(|e| e.kind())
}
fn set_active_account(&self, label: &String) -> Result<(), ErrorKind> {
Owner::set_active_account(self, label).map_err(|e| e.kind())
Owner::set_active_account(self, None, label).map_err(|e| e.kind())
}
fn retrieve_outputs(
@ -1268,7 +1268,8 @@ where
refresh_from_node: bool,
tx_id: Option<u32>,
) -> Result<(bool, Vec<OutputCommitMapping>), ErrorKind> {
Owner::retrieve_outputs(self, include_spent, refresh_from_node, tx_id).map_err(|e| e.kind())
Owner::retrieve_outputs(self, None, include_spent, refresh_from_node, tx_id)
.map_err(|e| e.kind())
}
fn retrieve_txs(
@ -1277,7 +1278,7 @@ where
tx_id: Option<u32>,
tx_slate_id: Option<Uuid>,
) -> Result<(bool, Vec<TxLogEntry>), ErrorKind> {
Owner::retrieve_txs(self, refresh_from_node, tx_id, tx_slate_id).map_err(|e| e.kind())
Owner::retrieve_txs(self, None, refresh_from_node, tx_id, tx_slate_id).map_err(|e| e.kind())
}
fn retrieve_summary_info(
@ -1285,18 +1286,18 @@ where
refresh_from_node: bool,
minimum_confirmations: u64,
) -> Result<(bool, WalletInfo), ErrorKind> {
Owner::retrieve_summary_info(self, refresh_from_node, minimum_confirmations)
Owner::retrieve_summary_info(self, None, refresh_from_node, minimum_confirmations)
.map_err(|e| e.kind())
}
fn init_send_tx(&self, args: InitTxArgs) -> Result<VersionedSlate, ErrorKind> {
let slate = Owner::init_send_tx(self, args).map_err(|e| e.kind())?;
let slate = Owner::init_send_tx(self, None, args).map_err(|e| e.kind())?;
let version = SlateVersion::V2;
Ok(VersionedSlate::into_version(slate, version))
}
fn issue_invoice_tx(&self, args: IssueInvoiceTxArgs) -> Result<VersionedSlate, ErrorKind> {
let slate = Owner::issue_invoice_tx(self, args).map_err(|e| e.kind())?;
let slate = Owner::issue_invoice_tx(self, None, args).map_err(|e| e.kind())?;
let version = SlateVersion::V2;
Ok(VersionedSlate::into_version(slate, version))
}
@ -1307,14 +1308,15 @@ where
args: InitTxArgs,
) -> Result<VersionedSlate, ErrorKind> {
let in_slate = Slate::from(slate);
let out_slate = Owner::process_invoice_tx(self, &in_slate, args).map_err(|e| e.kind())?;
let out_slate =
Owner::process_invoice_tx(self, None, &in_slate, args).map_err(|e| e.kind())?;
let version = SlateVersion::V2;
Ok(VersionedSlate::into_version(out_slate, version))
}
fn finalize_tx(&self, slate: VersionedSlate) -> Result<VersionedSlate, ErrorKind> {
let in_slate = Slate::from(slate);
let out_slate = Owner::finalize_tx(self, &in_slate).map_err(|e| e.kind())?;
let out_slate = Owner::finalize_tx(self, None, &in_slate).map_err(|e| e.kind())?;
let version = SlateVersion::V2;
Ok(VersionedSlate::into_version(out_slate, version))
}
@ -1325,36 +1327,36 @@ where
participant_id: usize,
) -> Result<(), ErrorKind> {
let in_slate = Slate::from(slate);
Owner::tx_lock_outputs(self, &in_slate, participant_id).map_err(|e| e.kind())
Owner::tx_lock_outputs(self, None, &in_slate, participant_id).map_err(|e| e.kind())
}
fn cancel_tx(&self, tx_id: Option<u32>, tx_slate_id: Option<Uuid>) -> Result<(), ErrorKind> {
Owner::cancel_tx(self, tx_id, tx_slate_id).map_err(|e| e.kind())
Owner::cancel_tx(self, None, tx_id, tx_slate_id).map_err(|e| e.kind())
}
fn get_stored_tx(&self, tx: &TxLogEntry) -> Result<Option<Transaction>, ErrorKind> {
Owner::get_stored_tx(self, tx).map_err(|e| e.kind())
Owner::get_stored_tx(self, None, tx).map_err(|e| e.kind())
}
fn post_tx(&self, tx: &Transaction, fluff: bool) -> Result<(), ErrorKind> {
Owner::post_tx(self, tx, fluff).map_err(|e| e.kind())
Owner::post_tx(self, None, tx, fluff).map_err(|e| e.kind())
}
fn verify_slate_messages(&self, slate: VersionedSlate) -> Result<(), ErrorKind> {
let in_slate = Slate::from(slate);
Owner::verify_slate_messages(self, &in_slate).map_err(|e| e.kind())
Owner::verify_slate_messages(self, None, &in_slate).map_err(|e| e.kind())
}
fn restore(&self) -> Result<(), ErrorKind> {
Owner::restore(self).map_err(|e| e.kind())
Owner::restore(self, None).map_err(|e| e.kind())
}
fn check_repair(&self, delete_unconfirmed: bool) -> Result<(), ErrorKind> {
Owner::check_repair(self, delete_unconfirmed).map_err(|e| e.kind())
Owner::check_repair(self, None, delete_unconfirmed).map_err(|e| e.kind())
}
fn node_height(&self) -> Result<NodeHeightResult, ErrorKind> {
Owner::node_height(self).map_err(|e| e.kind())
Owner::node_height(self, None).map_err(|e| e.kind())
}
}
@ -1362,6 +1364,7 @@ where
pub fn run_doctest_owner(
request: serde_json::Value,
test_dir: &str,
use_token: bool,
blocks_to_mine: u64,
perform_tx: bool,
lock_tx: bool,
@ -1412,10 +1415,21 @@ pub fn run_doctest_owner(
lc.set_wallet_directory(&format!("{}/wallet1", test_dir));
lc.create_wallet(None, Some(rec_phrase_1), 32, empty_string.clone())
.unwrap();
lc.open_wallet(None, empty_string.clone()).unwrap();
let mask1 = lc
.open_wallet(None, empty_string.clone(), use_token, true)
.unwrap();
let wallet1 = Arc::new(Mutex::new(wallet1));
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
if mask1.is_some() {
println!("WALLET 1 MASK: {:?}", mask1.clone().unwrap());
}
wallet_proxy.add_wallet(
"wallet1",
client1.get_send_instance(),
wallet1.clone(),
mask1.clone(),
);
let rec_phrase_2 = util::ZeroingString::from(
"hour kingdom ripple lunch razor inquiry coyote clay stamp mean \
@ -1436,10 +1450,21 @@ pub fn run_doctest_owner(
lc.set_wallet_directory(&format!("{}/wallet2", test_dir));
lc.create_wallet(None, Some(rec_phrase_2), 32, empty_string.clone())
.unwrap();
lc.open_wallet(None, empty_string.clone()).unwrap();
let mask2 = lc
.open_wallet(None, empty_string.clone(), use_token, true)
.unwrap();
let wallet2 = Arc::new(Mutex::new(wallet2));
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
if mask2.is_some() {
println!("WALLET 2 MASK: {:?}", mask2.clone().unwrap());
}
wallet_proxy.add_wallet(
"wallet2",
client2.get_send_instance(),
wallet2.clone(),
mask2.clone(),
);
// Set the wallet proxy listener running
thread::spawn(move || {
@ -1450,12 +1475,18 @@ pub fn run_doctest_owner(
// Mine a few blocks to wallet 1 so there's something to send
for _ in 0..blocks_to_mine {
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 1 as usize, false);
let _ = test_framework::award_blocks_to_wallet(
&chain,
wallet1.clone(),
(&mask1).as_ref(),
1 as usize,
false,
);
//update local outputs after each block, so transaction IDs stay consistent
let mut w_lock = wallet1.lock();
let w = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
let (wallet_refreshed, _) =
api_impl::owner::retrieve_summary_info(&mut **w, true, 1).unwrap();
api_impl::owner::retrieve_summary_info(&mut **w, (&mask1).as_ref(), true, 1).unwrap();
assert!(wallet_refreshed);
}
@ -1472,23 +1503,32 @@ pub fn run_doctest_owner(
selection_strategy_is_use_all: true,
..Default::default()
};
let mut slate = api_impl::owner::init_send_tx(&mut **w, args, true).unwrap();
let mut slate =
api_impl::owner::init_send_tx(&mut **w, (&mask1).as_ref(), args, true).unwrap();
println!("INITIAL SLATE");
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
{
let mut w_lock = wallet2.lock();
let w2 = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
slate = api_impl::foreign::receive_tx(&mut **w2, &slate, None, None, true).unwrap();
slate = api_impl::foreign::receive_tx(
&mut **w2,
(&mask2).as_ref(),
&slate,
None,
None,
true,
)
.unwrap();
w2.close().unwrap();
}
// Spit out slate for input to finalize_tx
if lock_tx {
api_impl::owner::tx_lock_outputs(&mut **w, &slate, 0).unwrap();
api_impl::owner::tx_lock_outputs(&mut **w, (&mask2).as_ref(), &slate, 0).unwrap();
}
println!("RECEIPIENT SLATE");
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
if finalize_tx {
slate = api_impl::owner::finalize_tx(&mut **w, &slate).unwrap();
slate = api_impl::owner::finalize_tx(&mut **w, (&mask2).as_ref(), &slate).unwrap();
error!("FINALIZED TX SLATE");
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
}
@ -1496,19 +1536,30 @@ pub fn run_doctest_owner(
if perform_tx && lock_tx && finalize_tx {
// mine to move the chain on
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3 as usize, false);
let _ = test_framework::award_blocks_to_wallet(
&chain,
wallet1.clone(),
(&mask1).as_ref(),
3 as usize,
false,
);
}
let mut api_owner = Owner::new(wallet1);
api_owner.doctest_mode = true;
let owner_api = &api_owner as &dyn OwnerRpc;
Ok(owner_api.handle_request(request).as_option())
if use_token {
let owner_api = &api_owner as &dyn OwnerRpcS;
Ok(owner_api.handle_request(request).as_option())
} else {
let owner_api = &api_owner as &dyn OwnerRpc;
Ok(owner_api.handle_request(request).as_option())
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! doctest_helper_json_rpc_owner_assert_response {
($request:expr, $expected_response:expr, $blocks_to_mine:expr, $perform_tx:expr, $lock_tx:expr, $finalize_tx:expr) => {
($request:expr, $expected_response:expr, $use_token:expr, $blocks_to_mine:expr, $perform_tx:expr, $lock_tx:expr, $finalize_tx:expr) => {
// create temporary wallet, run jsonrpc request on owner api of wallet, delete wallet, return
// json response.
// In order to prevent leaking tempdirs, This function should not panic.
@ -1530,6 +1581,7 @@ macro_rules! doctest_helper_json_rpc_owner_assert_response {
let response = run_doctest_owner(
request_val,
dir,
$use_token,
$blocks_to_mine,
$perform_tx,
$lock_tx,

1474
api/src/owner_rpc_s.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -52,7 +52,7 @@ fn _receive_versioned_slate() {
let request_val: Value = serde_json::from_str(v1_req).unwrap();
let expected_response: Value = serde_json::from_str(v1_resp).unwrap();
let response = run_doctest_foreign(request_val, dir, 5, true, false)
let response = run_doctest_foreign(request_val, dir, false, 5, true, false)
.unwrap()
.unwrap();

View file

@ -22,6 +22,7 @@ use crate::impls::{create_sender, KeybaseAllChannels, SlateGetter as _, SlateRec
use crate::impls::{PathToSlate, SlatePutter};
use crate::keychain;
use crate::libwallet::{InitTxArgs, IssueInvoiceTxArgs, NodeClient, WalletInst, WalletLCProvider};
use crate::util::secp::key::SecretKey;
use crate::util::{Mutex, ZeroingString};
use crate::{controller, display};
use serde_json as json;
@ -120,6 +121,7 @@ pub struct ListenArgs {
pub fn listen<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>,
keychain_mask: Option<SecretKey>,
config: &WalletConfig,
args: &ListenArgs,
g_args: &GlobalArgs,
@ -132,6 +134,7 @@ where
let res = match args.method.as_str() {
"http" => controller::foreign_listener(
wallet.clone(),
keychain_mask,
&config.api_listen_addr(),
g_args.tls_conf.clone(),
),
@ -158,6 +161,7 @@ where
pub fn owner_api<L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>,
keychain_mask: Option<SecretKey>,
config: &WalletConfig,
g_args: &GlobalArgs,
) -> Result<(), Error>
@ -168,6 +172,7 @@ where
{
let res = controller::owner_listener(
wallet,
keychain_mask,
config.owner_api_listen_addr().as_str(),
g_args.node_api_secret.clone(),
g_args.tls_conf.clone(),
@ -186,6 +191,7 @@ pub struct AccountArgs {
pub fn account<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
args: AccountArgs,
) -> Result<(), Error>
where
@ -194,8 +200,8 @@ where
K: keychain::Keychain + 'a,
{
if args.create.is_none() {
let res = controller::owner_single_use(wallet, |api| {
let acct_mappings = api.accounts()?;
let res = controller::owner_single_use(wallet, keychain_mask, |api, m| {
let acct_mappings = api.accounts(m)?;
// give logging thread a moment to catch up
thread::sleep(Duration::from_millis(200));
display::accounts(acct_mappings);
@ -207,8 +213,8 @@ where
}
} else {
let label = args.create.unwrap();
let res = controller::owner_single_use(wallet, |api| {
api.create_account_path(&label)?;
let res = controller::owner_single_use(wallet, keychain_mask, |api, m| {
api.create_account_path(m, &label)?;
thread::sleep(Duration::from_millis(200));
info!("Account: '{}' Created!", label);
Ok(())
@ -239,6 +245,7 @@ pub struct SendArgs {
pub fn send<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
args: SendArgs,
dark_scheme: bool,
) -> Result<(), Error>
@ -247,7 +254,7 @@ where
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| {
if args.estimate_selection_strategies {
let strategies = vec!["smallest", "all"]
.into_iter()
@ -262,7 +269,7 @@ where
estimate_only: Some(true),
..Default::default()
};
let slate = api.init_send_tx(init_args).unwrap();
let slate = api.init_send_tx(m, init_args).unwrap();
(strategy, slate.amount, slate.fee)
})
.collect();
@ -280,7 +287,7 @@ where
send_args: None,
..Default::default()
};
let result = api.init_send_tx(init_args);
let result = api.init_send_tx(m, init_args);
let mut slate = match result {
Ok(s) => {
info!(
@ -300,12 +307,16 @@ where
match args.method.as_str() {
"file" => {
PathToSlate((&args.dest).into()).put_tx(&slate)?;
api.tx_lock_outputs(&slate, 0)?;
api.tx_lock_outputs(m, &slate, 0)?;
return Ok(());
}
"self" => {
api.tx_lock_outputs(&slate, 0)?;
controller::foreign_single_use(wallet, |api| {
api.tx_lock_outputs(m, &slate, 0)?;
let km = match keychain_mask.as_ref() {
None => None,
Some(&m) => Some(m.to_owned()),
};
controller::foreign_single_use(wallet, km, |api| {
slate = api.receive_tx(&slate, Some(&args.dest), None)?;
Ok(())
})?;
@ -313,16 +324,16 @@ where
method => {
let sender = create_sender(method, &args.dest)?;
slate = sender.send_tx(&slate)?;
api.tx_lock_outputs(&slate, 0)?;
api.tx_lock_outputs(m, &slate, 0)?;
}
}
api.verify_slate_messages(&slate).map_err(|e| {
api.verify_slate_messages(m, &slate).map_err(|e| {
error!("Error validating participant messages: {}", e);
e
})?;
slate = api.finalize_tx(&slate)?;
let result = api.post_tx(&slate.tx, args.fluff);
slate = api.finalize_tx(m, &slate)?;
let result = api.post_tx(m, &slate.tx, args.fluff);
match result {
Ok(_) => {
info!("Tx sent ok",);
@ -347,6 +358,7 @@ pub struct ReceiveArgs {
pub fn receive<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
g_args: &GlobalArgs,
args: ReceiveArgs,
) -> Result<(), Error>
@ -356,7 +368,11 @@ where
K: keychain::Keychain + 'a,
{
let mut slate = PathToSlate((&args.input).into()).get_tx()?;
controller::foreign_single_use(wallet, |api| {
let km = match keychain_mask.as_ref() {
None => None,
Some(&m) => Some(m.to_owned()),
};
controller::foreign_single_use(wallet, km, |api| {
if let Err(e) = api.verify_slate_messages(&slate) {
error!("Error validating participant messages: {}", e);
return Err(e);
@ -380,6 +396,7 @@ pub struct FinalizeArgs {
pub fn finalize<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
args: FinalizeArgs,
) -> Result<(), Error>
where
@ -408,7 +425,11 @@ where
};
if is_invoice {
controller::foreign_single_use(wallet.clone(), |api| {
let km = match keychain_mask.as_ref() {
None => None,
Some(&m) => Some(m.to_owned()),
};
controller::foreign_single_use(wallet.clone(), km, |api| {
if let Err(e) = api.verify_slate_messages(&slate) {
error!("Error validating participant messages: {}", e);
return Err(e);
@ -417,18 +438,18 @@ where
Ok(())
})?;
} else {
controller::owner_single_use(wallet.clone(), |api| {
if let Err(e) = api.verify_slate_messages(&slate) {
controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| {
if let Err(e) = api.verify_slate_messages(m, &slate) {
error!("Error validating participant messages: {}", e);
return Err(e);
}
slate = api.finalize_tx(&mut slate)?;
slate = api.finalize_tx(m, &mut slate)?;
Ok(())
})?;
}
controller::owner_single_use(wallet.clone(), |api| {
let result = api.post_tx(&slate.tx, args.fluff);
controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| {
let result = api.post_tx(m, &slate.tx, args.fluff);
match result {
Ok(_) => {
info!("Transaction sent successfully, check the wallet again for confirmation.");
@ -454,6 +475,7 @@ pub struct IssueInvoiceArgs {
pub fn issue_invoice_tx<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
args: IssueInvoiceArgs,
) -> Result<(), Error>
where
@ -461,8 +483,8 @@ where
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let slate = api.issue_invoice_tx(args.issue_args)?;
controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| {
let slate = api.issue_invoice_tx(m, args.issue_args)?;
let mut tx_file = File::create(args.dest.clone())?;
tx_file.write_all(json::to_string(&slate).unwrap().as_bytes())?;
tx_file.sync_all()?;
@ -486,6 +508,7 @@ pub struct ProcessInvoiceArgs {
/// Process invoice
pub fn process_invoice<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
args: ProcessInvoiceArgs,
dark_scheme: bool,
) -> Result<(), Error>
@ -495,7 +518,7 @@ where
K: keychain::Keychain + 'a,
{
let slate = PathToSlate((&args.input).into()).get_tx()?;
controller::owner_single_use(wallet.clone(), |api| {
controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| {
if args.estimate_selection_strategies {
let strategies = vec!["smallest", "all"]
.into_iter()
@ -510,7 +533,7 @@ where
estimate_only: Some(true),
..Default::default()
};
let slate = api.init_send_tx(init_args).unwrap();
let slate = api.init_send_tx(m, init_args).unwrap();
(strategy, slate.amount, slate.fee)
})
.collect();
@ -527,11 +550,11 @@ where
send_args: None,
..Default::default()
};
if let Err(e) = api.verify_slate_messages(&slate) {
if let Err(e) = api.verify_slate_messages(m, &slate) {
error!("Error validating participant messages: {}", e);
return Err(e);
}
let result = api.process_invoice_tx(&slate, init_args);
let result = api.process_invoice_tx(m, &slate, init_args);
let mut slate = match result {
Ok(s) => {
info!(
@ -552,11 +575,15 @@ where
"file" => {
let slate_putter = PathToSlate((&args.dest).into());
slate_putter.put_tx(&slate)?;
api.tx_lock_outputs(&slate, 0)?;
api.tx_lock_outputs(m, &slate, 0)?;
}
"self" => {
api.tx_lock_outputs(&slate, 0)?;
controller::foreign_single_use(wallet, |api| {
api.tx_lock_outputs(m, &slate, 0)?;
let km = match keychain_mask.as_ref() {
None => None,
Some(&m) => Some(m.to_owned()),
};
controller::foreign_single_use(wallet, km, |api| {
slate = api.finalize_invoice_tx(&slate)?;
Ok(())
})?;
@ -564,7 +591,7 @@ where
method => {
let sender = create_sender(method, &args.dest)?;
slate = sender.send_tx(&slate)?;
api.tx_lock_outputs(&slate, 0)?;
api.tx_lock_outputs(m, &slate, 0)?;
}
}
}
@ -579,6 +606,7 @@ pub struct InfoArgs {
pub fn info<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
g_args: &GlobalArgs,
args: InfoArgs,
dark_scheme: bool,
@ -588,9 +616,9 @@ where
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| {
let (validated, wallet_info) =
api.retrieve_summary_info(true, args.minimum_confirmations)?;
api.retrieve_summary_info(m, true, args.minimum_confirmations)?;
display::info(&g_args.account, &wallet_info, validated, dark_scheme);
Ok(())
})?;
@ -599,6 +627,7 @@ where
pub fn outputs<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
g_args: &GlobalArgs,
dark_scheme: bool,
) -> Result<(), Error>
@ -607,9 +636,9 @@ where
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let res = api.node_height()?;
let (validated, outputs) = api.retrieve_outputs(g_args.show_spent, true, None)?;
controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| {
let res = api.node_height(m)?;
let (validated, outputs) = api.retrieve_outputs(m, g_args.show_spent, true, None)?;
display::outputs(&g_args.account, res.height, validated, outputs, dark_scheme)?;
Ok(())
})?;
@ -624,6 +653,7 @@ pub struct TxsArgs {
pub fn txs<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
g_args: &GlobalArgs,
args: TxsArgs,
dark_scheme: bool,
@ -633,9 +663,9 @@ where
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let res = api.node_height()?;
let (validated, txs) = api.retrieve_txs(true, args.id, args.tx_slate_id)?;
controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| {
let res = api.node_height(m)?;
let (validated, txs) = api.retrieve_txs(m, true, args.id, args.tx_slate_id)?;
let include_status = !args.id.is_some() && !args.tx_slate_id.is_some();
display::txs(
&g_args.account,
@ -662,7 +692,7 @@ where
};
if id.is_some() {
let (_, outputs) = api.retrieve_outputs(true, false, id)?;
let (_, outputs) = api.retrieve_outputs(m, true, false, id)?;
display::outputs(&g_args.account, res.height, validated, outputs, dark_scheme)?;
// should only be one here, but just in case
for tx in txs {
@ -684,6 +714,7 @@ pub struct RepostArgs {
pub fn repost<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
args: RepostArgs,
) -> Result<(), Error>
where
@ -691,9 +722,9 @@ where
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let (_, txs) = api.retrieve_txs(true, Some(args.id), None)?;
let stored_tx = api.get_stored_tx(&txs[0])?;
controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| {
let (_, txs) = api.retrieve_txs(m, true, Some(args.id), None)?;
let stored_tx = api.get_stored_tx(m, &txs[0])?;
if stored_tx.is_none() {
error!(
"Transaction with id {} does not have transaction data. Not reposting.",
@ -710,7 +741,7 @@ where
);
return Ok(());
}
api.post_tx(&stored_tx.unwrap(), args.fluff)?;
api.post_tx(m, &stored_tx.unwrap(), args.fluff)?;
info!("Reposted transaction at {}", args.id);
return Ok(());
}
@ -735,6 +766,7 @@ pub struct CancelArgs {
pub fn cancel<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
args: CancelArgs,
) -> Result<(), Error>
where
@ -742,8 +774,8 @@ where
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let result = api.cancel_tx(args.tx_id, args.tx_slate_id);
controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| {
let result = api.cancel_tx(m, args.tx_id, args.tx_slate_id);
match result {
Ok(_) => {
info!("Transaction {} Cancelled", args.tx_id_string);
@ -760,14 +792,15 @@ where
pub fn restore<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let result = api.restore();
controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| {
let result = api.restore(m);
match result {
Ok(_) => {
warn!("Wallet restore complete",);
@ -790,6 +823,7 @@ pub struct CheckArgs {
pub fn check_repair<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
args: CheckArgs,
) -> Result<(), Error>
where
@ -797,10 +831,10 @@ where
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| {
warn!("Starting wallet check...",);
warn!("Updating all wallet outputs, please wait ...",);
let result = api.check_repair(args.delete_unconfirmed);
let result = api.check_repair(m, args.delete_unconfirmed);
match result {
Ok(_) => {
warn!("Wallet check complete",);

View file

@ -20,6 +20,7 @@ use crate::libwallet::{
Error, ErrorKind, NodeClient, NodeVersionInfo, Slate, WalletInst, WalletLCProvider,
CURRENT_SLATE_VERSION, GRIN_BLOCK_HEADER_VERSION,
};
use crate::util::secp::key::SecretKey;
use crate::util::{to_base64, Mutex};
use failure::ResultExt;
use futures::future::{err, ok};
@ -73,23 +74,25 @@ fn check_middleware(
/// Instantiate wallet Owner API for a single-use (command line) call
/// Return a function containing a loaded API context to call
pub fn owner_single_use<'a, L, F, C, K>(
lc_provider: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
f: F,
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
F: FnOnce(&mut Owner<'a, L, C, K>) -> Result<(), Error>,
F: FnOnce(&mut Owner<'a, L, C, K>, Option<&SecretKey>) -> Result<(), Error>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
f(&mut Owner::new(lc_provider))?;
f(&mut Owner::new(wallet), keychain_mask)?;
Ok(())
}
/// Instantiate wallet Foreign API for a single-use (command line) call
/// Return a function containing a loaded API context to call
pub fn foreign_single_use<'a, L, F, C, K>(
lc_provider: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<SecretKey>,
f: F,
) -> Result<(), Error>
where
@ -98,14 +101,21 @@ where
C: NodeClient + 'a,
K: Keychain + 'a,
{
f(&mut Foreign::new(lc_provider, Some(check_middleware)))?;
f(&mut Foreign::new(
wallet,
keychain_mask,
Some(check_middleware),
))?;
Ok(())
}
/// Listener version, providing same API but listening for requests on a
/// port and wrapping the calls
/// Note keychain mask is only provided here in case the foreign listener is also being used
/// in the same wallet instance
pub fn owner_listener<L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
keychain_mask: Option<SecretKey>,
addr: &str,
api_secret: Option<String>,
tls_config: Option<TLSConfig>,
@ -136,7 +146,7 @@ where
// If so configured, add the foreign API to the same port
if owner_api_include_foreign.unwrap_or(false) {
warn!("Starting HTTP Foreign API on Owner server at {}.", addr);
let foreign_api_handler_v2 = ForeignAPIHandlerV2::new(wallet);
let foreign_api_handler_v2 = ForeignAPIHandlerV2::new(wallet, keychain_mask);
router
.add_route("/v2/foreign", Arc::new(foreign_api_handler_v2))
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
@ -160,6 +170,7 @@ where
/// port and wrapping the calls
pub fn foreign_listener<L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
keychain_mask: Option<SecretKey>,
addr: &str,
tls_config: Option<TLSConfig>,
) -> Result<(), Error>
@ -168,7 +179,7 @@ where
C: NodeClient + 'static,
K: Keychain + 'static,
{
let api_handler_v2 = ForeignAPIHandlerV2::new(wallet);
let api_handler_v2 = ForeignAPIHandlerV2::new(wallet, keychain_mask);
let mut router = Router::new();
@ -275,6 +286,8 @@ where
{
/// Wallet instance
pub wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
/// Keychain mask
pub keychain_mask: Option<SecretKey>,
}
impl<L, C, K> ForeignAPIHandlerV2<L, C, K>
@ -286,8 +299,12 @@ where
/// Create a new foreign API handler for GET methods
pub fn new(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
keychain_mask: Option<SecretKey>,
) -> ForeignAPIHandlerV2<L, C, K> {
ForeignAPIHandlerV2 { wallet }
ForeignAPIHandlerV2 {
wallet,
keychain_mask,
}
}
fn call_api(
@ -309,7 +326,11 @@ where
}
fn handle_post_request(&self, req: Request<Body>) -> WalletResponseFuture {
let api = Foreign::new(self.wallet.clone(), Some(check_middleware));
let api = Foreign::new(
self.wallet.clone(),
self.keychain_mask.clone(),
Some(check_middleware),
);
Box::new(
self.call_api(req, api)
.and_then(|resp| ok(json_response_pretty(&resp))),

View file

@ -43,20 +43,29 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
create_wallet_and_add!(
client1,
wallet1,
mask1_i,
test_dir,
"wallet1",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask1 = (&mask1_i).as_ref();
create_wallet_and_add!(
client2,
wallet2,
mask2_i,
test_dir,
"wallet2",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask2 = (&mask2_i).as_ref();
// Set the wallet proxy listener running
thread::spawn(move || {
if let Err(e) = wallet_proxy.run() {
@ -69,30 +78,30 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
let cm = global::coinbase_maturity(); // assume all testing precedes soft fork height
// test default accounts exist
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let accounts = api.accounts()?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let accounts = api.accounts(m)?;
assert_eq!(accounts[0].label, "default");
assert_eq!(accounts[0].path, ExtKeychain::derive_key_id(2, 0, 0, 0, 0));
Ok(())
})?;
// add some accounts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let new_path = api.create_account_path("account1").unwrap();
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let new_path = api.create_account_path(m, "account1").unwrap();
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 1, 0, 0, 0));
let new_path = api.create_account_path("account2").unwrap();
let new_path = api.create_account_path(m, "account2").unwrap();
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 2, 0, 0, 0));
let new_path = api.create_account_path("account3").unwrap();
let new_path = api.create_account_path(m, "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.create_account_path("account1");
let res = api.create_account_path(m, "account1");
assert!(res.is_err());
Ok(())
})?;
// add account to wallet 2
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let new_path = api.create_account_path("listener_account").unwrap();
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let new_path = api.create_account_path(m, "listener_account").unwrap();
assert_eq!(new_path, ExtKeychain::derive_key_id(2, 1, 0, 0, 0));
Ok(())
})?;
@ -109,24 +118,24 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
w.set_parent_key_id_by_name("account1")?;
assert_eq!(w.parent_key_id(), ExtKeychain::derive_key_id(2, 1, 0, 0, 0));
}
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 7, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 7, false);
{
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("account2")?;
assert_eq!(w.parent_key_id(), ExtKeychain::derive_key_id(2, 2, 0, 0, 0));
}
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 5, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 5, false);
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, 12);
assert_eq!(wallet1_info.total, 5 * reward);
assert_eq!(wallet1_info.amount_currently_spendable, (5 - cm) * reward);
// check tx log as well
let (_, txs) = api.retrieve_txs(true, None, None)?;
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
assert_eq!(txs.len(), 5);
Ok(())
})?;
@ -137,17 +146,17 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
let w = lc.wallet_inst()?;
w.set_parent_key_id_by_name("account1")?;
}
wallet::controller::owner_single_use(wallet1.clone(), |api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
// check last confirmed height on this account is different from above (should be 0)
let (_, wallet1_info) = api.retrieve_summary_info(false, 1)?;
let (_, wallet1_info) = api.retrieve_summary_info(m, false, 1)?;
assert_eq!(wallet1_info.last_confirmed_height, 0);
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, 12);
assert_eq!(wallet1_info.total, 7 * reward);
assert_eq!(wallet1_info.amount_currently_spendable, 7 * reward);
// check tx log as well
let (_, txs) = api.retrieve_txs(true, None, None)?;
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
assert_eq!(txs.len(), 7);
Ok(())
})?;
@ -157,16 +166,16 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("default")?;
}
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (_, wallet1_info) = api.retrieve_summary_info(false, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (_, wallet1_info) = api.retrieve_summary_info(m, false, 1)?;
assert_eq!(wallet1_info.last_confirmed_height, 0);
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, 12);
assert_eq!(wallet1_info.total, 0,);
assert_eq!(wallet1_info.amount_currently_spendable, 0,);
// check tx log as well
let (_, txs) = api.retrieve_txs(true, None, None)?;
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
assert_eq!(txs.len(), 0);
Ok(())
})?;
@ -177,7 +186,7 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
w.set_parent_key_id_by_name("account1")?;
}
wallet::controller::owner_single_use(wallet1.clone(), |api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let args = InitTxArgs {
src_acct_name: None,
amount: reward,
@ -187,19 +196,19 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
selection_strategy_is_use_all: true,
..Default::default()
};
let mut slate = api.init_send_tx(args)?;
let mut slate = api.init_send_tx(m, args)?;
slate = client1.send_tx_slate_direct("wallet2", &slate)?;
api.tx_lock_outputs(&slate, 0)?;
slate = api.finalize_tx(&slate)?;
api.post_tx(&slate.tx, false)?;
api.tx_lock_outputs(m, &slate, 0)?;
slate = api.finalize_tx(m, &slate)?;
api.post_tx(m, &slate.tx, false)?;
Ok(())
})?;
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, 13);
let (_, txs) = api.retrieve_txs(true, None, None)?;
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
assert_eq!(txs.len(), 9);
Ok(())
})?;
@ -209,23 +218,23 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("account2")?;
}
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (_, wallet1_info) = api.retrieve_summary_info(false, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (_, wallet1_info) = api.retrieve_summary_info(m, false, 1)?;
assert_eq!(wallet1_info.last_confirmed_height, 12);
let (_, wallet1_info) = api.retrieve_summary_info(true, 1)?;
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert_eq!(wallet1_info.last_confirmed_height, 13);
let (_, txs) = api.retrieve_txs(true, None, None)?;
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
println!("{:?}", txs);
assert_eq!(txs.len(), 5);
Ok(())
})?;
// wallet 2 should only have this tx on the listener account
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet2_refreshed);
assert_eq!(wallet2_info.last_confirmed_height, 13);
let (_, txs) = api.retrieve_txs(true, None, None)?;
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
assert_eq!(txs.len(), 1);
Ok(())
})?;
@ -234,16 +243,16 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
wallet_inst!(wallet2, w);
w.set_parent_key_id_by_name("default")?;
}
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let (_, wallet2_info) = api.retrieve_summary_info(false, 1)?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let (_, wallet2_info) = api.retrieve_summary_info(m, false, 1)?;
assert_eq!(wallet2_info.last_confirmed_height, 0);
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(true, 1)?;
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet2_refreshed);
assert_eq!(wallet2_info.last_confirmed_height, 13);
assert_eq!(wallet2_info.total, 0,);
assert_eq!(wallet2_info.amount_currently_spendable, 0,);
// check tx log as well
let (_, txs) = api.retrieve_txs(true, None, None)?;
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
assert_eq!(txs.len(), 0);
Ok(())
})?;

View file

@ -35,14 +35,14 @@ mod common;
use common::{create_wallet_proxy, setup};
macro_rules! send_to_dest {
($a:expr, $b:expr, $c:expr, $d:expr) => {
test_framework::send_to_dest($a, $b, $c, $d, false)
($a:expr, $m: expr, $b:expr, $c:expr, $d:expr) => {
test_framework::send_to_dest($a, $m, $b, $c, $d, false)
};
}
macro_rules! wallet_info {
($a:expr) => {
test_framework::wallet_info($a)
($a:expr, $m:expr) => {
test_framework::wallet_info($a, $m)
};
}
@ -59,19 +59,25 @@ fn check_repair_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
create_wallet_and_add!(
client1,
wallet1,
mask1_i,
test_dir,
"wallet1",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask1 = (&mask1_i).as_ref();
create_wallet_and_add!(
client2,
wallet2,
mask2_i,
test_dir,
"wallet2",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask2 = (&mask2_i).as_ref();
// Set the wallet proxy listener running
thread::spawn(move || {
@ -85,34 +91,35 @@ fn check_repair_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
let cm = global::coinbase_maturity() as u64; // assume all testing precedes soft fork height
// add some accounts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.create_account_path("account_1")?;
api.create_account_path("account_2")?;
api.create_account_path("account_3")?;
api.set_active_account("account_1")?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.create_account_path(m, "account_1")?;
api.create_account_path(m, "account_2")?;
api.create_account_path(m, "account_3")?;
api.set_active_account(m, "account_1")?;
Ok(())
})?;
// add account to wallet 2
wallet::controller::owner_single_use(wallet2.clone(), |api| {
api.create_account_path("account_1")?;
api.set_active_account("account_1")?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
api.create_account_path(m, "account_1")?;
api.set_active_account(m, "account_1")?;
Ok(())
})?;
// Do some mining
let bh = 20u64;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), bh as usize, false);
let _ =
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
// Sanity check contents
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward);
assert_eq!(wallet1_info.amount_currently_spendable, (bh - cm) * reward);
// check tx log as well
let (_, txs) = api.retrieve_txs(true, None, None)?;
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
let (c, _) = libwallet::TxLogEntry::sum_confirmed(&txs);
assert_eq!(wallet1_info.total, c);
assert_eq!(txs.len(), bh as usize);
@ -121,8 +128,8 @@ fn check_repair_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
// Accidentally delete some outputs
let mut w1_outputs_commits = vec![];
wallet::controller::owner_single_use(wallet1.clone(), |api| {
w1_outputs_commits = api.retrieve_outputs(false, true, None)?.1;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
w1_outputs_commits = api.retrieve_outputs(m, false, true, None)?.1;
Ok(())
})?;
let w1_outputs: Vec<libwallet::OutputData> =
@ -130,7 +137,7 @@ fn check_repair_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
{
wallet_inst!(wallet1, w);
{
let mut batch = w.batch()?;
let mut batch = w.batch(mask1)?;
batch.delete(&w1_outputs[4].key_id, &None)?;
batch.delete(&w1_outputs[10].key_id, &None)?;
let mut accidental_spent = w1_outputs[13].clone();
@ -141,30 +148,30 @@ fn check_repair_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
}
// check we have a problem now
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (_, wallet1_info) = api.retrieve_summary_info(true, 1)?;
let (_, txs) = api.retrieve_txs(true, None, None)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
let (c, _) = libwallet::TxLogEntry::sum_confirmed(&txs);
assert!(wallet1_info.total != c);
Ok(())
})?;
// this should restore our missing outputs
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.check_repair(true)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.check_repair(m, true)?;
Ok(())
})?;
// check our outputs match again
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.total, bh * reward);
Ok(())
})?;
// perform a transaction, but don't let it finish
wallet::controller::owner_single_use(wallet1.clone(), |api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
// send to send
let args = InitTxArgs {
src_acct_name: None,
@ -175,30 +182,30 @@ fn check_repair_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
selection_strategy_is_use_all: true,
..Default::default()
};
let slate = api.init_send_tx(args)?;
let slate = api.init_send_tx(m, args)?;
// output tx file
let send_file = format!("{}/part_tx_1.tx", test_dir);
PathToSlate(send_file.into()).put_tx(&slate)?;
api.tx_lock_outputs(&slate, 0)?;
api.tx_lock_outputs(m, &slate, 0)?;
Ok(())
})?;
// check we're all locked
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (_, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_info.amount_currently_spendable == 0);
Ok(())
})?;
// unlock/restore
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.check_repair(true)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.check_repair(m, true)?;
Ok(())
})?;
// check spendable amount again
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (_, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert_eq!(wallet1_info.amount_currently_spendable, (bh - cm) * reward);
Ok(())
})?;
@ -221,94 +228,134 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er
// Create a new wallet test client, and set its queues to communicate with the
// proxy
create_wallet_and_add!(m_client, miner, test_dir, "miner", None, &mut wallet_proxy);
create_wallet_and_add!(
m_client,
miner,
miner_mask_i,
test_dir,
"miner",
None,
&mut wallet_proxy,
false
);
let miner_mask = (&miner_mask_i).as_ref();
// non-mining recipient wallets
create_wallet_and_add!(
client1,
wallet1,
mask1_i,
test_dir,
"wallet1",
seed_phrase,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask1 = (&mask1_i).as_ref();
create_wallet_and_add!(
client2,
wallet2,
mask2_i,
test_dir,
"wallet2",
seed_phrase,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask2 = (&mask2_i).as_ref();
// we'll restore into here
create_wallet_and_add!(
client3,
wallet3,
mask3_i,
test_dir,
"wallet3",
seed_phrase,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask3 = (&mask3_i).as_ref();
// also restore into here
create_wallet_and_add!(
client4,
wallet4,
mask4_i,
test_dir,
"wallet4",
seed_phrase,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask4 = (&mask4_i).as_ref();
// Simulate a recover from seed without restore into here
create_wallet_and_add!(
client5,
wallet5,
mask5_i,
test_dir,
"wallet5",
seed_phrase,
&mut wallet_proxy
&mut wallet_proxy,
false
);
//simulate a recover from seed without restore into here
let mask5 = (&mask5_i).as_ref();
create_wallet_and_add!(
client6,
wallet6,
mask6_i,
test_dir,
"wallet6",
seed_phrase,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask6 = (&mask6_i).as_ref();
create_wallet_and_add!(
client7,
wallet7,
mask7_i,
test_dir,
"wallet7",
seed_phrase,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask7 = (&mask7_i).as_ref();
create_wallet_and_add!(
client8,
wallet8,
mask8_i,
test_dir,
"wallet8",
seed_phrase,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask8 = (&mask8_i).as_ref();
create_wallet_and_add!(
client9,
wallet9,
mask9_i,
test_dir,
"wallet9",
seed_phrase,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask9 = (&mask9_i).as_ref();
create_wallet_and_add!(
client10,
wallet10,
mask10_i,
test_dir,
"wallet10",
seed_phrase,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask10 = (&mask10_i).as_ref();
// Set the wallet proxy listener running
thread::spawn(move || {
@ -324,38 +371,80 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er
// Do some mining
let mut bh = 20u64;
let base_amount = consensus::GRIN_BASE;
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), bh as usize, false);
let _ = test_framework::award_blocks_to_wallet(
&chain,
miner.clone(),
miner_mask,
bh as usize,
false,
);
// send some funds to wallets 1
send_to_dest!(miner.clone(), m_client.clone(), "wallet1", base_amount * 1)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet1", base_amount * 2)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet1", base_amount * 3)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet1",
base_amount * 1
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet1",
base_amount * 2
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet1",
base_amount * 3
)?;
bh += 3;
// 0) Check repair when all is okay should leave wallet contents alone
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.check_repair(true)?;
let info = wallet_info!(wallet1.clone())?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.check_repair(m, true)?;
let info = wallet_info!(wallet1.clone(), m)?;
assert_eq!(info.amount_currently_spendable, base_amount * 6);
assert_eq!(info.total, base_amount * 6);
Ok(())
})?;
// send some funds to wallet 2
send_to_dest!(miner.clone(), m_client.clone(), "wallet2", base_amount * 4)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet2", base_amount * 5)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet2", base_amount * 6)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet2",
base_amount * 4
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet2",
base_amount * 5
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet2",
base_amount * 6
)?;
bh += 3;
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm, false);
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), miner_mask, cm, false);
bh += cm as u64;
// confirm balances
let info = wallet_info!(wallet1.clone())?;
let info = wallet_info!(wallet1.clone(), mask1)?;
assert_eq!(info.amount_currently_spendable, base_amount * 6);
assert_eq!(info.total, base_amount * 6);
let info = wallet_info!(wallet2.clone())?;
let info = wallet_info!(wallet2.clone(), mask2)?;
assert_eq!(info.amount_currently_spendable, base_amount * 15);
assert_eq!(info.total, base_amount * 15);
@ -363,14 +452,14 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er
// seed + BIP32 path.
// 1) a full restore should recover all of them:
wallet::controller::owner_single_use(wallet3.clone(), |api| {
api.restore()?;
wallet::controller::owner_single_use(wallet3.clone(), mask3, |api, m| {
api.restore(m)?;
Ok(())
})?;
wallet::controller::owner_single_use(wallet3.clone(), |api| {
let info = wallet_info!(wallet3.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
wallet::controller::owner_single_use(wallet3.clone(), mask3, |api, m| {
let info = wallet_info!(wallet3.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 6);
assert_eq!(info.amount_currently_spendable, base_amount * 21);
assert_eq!(info.total, base_amount * 21);
@ -378,14 +467,14 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er
})?;
// 2) check_repair should recover them into a single wallet
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.check_repair(true)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.check_repair(m, true)?;
Ok(())
})?;
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let info = wallet_info!(wallet1.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let info = wallet_info!(wallet1.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 6);
assert_eq!(info.amount_currently_spendable, base_amount * 21);
Ok(())
@ -393,30 +482,48 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er
// 3) If I recover from seed and start using the wallet without restoring,
// check_repair should restore the older outputs
send_to_dest!(miner.clone(), m_client.clone(), "wallet4", base_amount * 7)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet4", base_amount * 8)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet4", base_amount * 9)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet4",
base_amount * 7
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet4",
base_amount * 8
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet4",
base_amount * 9
)?;
bh += 3;
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm, false);
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), miner_mask, cm, false);
bh += cm as u64;
wallet::controller::owner_single_use(wallet4.clone(), |api| {
let info = wallet_info!(wallet4.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
wallet::controller::owner_single_use(wallet4.clone(), mask4, |api, m| {
let info = wallet_info!(wallet4.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 3);
assert_eq!(info.amount_currently_spendable, base_amount * 24);
Ok(())
})?;
wallet::controller::owner_single_use(wallet5.clone(), |api| {
api.restore()?;
wallet::controller::owner_single_use(wallet5.clone(), mask5, |api, m| {
api.restore(m)?;
Ok(())
})?;
wallet::controller::owner_single_use(wallet5.clone(), |api| {
let info = wallet_info!(wallet5.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
wallet::controller::owner_single_use(wallet5.clone(), mask5, |api, m| {
let info = wallet_info!(wallet5.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 9);
assert_eq!(info.amount_currently_spendable, base_amount * (45));
Ok(())
@ -424,30 +531,54 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er
// 4) If I recover from seed and start using the wallet without restoring,
// check_repair should restore the older outputs
send_to_dest!(miner.clone(), m_client.clone(), "wallet6", base_amount * 10)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet6", base_amount * 11)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet6", base_amount * 12)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet6",
base_amount * 10
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet6",
base_amount * 11
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet6",
base_amount * 12
)?;
bh += 3;
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm as usize, false);
let _ = test_framework::award_blocks_to_wallet(
&chain,
miner.clone(),
miner_mask,
cm as usize,
false,
);
bh += cm as u64;
wallet::controller::owner_single_use(wallet6.clone(), |api| {
let info = wallet_info!(wallet6.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
wallet::controller::owner_single_use(wallet6.clone(), mask6, |api, m| {
let info = wallet_info!(wallet6.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 3);
assert_eq!(info.amount_currently_spendable, base_amount * 33);
Ok(())
})?;
wallet::controller::owner_single_use(wallet6.clone(), |api| {
api.check_repair(true)?;
wallet::controller::owner_single_use(wallet6.clone(), mask6, |api, m| {
api.check_repair(m, true)?;
Ok(())
})?;
wallet::controller::owner_single_use(wallet6.clone(), |api| {
let info = wallet_info!(wallet6.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
wallet::controller::owner_single_use(wallet6.clone(), mask6, |api, m| {
let info = wallet_info!(wallet6.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 12);
assert_eq!(info.amount_currently_spendable, base_amount * (78));
Ok(())
@ -456,49 +587,85 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er
// 5) Start using same seed with a different account, amounts should
// be distinct and restore should return funds from other account
send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 13)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 14)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 15)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet7",
base_amount * 13
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet7",
base_amount * 14
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet7",
base_amount * 15
)?;
bh += 3;
// mix it up a bit
wallet::controller::owner_single_use(wallet7.clone(), |api| {
api.create_account_path("account_1")?;
api.set_active_account("account_1")?;
wallet::controller::owner_single_use(wallet7.clone(), mask7, |api, m| {
api.create_account_path(m, "account_1")?;
api.set_active_account(m, "account_1")?;
Ok(())
})?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 1)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 2)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 3)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet7",
base_amount * 1
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet7",
base_amount * 2
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet7",
base_amount * 3
)?;
bh += 3;
// check balances
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm, false);
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), miner_mask, cm, false);
bh += cm as u64;
wallet::controller::owner_single_use(wallet7.clone(), |api| {
let info = wallet_info!(wallet7.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
wallet::controller::owner_single_use(wallet7.clone(), mask7, |api, m| {
let info = wallet_info!(wallet7.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 3);
assert_eq!(info.amount_currently_spendable, base_amount * 6);
api.set_active_account("default")?;
let info = wallet_info!(wallet7.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
api.set_active_account(m, "default")?;
let info = wallet_info!(wallet7.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 3);
assert_eq!(info.amount_currently_spendable, base_amount * 42);
Ok(())
})?;
wallet::controller::owner_single_use(wallet8.clone(), |api| {
api.restore()?;
let info = wallet_info!(wallet8.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
wallet::controller::owner_single_use(wallet8.clone(), mask8, |api, m| {
api.restore(m)?;
let info = wallet_info!(wallet8.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 15);
assert_eq!(info.amount_currently_spendable, base_amount * 120);
api.set_active_account("account_1")?;
let info = wallet_info!(wallet8.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
api.set_active_account(m, "account_1")?;
let info = wallet_info!(wallet8.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 3);
assert_eq!(info.amount_currently_spendable, base_amount * 6);
Ok(())
@ -508,51 +675,69 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er
// ids on account 2 as well, check_repair should get all outputs created
// to now into 2 accounts
wallet::controller::owner_single_use(wallet9.clone(), |api| {
api.create_account_path("account_1")?;
api.set_active_account("account_1")?;
wallet::controller::owner_single_use(wallet9.clone(), mask9, |api, m| {
api.create_account_path(m, "account_1")?;
api.set_active_account(m, "account_1")?;
Ok(())
})?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet9", base_amount * 4)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet9", base_amount * 5)?;
send_to_dest!(miner.clone(), m_client.clone(), "wallet9", base_amount * 6)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet9",
base_amount * 4
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet9",
base_amount * 5
)?;
send_to_dest!(
miner.clone(),
miner_mask,
m_client.clone(),
"wallet9",
base_amount * 6
)?;
bh += 3;
let _bh = bh;
wallet::controller::owner_single_use(wallet9.clone(), |api| {
let info = wallet_info!(wallet9.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
wallet::controller::owner_single_use(wallet9.clone(), mask9, |api, m| {
let info = wallet_info!(wallet9.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 3);
assert_eq!(info.amount_currently_spendable, base_amount * 15);
api.check_repair(true)?;
let info = wallet_info!(wallet9.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
api.check_repair(m, true)?;
let info = wallet_info!(wallet9.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 6);
assert_eq!(info.amount_currently_spendable, base_amount * 21);
api.set_active_account("default")?;
let info = wallet_info!(wallet9.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
api.set_active_account(m, "default")?;
let info = wallet_info!(wallet9.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 15);
assert_eq!(info.amount_currently_spendable, base_amount * 120);
Ok(())
})?;
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm, false);
let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), miner_mask, cm, false);
// 7) Ensure check_repair creates missing accounts
wallet::controller::owner_single_use(wallet10.clone(), |api| {
api.check_repair(true)?;
api.set_active_account("account_1")?;
let info = wallet_info!(wallet10.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
wallet::controller::owner_single_use(wallet10.clone(), mask10, |api, m| {
api.check_repair(m, true)?;
api.set_active_account(m, "account_1")?;
let info = wallet_info!(wallet10.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 6);
assert_eq!(info.amount_currently_spendable, base_amount * 21);
api.set_active_account("default")?;
let info = wallet_info!(wallet10.clone())?;
let outputs = api.retrieve_outputs(true, false, None)?.1;
api.set_active_account(m, "default")?;
let info = wallet_info!(wallet10.clone(), m)?;
let outputs = api.retrieve_outputs(m, true, false, None)?.1;
assert_eq!(outputs.len(), 15);
assert_eq!(info.amount_currently_spendable, base_amount * 120);
Ok(())

View file

@ -28,6 +28,7 @@ use impls::test_framework::{LocalWalletClient, WalletProxy};
use impls::{DefaultLCProvider, DefaultWalletImpl};
use std::fs;
use std::sync::Arc;
use util::secp::key::SecretKey;
use util::{Mutex, ZeroingString};
#[macro_export]
@ -41,20 +42,36 @@ macro_rules! wallet_inst {
#[macro_export]
macro_rules! create_wallet_and_add {
($client:ident, $wallet: ident, $test_dir: expr, $name: expr, $seed_phrase: expr, $proxy: expr) => {
($client:ident, $wallet: ident, $mask: ident, $test_dir: expr, $name: expr, $seed_phrase: expr, $proxy: expr, $create_mask: expr) => {
let $client = LocalWalletClient::new($name, $proxy.tx.clone());
let $wallet =
common::create_local_wallet($test_dir, $name, $seed_phrase.clone(), $client.clone());
$proxy.add_wallet($name, $client.get_send_instance(), $wallet.clone());
let ($wallet, $mask) = common::create_local_wallet(
$test_dir,
$name,
$seed_phrase.clone(),
$client.clone(),
$create_mask,
);
$proxy.add_wallet(
$name,
$client.get_send_instance(),
$wallet.clone(),
$mask.clone(),
);
};
}
#[macro_export]
macro_rules! open_wallet_and_add {
($client:ident, $wallet: ident, $test_dir: expr, $name: expr, $proxy: expr) => {
($client:ident, $wallet: ident, $mask: ident, $test_dir: expr, $name: expr, $proxy: expr, $create_mask: expr) => {
let $client = LocalWalletClient::new($name, $proxy.tx.clone());
let $wallet = common::open_local_wallet($test_dir, $name, $client.clone());
$proxy.add_wallet($name, $client.get_send_instance(), $wallet.clone());
let ($wallet, $mask) =
common::open_local_wallet($test_dir, $name, $client.clone(), $create_mask);
$proxy.add_wallet(
$name,
$client.get_send_instance(),
$wallet.clone(),
$mask.clone(),
);
};
}
pub fn clean_output_dir(test_dir: &str) {
@ -79,18 +96,22 @@ pub fn create_local_wallet(
name: &str,
mnemonic: Option<ZeroingString>,
client: LocalWalletClient,
) -> Arc<
Mutex<
Box<
WalletInst<
'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
create_mask: bool,
) -> (
Arc<
Mutex<
Box<
WalletInst<
'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>,
>,
>,
> {
Option<SecretKey>,
) {
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
as Box<
WalletInst<
@ -103,26 +124,32 @@ pub fn create_local_wallet(
lc.set_wallet_directory(&format!("{}/{}", test_dir, name));
lc.create_wallet(None, mnemonic, 32, ZeroingString::from(""))
.unwrap();
lc.open_wallet(None, ZeroingString::from("")).unwrap();
Arc::new(Mutex::new(wallet))
let mask = lc
.open_wallet(None, ZeroingString::from(""), create_mask, false)
.unwrap();
(Arc::new(Mutex::new(wallet)), mask)
}
pub fn open_local_wallet(
test_dir: &str,
name: &str,
client: LocalWalletClient,
) -> Arc<
Mutex<
Box<
WalletInst<
'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
create_mask: bool,
) -> (
Arc<
Mutex<
Box<
WalletInst<
'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>,
>,
>,
> {
Option<SecretKey>,
) {
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
as Box<
WalletInst<
@ -133,6 +160,8 @@ pub fn open_local_wallet(
>;
let lc = wallet.lc_provider().unwrap();
lc.set_wallet_directory(&format!("{}/{}", test_dir, name));
lc.open_wallet(None, ZeroingString::from("")).unwrap();
Arc::new(Mutex::new(wallet))
let mask = lc
.open_wallet(None, ZeroingString::from(""), create_mask, false)
.unwrap();
(Arc::new(Mutex::new(wallet)), mask)
}

View file

@ -46,19 +46,25 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
create_wallet_and_add!(
client1,
wallet1,
mask1_i,
test_dir,
"wallet1",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask1 = (&mask1_i).as_ref();
create_wallet_and_add!(
client2,
wallet2,
mask2_i,
test_dir,
"wallet2",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask2 = (&mask2_i).as_ref();
// Set the wallet proxy listener running
thread::spawn(move || {
@ -71,16 +77,16 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
let reward = core::consensus::REWARD;
// add some accounts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.create_account_path("mining")?;
api.create_account_path("listener")?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.create_account_path(m, "mining")?;
api.create_account_path(m, "listener")?;
Ok(())
})?;
// add some accounts
wallet::controller::owner_single_use(wallet2.clone(), |api| {
api.create_account_path("account1")?;
api.create_account_path("account2")?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
api.create_account_path(m, "account1")?;
api.create_account_path(m, "account2")?;
Ok(())
})?;
@ -90,7 +96,8 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
w.set_parent_key_id_by_name("mining")?;
}
let mut bh = 10u64;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), bh as usize, false);
let _ =
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
let send_file = format!("{}/part_tx_1.tx", test_dir);
let receive_file = format!("{}/part_tx_2.tx", test_dir);
@ -99,8 +106,8 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
let message = "sender test message, sender test message";
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward);
@ -115,10 +122,10 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
message: Some(message.to_owned()),
..Default::default()
};
let mut slate = api.init_send_tx(args)?;
let mut slate = api.init_send_tx(m, args)?;
// output tx file
PathToSlate((&send_file).into()).put_tx(&mut slate)?;
api.tx_lock_outputs(&slate, 0)?;
api.tx_lock_outputs(m, &slate, 0)?;
Ok(())
})?;
@ -133,37 +140,37 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
naughty_slate.participant_data[0].message = Some("I changed the message".to_owned());
// verify messages on slate match
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.verify_slate_messages(&slate)?;
assert!(api.verify_slate_messages(&naughty_slate).is_err());
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.verify_slate_messages(m, &slate)?;
assert!(api.verify_slate_messages(m, &naughty_slate).is_err());
Ok(())
})?;
let sender2_message = "And this is sender 2's message".to_owned();
// wallet 2 receives file, completes, sends file back
wallet::controller::foreign_single_use(wallet2.clone(), |api| {
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
slate = api.receive_tx(&slate, None, Some(sender2_message.clone()))?;
PathToSlate((&receive_file).into()).put_tx(&slate)?;
Ok(())
})?;
// wallet 1 finalises and posts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let mut slate = PathToSlate(receive_file.into()).get_tx()?;
api.verify_slate_messages(&slate)?;
slate = api.finalize_tx(&slate)?;
api.post_tx(&slate.tx, false)?;
api.verify_slate_messages(m, &slate)?;
slate = api.finalize_tx(m, &slate)?;
api.post_tx(m, &slate.tx, false)?;
bh += 1;
Ok(())
})?;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
bh += 3;
// Check total in mining account
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward - reward * 2);
@ -171,8 +178,8 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
})?;
// Check total in 'wallet 2' account
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet2_refreshed);
assert_eq!(wallet2_info.last_confirmed_height, bh);
assert_eq!(wallet2_info.total, 2 * reward);
@ -180,8 +187,8 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
})?;
// Check messages, all participants should have both
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (_, tx) = api.retrieve_txs(true, None, Some(slate.id))?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (_, tx) = api.retrieve_txs(m, true, None, Some(slate.id))?;
assert_eq!(
tx[0].clone().messages.unwrap().messages[0].message,
Some(message.to_owned())
@ -196,8 +203,8 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
Ok(())
})?;
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let (_, tx) = api.retrieve_txs(true, None, Some(slate.id))?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let (_, tx) = api.retrieve_txs(m, true, None, Some(slate.id))?;
assert_eq!(
tx[0].clone().messages.unwrap().messages[0].message,
Some(message.to_owned())

View file

@ -41,19 +41,25 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
create_wallet_and_add!(
client1,
wallet1,
mask1_i,
test_dir,
"wallet1",
None,
&mut wallet_proxy
&mut wallet_proxy,
true
);
let mask1 = (&mask1_i).as_ref();
create_wallet_and_add!(
client2,
wallet2,
mask2_i,
test_dir,
"wallet2",
None,
&mut wallet_proxy
&mut wallet_proxy,
true
);
let mask2 = (&mask2_i).as_ref();
// Set the wallet proxy listener running
thread::spawn(move || {
@ -66,9 +72,9 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
let reward = core::consensus::REWARD;
// add some accounts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.create_account_path("mining")?;
api.create_account_path("listener")?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.create_account_path(m, "mining")?;
api.create_account_path(m, "listener")?;
Ok(())
})?;
@ -78,11 +84,17 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
w.set_parent_key_id_by_name("mining")?;
}
let mut bh = 10u64;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), bh as usize, false);
let _ = test_framework::award_blocks_to_wallet(
&chain,
wallet1.clone(),
mask1,
bh as usize,
false,
);
// Sanity check wallet 1 contents
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward);
@ -91,17 +103,17 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
let mut slate = Slate::blank(2);
wallet::controller::owner_single_use(wallet2.clone(), |api| {
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
// Wallet 2 inititates an invoice transaction, requesting payment
let args = IssueInvoiceTxArgs {
amount: reward * 2,
..Default::default()
};
slate = api.issue_invoice_tx(args)?;
slate = api.issue_invoice_tx(m, args)?;
Ok(())
})?;
wallet::controller::owner_single_use(wallet1.clone(), |api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
// Wallet 1 receives the invoice transaction
let args = InitTxArgs {
src_acct_name: None,
@ -112,32 +124,32 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
selection_strategy_is_use_all: true,
..Default::default()
};
slate = api.process_invoice_tx(&slate, args)?;
api.tx_lock_outputs(&slate, 0)?;
slate = api.process_invoice_tx(m, &slate, args)?;
api.tx_lock_outputs(m, &slate, 0)?;
Ok(())
})?;
// wallet 2 finalizes and posts
wallet::controller::foreign_single_use(wallet2.clone(), |api| {
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
// Wallet 2 receives the invoice transaction
slate = api.finalize_invoice_tx(&slate)?;
Ok(())
})?;
// wallet 1 posts so wallet 2 doesn't get the mined amount
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.post_tx(&slate.tx, false)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.post_tx(m, &slate.tx, false)?;
Ok(())
})?;
bh += 1;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
bh += 3;
// Check transaction log for wallet 2
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let (_, wallet2_info) = api.retrieve_summary_info(true, 1)?;
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let (_, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
assert!(refreshed);
assert!(txs.len() == 1);
println!(
@ -151,9 +163,9 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
// Check transaction log for wallet 1, ensure only 1 entry
// exists
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (_, wallet1_info) = api.retrieve_summary_info(true, 1)?;
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
assert!(refreshed);
assert_eq!(txs.len() as u64, bh + 1);
println!(
@ -164,13 +176,13 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
})?;
// Test self-sending
wallet::controller::owner_single_use(wallet1.clone(), |api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
// Wallet 1 inititates an invoice transaction, requesting payment
let args = IssueInvoiceTxArgs {
amount: reward * 2,
..Default::default()
};
slate = api.issue_invoice_tx(args)?;
slate = api.issue_invoice_tx(m, args)?;
// Wallet 1 receives the invoice transaction
let args = InitTxArgs {
src_acct_name: None,
@ -181,19 +193,19 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
selection_strategy_is_use_all: true,
..Default::default()
};
slate = api.process_invoice_tx(&slate, args)?;
api.tx_lock_outputs(&slate, 0)?;
slate = api.process_invoice_tx(m, &slate, args)?;
api.tx_lock_outputs(m, &slate, 0)?;
Ok(())
})?;
// wallet 1 finalizes and posts
wallet::controller::foreign_single_use(wallet1.clone(), |api| {
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
// Wallet 2 receives the invoice transaction
slate = api.finalize_invoice_tx(&slate)?;
Ok(())
})?;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
//bh += 3;
// let logging finish

View file

@ -43,19 +43,25 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
create_wallet_and_add!(
client1,
wallet1,
mask1_i,
test_dir,
"wallet1",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask1 = (&mask1_i).as_ref();
create_wallet_and_add!(
client2,
wallet2,
mask2_i,
test_dir,
"wallet2",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask2 = (&mask2_i).as_ref();
// Set the wallet proxy listener running
thread::spawn(move || {
@ -68,16 +74,16 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
let reward = core::consensus::REWARD;
// add some accounts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.create_account_path("mining")?;
api.create_account_path("listener")?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.create_account_path(m, "mining")?;
api.create_account_path(m, "listener")?;
Ok(())
})?;
// add some accounts
wallet::controller::owner_single_use(wallet2.clone(), |api| {
api.create_account_path("account1")?;
api.create_account_path("account2")?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
api.create_account_path(m, "account1")?;
api.create_account_path(m, "account2")?;
Ok(())
})?;
@ -87,7 +93,8 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
w.set_parent_key_id_by_name("mining")?;
}
let mut bh = 10u64;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), bh as usize, false);
let _ =
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
let send_file = format!("{}/part_tx_1.tx", test_dir);
let receive_file = format!("{}/part_tx_2.tx", test_dir);
@ -95,8 +102,8 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
let mut slate = Slate::blank(2);
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward);
@ -110,13 +117,13 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
selection_strategy_is_use_all: true,
..Default::default()
};
let slate = api.init_send_tx(args)?;
let slate = api.init_send_tx(m, args)?;
PathToSlate((&send_file).into()).put_tx(&slate)?;
api.tx_lock_outputs(&slate, 0)?;
api.tx_lock_outputs(m, &slate, 0)?;
Ok(())
})?;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
bh += 3;
// wallet 1 receives file to different account, completes
@ -125,7 +132,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
w.set_parent_key_id_by_name("listener")?;
}
wallet::controller::foreign_single_use(wallet1.clone(), |api| {
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
slate = PathToSlate((&send_file).into()).get_tx()?;
slate = api.receive_tx(&slate, None, None)?;
PathToSlate((&receive_file).into()).put_tx(&slate)?;
@ -139,27 +146,27 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
}
// wallet 1 finalize
wallet::controller::owner_single_use(wallet1.clone(), |api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
slate = PathToSlate((&receive_file).into()).get_tx()?;
slate = api.finalize_tx(&slate)?;
slate = api.finalize_tx(m, &slate)?;
Ok(())
})?;
// Now repost from cached
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (_, txs) = api.retrieve_txs(true, None, Some(slate.id))?;
let stored_tx = api.get_stored_tx(&txs[0])?;
api.post_tx(&stored_tx.unwrap(), false)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (_, txs) = api.retrieve_txs(m, true, None, Some(slate.id))?;
let stored_tx = api.get_stored_tx(m, &txs[0])?;
api.post_tx(m, &stored_tx.unwrap(), false)?;
bh += 1;
Ok(())
})?;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
bh += 3;
// update/test contents of both accounts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward - reward * 2);
@ -171,8 +178,8 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
w.set_parent_key_id_by_name("listener")?;
}
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet2_refreshed);
assert_eq!(wallet2_info.last_confirmed_height, bh);
assert_eq!(wallet2_info.total, 2 * reward);
@ -192,7 +199,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
let mut slate = Slate::blank(2);
let amount = 60_000_000_000;
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| {
// note this will increment the block count as part of the transaction "Posting"
let args = InitTxArgs {
src_acct_name: None,
@ -203,39 +210,39 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.init_send_tx(args)?;
let slate_i = sender_api.init_send_tx(m, args)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, 0)?;
slate = sender_api.finalize_tx(&mut slate)?;
sender_api.tx_lock_outputs(m, &slate, 0)?;
slate = sender_api.finalize_tx(m, &mut slate)?;
Ok(())
})?;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
bh += 3;
// Now repost from cached
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (_, txs) = api.retrieve_txs(true, None, Some(slate.id))?;
let stored_tx = api.get_stored_tx(&txs[0])?;
api.post_tx(&stored_tx.unwrap(), false)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (_, txs) = api.retrieve_txs(m, true, None, Some(slate.id))?;
let stored_tx = api.get_stored_tx(m, &txs[0])?;
api.post_tx(m, &stored_tx.unwrap(), false)?;
bh += 1;
Ok(())
})?;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
bh += 3;
//
// update/test contents of both accounts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward - reward * 4);
Ok(())
})?;
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet2_refreshed);
assert_eq!(wallet2_info.last_confirmed_height, bh);
assert_eq!(wallet2_info.total, 2 * amount);

View file

@ -41,10 +41,12 @@ fn restore_wallet(base_dir: &'static str, wallet_dir: &str) -> Result<(), libwal
create_wallet_and_add!(
client,
wallet,
mask,
base_dir,
&dest_wallet_name,
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
// close created wallet
let mut w_lock = wallet.lock();
@ -59,10 +61,13 @@ fn restore_wallet(base_dir: &'static str, wallet_dir: &str) -> Result<(), libwal
open_wallet_and_add!(
client,
wallet,
mask_i,
&base_dir,
&dest_wallet_name,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask = (&mask_i).as_ref();
// Set the wallet proxy listener running
let wp_running = wallet_proxy.running.clone();
@ -73,9 +78,9 @@ fn restore_wallet(base_dir: &'static str, wallet_dir: &str) -> Result<(), libwal
});
// perform the restore and update wallet info
wallet::controller::owner_single_use(wallet.clone(), |api| {
let _ = api.restore()?;
let _ = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet.clone(), mask, |api, m| {
let _ = api.restore(m)?;
let _ = api.retrieve_summary_info(m, true, 1)?;
Ok(())
})?;
@ -96,17 +101,23 @@ fn compare_wallet_restore(
open_wallet_and_add!(
client,
wallet_source,
source_mask_i,
&base_dir,
&wallet_dir,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let source_mask = (&source_mask_i).as_ref();
open_wallet_and_add!(
client,
wallet_dest,
dest_mask_i,
&base_dir,
&restore_name,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let dest_mask = (&dest_mask_i).as_ref();
{
wallet_inst!(wallet_source, w);
@ -136,17 +147,17 @@ fn compare_wallet_restore(
let mut dest_accts: Option<Vec<AcctPathMapping>> = None;
// Overall wallet info should be the same
wallet::controller::owner_single_use(wallet_source.clone(), |api| {
src_info = Some(api.retrieve_summary_info(true, 1)?.1);
src_txs = Some(api.retrieve_txs(true, None, None)?.1);
src_accts = Some(api.accounts()?);
wallet::controller::owner_single_use(wallet_source.clone(), source_mask, |api, m| {
src_info = Some(api.retrieve_summary_info(m, true, 1)?.1);
src_txs = Some(api.retrieve_txs(m, true, None, None)?.1);
src_accts = Some(api.accounts(m)?);
Ok(())
})?;
wallet::controller::owner_single_use(wallet_dest.clone(), |api| {
dest_info = Some(api.retrieve_summary_info(true, 1)?.1);
dest_txs = Some(api.retrieve_txs(true, None, None)?.1);
dest_accts = Some(api.accounts()?);
wallet::controller::owner_single_use(wallet_dest.clone(), dest_mask, |api, m| {
dest_info = Some(api.retrieve_summary_info(m, true, 1)?.1);
dest_txs = Some(api.retrieve_txs(m, true, None, None)?.1);
dest_accts = Some(api.accounts(m)?);
Ok(())
})?;
@ -194,24 +205,30 @@ fn setup_restore(test_dir: &'static str) -> Result<(), libwallet::Error> {
create_wallet_and_add!(
client1,
wallet1,
mask1_i,
test_dir,
"wallet1",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask1 = (&mask1_i).as_ref();
create_wallet_and_add!(
client2,
wallet2,
mask2_i,
test_dir,
"wallet2",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask2 = (&mask2_i).as_ref();
// wallet 2 will use another account
wallet::controller::owner_single_use(wallet2.clone(), |api| {
api.create_account_path("account1")?;
api.create_account_path("account2")?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
api.create_account_path(m, "account1")?;
api.create_account_path(m, "account2")?;
Ok(())
})?;
@ -225,11 +242,14 @@ fn setup_restore(test_dir: &'static str) -> Result<(), libwallet::Error> {
create_wallet_and_add!(
client3,
wallet3,
mask3_i,
test_dir,
"wallet3",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask3 = (&mask3_i).as_ref();
// Set the wallet proxy listener running
let wp_running = wallet_proxy.running.clone();
@ -240,13 +260,13 @@ fn setup_restore(test_dir: &'static str) -> Result<(), libwallet::Error> {
});
// mine a few blocks
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 10, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 10, false);
// assert wallet contents
// and a single use api for a send command
let amount = 60_000_000_000;
let mut slate = Slate::blank(1);
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| {
// note this will increment the block count as part of the transaction "Posting"
let args = InitTxArgs {
src_acct_name: None,
@ -257,19 +277,19 @@ fn setup_restore(test_dir: &'static str) -> Result<(), libwallet::Error> {
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.init_send_tx(args)?;
let slate_i = sender_api.init_send_tx(m, args)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, 0)?;
slate = sender_api.finalize_tx(&slate)?;
sender_api.post_tx(&slate.tx, false)?;
sender_api.tx_lock_outputs(m, &slate, 0)?;
slate = sender_api.finalize_tx(m, &slate)?;
sender_api.post_tx(m, &slate.tx, false)?;
Ok(())
})?;
// mine a few more blocks
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
// Send some to wallet 3
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| {
// note this will increment the block count as part of the transaction "Posting"
let args = InitTxArgs {
src_acct_name: None,
@ -280,19 +300,19 @@ fn setup_restore(test_dir: &'static str) -> Result<(), libwallet::Error> {
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.init_send_tx(args)?;
let slate_i = sender_api.init_send_tx(m, args)?;
slate = client1.send_tx_slate_direct("wallet3", &slate_i)?;
sender_api.tx_lock_outputs(&slate, 0)?;
slate = sender_api.finalize_tx(&slate)?;
sender_api.post_tx(&slate.tx, false)?;
sender_api.tx_lock_outputs(m, &slate, 0)?;
slate = sender_api.finalize_tx(m, &slate)?;
sender_api.post_tx(m, &slate.tx, false)?;
Ok(())
})?;
// mine a few more blocks
let _ = test_framework::award_blocks_to_wallet(&chain, wallet3.clone(), 10, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet3.clone(), mask3, 10, false);
// Wallet3 to wallet 2
wallet::controller::owner_single_use(wallet3.clone(), |sender_api| {
wallet::controller::owner_single_use(wallet3.clone(), mask3, |sender_api, m| {
// note this will increment the block count as part of the transaction "Posting"
let args = InitTxArgs {
src_acct_name: None,
@ -303,11 +323,11 @@ fn setup_restore(test_dir: &'static str) -> Result<(), libwallet::Error> {
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.init_send_tx(args)?;
let slate_i = sender_api.init_send_tx(m, args)?;
slate = client3.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, 0)?;
slate = sender_api.finalize_tx(&slate)?;
sender_api.post_tx(&slate.tx, false)?;
sender_api.tx_lock_outputs(m, &slate, 0)?;
slate = sender_api.finalize_tx(m, &slate)?;
sender_api.post_tx(m, &slate.tx, false)?;
Ok(())
})?;
@ -318,10 +338,10 @@ fn setup_restore(test_dir: &'static str) -> Result<(), libwallet::Error> {
}
// mine a few more blocks
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 2, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 2, false);
// Wallet3 to wallet 2 again (to another account)
wallet::controller::owner_single_use(wallet3.clone(), |sender_api| {
wallet::controller::owner_single_use(wallet3.clone(), mask3, |sender_api, m| {
// note this will increment the block count as part of the transaction "Posting"
let args = InitTxArgs {
src_acct_name: None,
@ -332,28 +352,28 @@ fn setup_restore(test_dir: &'static str) -> Result<(), libwallet::Error> {
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.init_send_tx(args)?;
let slate_i = sender_api.init_send_tx(m, args)?;
slate = client3.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, 0)?;
slate = sender_api.finalize_tx(&slate)?;
sender_api.post_tx(&slate.tx, false)?;
sender_api.tx_lock_outputs(m, &slate, 0)?;
slate = sender_api.finalize_tx(m, &slate)?;
sender_api.post_tx(m, &slate.tx, false)?;
Ok(())
})?;
// mine a few more blocks
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 5, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 5, false);
// update everyone
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let _ = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let _ = api.retrieve_summary_info(m, true, 1)?;
Ok(())
})?;
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let _ = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let _ = api.retrieve_summary_info(m, true, 1)?;
Ok(())
})?;
wallet::controller::owner_single_use(wallet3.clone(), |api| {
let _ = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet3.clone(), mask3, |api, m| {
let _ = api.retrieve_summary_info(m, true, 1)?;
Ok(())
})?;

View file

@ -41,11 +41,14 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
create_wallet_and_add!(
client1,
wallet1,
mask1_i,
test_dir,
"wallet1",
None,
&mut wallet_proxy
&mut wallet_proxy,
true
);
let mask1 = (&mask1_i).as_ref();
// Set the wallet proxy listener running
thread::spawn(move || {
@ -58,9 +61,9 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
let reward = core::consensus::REWARD;
// add some accounts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.create_account_path("mining")?;
api.create_account_path("listener")?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.create_account_path(m, "mining")?;
api.create_account_path(m, "listener")?;
Ok(())
})?;
@ -70,11 +73,12 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
w.set_parent_key_id_by_name("mining")?;
}
let mut bh = 10u64;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), bh as usize, false);
let _ =
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward);
@ -88,25 +92,25 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
selection_strategy_is_use_all: true,
..Default::default()
};
let mut slate = api.init_send_tx(args)?;
api.tx_lock_outputs(&slate, 0)?;
let mut slate = api.init_send_tx(m, args)?;
api.tx_lock_outputs(m, &slate, 0)?;
// Send directly to self
wallet::controller::foreign_single_use(wallet1.clone(), |api| {
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
slate = api.receive_tx(&slate, Some("listener"), None)?;
Ok(())
})?;
slate = api.finalize_tx(&slate)?;
api.post_tx(&slate.tx, false)?; // mines a block
slate = api.finalize_tx(m, &slate)?;
api.post_tx(m, &slate.tx, false)?; // mines a block
bh += 1;
Ok(())
})?;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
bh += 3;
// Check total in mining account
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward - reward * 2);
@ -118,8 +122,8 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("listener")?;
}
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, 2 * reward);

View file

@ -42,19 +42,27 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
create_wallet_and_add!(
client1,
wallet1,
mask1_i,
test_dir,
"wallet1",
None,
&mut wallet_proxy
&mut wallet_proxy,
true
);
let mask1 = (&mask1_i).as_ref();
println!("Mask1: {:?}", mask1);
create_wallet_and_add!(
client2,
wallet2,
mask2_i,
test_dir,
"wallet2",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask2 = (&mask2_i).as_ref();
println!("Mask2: {:?}", mask2);
// Set the wallet proxy listener running
thread::spawn(move || {
@ -67,11 +75,11 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
let reward = core::consensus::REWARD;
let cm = global::coinbase_maturity();
// mine a few blocks
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 10, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 10, false);
// Check wallet 1 contents are as expected
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
debug!(
"Wallet 1 Info Pre-Transaction, after {} blocks: {:?}",
wallet1_info.last_confirmed_height, wallet1_info
@ -89,7 +97,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
// and a single use api for a send command
let amount = 60_000_000_000;
let mut slate = Slate::blank(1);
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| {
// note this will increment the block count as part of the transaction "Posting"
let args = InitTxArgs {
src_acct_name: None,
@ -100,15 +108,15 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.init_send_tx(args)?;
let slate_i = sender_api.init_send_tx(m, args)?;
// Check we are creating a tx with the expected lock_height of 0.
// We will check this produces a Plain kernel later.
assert_eq!(0, slate.lock_height);
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, 0)?;
slate = sender_api.finalize_tx(&slate)?;
sender_api.tx_lock_outputs(m, &slate, 0)?;
slate = sender_api.finalize_tx(m, &slate)?;
// Check we have a single kernel and that it is a Plain kernel (no lock_height).
assert_eq!(slate.tx.kernels().len(), 1);
@ -125,9 +133,9 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
})?;
// Check transaction log for wallet 1
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (_, wallet1_info) = api.retrieve_summary_info(true, 1)?;
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
assert!(refreshed);
let fee = core::libtx::tx_fee(
wallet1_info.last_confirmed_height as usize - cm as usize,
@ -148,8 +156,8 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
})?;
// Check transaction log for wallet 2
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
assert!(refreshed);
// we should have a transaction entry for this slate
let tx = txs.iter().find(|t| t.tx_slate_id == Some(slate.id));
@ -164,14 +172,14 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
})?;
// post transaction
wallet::controller::owner_single_use(wallet1.clone(), |api| {
api.post_tx(&slate.tx, false)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.post_tx(m, &slate.tx, false)?;
Ok(())
})?;
// Check wallet 1 contents are as expected
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
debug!(
"Wallet 1 Info Post Transaction, after {} blocks: {:?}",
wallet1_info.last_confirmed_height, wallet1_info
@ -195,7 +203,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
assert_eq!(wallet1_info.amount_immature, cm * reward + fee);
// check tx log entry is confirmed
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
assert!(refreshed);
let tx = txs.iter().find(|t| t.tx_slate_id == Some(slate.id));
assert!(tx.is_some());
@ -207,11 +215,11 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
})?;
// mine a few more blocks
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
// refresh wallets and retrieve info/tests for each wallet after maturity
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
debug!("Wallet 1 Info: {:?}", wallet1_info);
assert!(wallet1_refreshed);
assert_eq!(
@ -225,13 +233,13 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
Ok(())
})?;
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet2_refreshed);
assert_eq!(wallet2_info.amount_currently_spendable, amount);
// check tx log entry is confirmed
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
assert!(refreshed);
let tx = txs.iter().find(|t| t.tx_slate_id == Some(slate.id));
assert!(tx.is_some());
@ -242,7 +250,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
})?;
// Estimate fee and locked amount for a transaction
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| {
let init_args = InitTxArgs {
src_acct_name: None,
amount: amount * 2,
@ -253,7 +261,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
estimate_only: Some(true),
..Default::default()
};
let est = sender_api.init_send_tx(init_args)?;
let est = sender_api.init_send_tx(m, init_args)?;
assert_eq!(est.amount, 600_000_000_000);
assert_eq!(est.fee, 4_000_000);
@ -267,7 +275,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
estimate_only: Some(true),
..Default::default()
};
let est = sender_api.init_send_tx(init_args)?;
let est = sender_api.init_send_tx(m, init_args)?;
assert_eq!(est.amount, 180_000_000_000);
assert_eq!(est.fee, 6_000_000);
@ -276,7 +284,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
// Send another transaction, but don't post to chain immediately and use
// the stored transaction instead
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| {
// note this will increment the block count as part of the transaction "Posting"
let args = InitTxArgs {
src_acct_name: None,
@ -287,25 +295,25 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = sender_api.init_send_tx(args)?;
let slate_i = sender_api.init_send_tx(m, args)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, 0)?;
slate = sender_api.finalize_tx(&slate)?;
sender_api.tx_lock_outputs(m, &slate, 0)?;
slate = sender_api.finalize_tx(m, &slate)?;
Ok(())
})?;
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
let (refreshed, _wallet1_info) = sender_api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| {
let (refreshed, _wallet1_info) = sender_api.retrieve_summary_info(m, true, 1)?;
assert!(refreshed);
let (_, txs) = sender_api.retrieve_txs(true, None, None)?;
let (_, txs) = sender_api.retrieve_txs(m, true, None, None)?;
// find the transaction
let tx = txs
.iter()
.find(|t| t.tx_slate_id == Some(slate.id))
.unwrap();
let stored_tx = sender_api.get_stored_tx(&tx)?;
sender_api.post_tx(&stored_tx.unwrap(), false)?;
let (_, wallet1_info) = sender_api.retrieve_summary_info(true, 1)?;
let stored_tx = sender_api.get_stored_tx(m, &tx)?;
sender_api.post_tx(m, &stored_tx.unwrap(), false)?;
let (_, wallet1_info) = sender_api.retrieve_summary_info(m, true, 1)?;
// should be mined now
assert_eq!(
wallet1_info.total,
@ -315,16 +323,16 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
})?;
// mine a few more blocks
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
// check wallet2 has stored transaction
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet2_refreshed);
assert_eq!(wallet2_info.amount_currently_spendable, amount * 3);
// check tx log entry is confirmed
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
assert!(refreshed);
let tx = txs.iter().find(|t| t.tx_slate_id == Some(slate.id));
assert!(tx.is_some());
@ -350,19 +358,25 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
create_wallet_and_add!(
client1,
wallet1,
mask1_i,
test_dir,
"wallet1",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask1 = (&mask1_i).as_ref();
create_wallet_and_add!(
client2,
wallet2,
mask2_i,
test_dir,
"wallet2",
None,
&mut wallet_proxy
&mut wallet_proxy,
false
);
let mask2 = (&mask2_i).as_ref();
// Set the wallet proxy listener running
thread::spawn(move || {
@ -375,11 +389,11 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
let reward = core::consensus::REWARD;
let cm = global::coinbase_maturity(); // assume all testing precedes soft fork height
// mine a few blocks
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 5, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 5, false);
let amount = 30_000_000_000;
let mut slate = Slate::blank(1);
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| {
// note this will increment the block count as part of the transaction "Posting"
let args = InitTxArgs {
src_acct_name: None,
@ -391,29 +405,29 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
..Default::default()
};
let slate_i = sender_api.init_send_tx(args)?;
let slate_i = sender_api.init_send_tx(m, args)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(&slate, 0)?;
slate = sender_api.finalize_tx(&slate)?;
sender_api.tx_lock_outputs(m, &slate, 0)?;
slate = sender_api.finalize_tx(m, &slate)?;
Ok(())
})?;
// Check transaction log for wallet 1
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let (refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
let (refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
println!(
"last confirmed height: {}",
wallet1_info.last_confirmed_height
);
assert!(refreshed);
let (_, txs) = api.retrieve_txs(true, None, None)?;
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
// we should have a transaction entry for this slate
let tx = txs.iter().find(|t| t.tx_slate_id == Some(slate.id));
assert!(tx.is_some());
let mut locked_count = 0;
let mut unconfirmed_count = 0;
// get the tx entry, check outputs are as expected
let (_, output_mappings) = api.retrieve_outputs(true, false, Some(tx.unwrap().id))?;
let (_, output_mappings) = api.retrieve_outputs(m, true, false, Some(tx.unwrap().id))?;
for m in output_mappings.clone() {
if m.output.status == OutputStatus::Locked {
locked_count = locked_count + 1;
@ -430,14 +444,14 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
})?;
// Check transaction log for wallet 2
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
assert!(refreshed);
let mut unconfirmed_count = 0;
let tx = txs.iter().find(|t| t.tx_slate_id == Some(slate.id));
assert!(tx.is_some());
// get the tx entry, check outputs are as expected
let (_, outputs) = api.retrieve_outputs(true, false, Some(tx.unwrap().id))?;
let (_, outputs) = api.retrieve_outputs(m, true, false, Some(tx.unwrap().id))?;
for m in outputs.clone() {
if m.output.status == OutputStatus::Unconfirmed {
unconfirmed_count = unconfirmed_count + 1;
@ -445,7 +459,7 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
}
assert_eq!(outputs.len(), 1);
assert_eq!(unconfirmed_count, 1);
let (refreshed, wallet2_info) = api.retrieve_summary_info(true, 1)?;
let (refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(refreshed);
assert_eq!(wallet2_info.amount_currently_spendable, 0,);
assert_eq!(wallet2_info.amount_awaiting_finalization, amount);
@ -454,20 +468,20 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
// wallet 1 is bold and doesn't ever post the transaction
// mine a few more blocks
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 5, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 5, false);
// Wallet 1 decides to roll back instead
wallet::controller::owner_single_use(wallet1.clone(), |api| {
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
// can't roll back coinbase
let res = api.cancel_tx(Some(1), None);
let res = api.cancel_tx(m, Some(1), None);
assert!(res.is_err());
let (_, txs) = api.retrieve_txs(true, None, None)?;
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
let tx = txs
.iter()
.find(|t| t.tx_slate_id == Some(slate.id))
.unwrap();
api.cancel_tx(Some(tx.id), None)?;
let (refreshed, wallet1_info) = api.retrieve_summary_info(true, 1)?;
api.cancel_tx(m, Some(tx.id), None)?;
let (refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(refreshed);
println!(
"last confirmed height: {}",
@ -480,27 +494,27 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
(wallet1_info.last_confirmed_height - cm) * reward
);
// can't roll back again
let res = api.cancel_tx(Some(tx.id), None);
let res = api.cancel_tx(m, Some(tx.id), None);
assert!(res.is_err());
Ok(())
})?;
// Wallet 2 rolls back
wallet::controller::owner_single_use(wallet2.clone(), |api| {
let (_, txs) = api.retrieve_txs(true, None, None)?;
wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
let tx = txs
.iter()
.find(|t| t.tx_slate_id == Some(slate.id))
.unwrap();
api.cancel_tx(Some(tx.id), None)?;
let (refreshed, wallet2_info) = api.retrieve_summary_info(true, 1)?;
api.cancel_tx(m, Some(tx.id), None)?;
let (refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(refreshed);
// check all eligible inputs should be now be spendable
assert_eq!(wallet2_info.amount_currently_spendable, 0,);
assert_eq!(wallet2_info.total, 0,);
// can't roll back again
let res = api.cancel_tx(Some(tx.id), None);
let res = api.cancel_tx(m, Some(tx.id), None);
assert!(res.is_err());
Ok(())

View file

@ -28,7 +28,7 @@ pub struct HttpSlateSender {
impl HttpSlateSender {
/// Create, return Err if scheme is not "http"
pub fn new(base_url: Url) -> Result<HttpSlateSender, SchemeNotHttp> {
if base_url.scheme() != "http" {
if base_url.scheme() != "http" && base_url.scheme() != "https" {
Err(SchemeNotHttp)
} else {
Ok(HttpSlateSender { base_url })

View file

@ -368,7 +368,7 @@ impl SlateReceiver for KeybaseAllChannels {
>;
let lc = wallet.lc_provider().unwrap();
lc.set_wallet_directory(&config.data_file_dir);
lc.open_wallet(None, passphrase)?;
let mask = lc.open_wallet(None, passphrase, true, false)?;
let wallet_inst = lc.wallet_inst()?;
wallet_inst.set_parent_key_id_by_name(account)?;
@ -411,8 +411,14 @@ impl SlateReceiver for KeybaseAllChannels {
return Err(e);
}
let res = {
let r =
foreign::receive_tx(&mut **wallet_inst, &slate, None, None, false);
let r = foreign::receive_tx(
&mut **wallet_inst,
Some(mask.as_ref().unwrap()),
&slate,
None,
None,
false,
);
r
};
match res {

View file

@ -24,7 +24,7 @@ use std::path::Path;
use failure::ResultExt;
use uuid::Uuid;
use crate::blake2::blake2b::Blake2b;
use crate::blake2::blake2b::{Blake2b, Blake2bResult};
use crate::keychain::{ChildNumber, ExtKeychain, Identifier, Keychain, SwitchCommitmentType};
use crate::store::{self, option_to_not_found, to_key, to_key_u64};
@ -36,8 +36,12 @@ use crate::libwallet::{
AcctPathMapping, Context, Error, ErrorKind, NodeClient, OutputData, TxLogEntry, WalletBackend,
WalletOutputBatch,
};
use crate::util;
use crate::util::secp::constants::SECRET_KEY_SIZE;
use crate::util::secp::key::SecretKey;
use crate::util::{self, secp};
use rand::rngs::mock::StepRng;
use rand::thread_rng;
pub const DB_DIR: &'static str = "db";
pub const TX_SAVE_DIR: &'static str = "saved_txs";
@ -99,6 +103,8 @@ where
data_file_dir: String,
/// Keychain
pub keychain: Option<K>,
/// Check value for XORed keychain seed
pub master_checksum: Box<Option<Blake2bResult>>,
/// Parent path to use by default for output operations
parent_key_id: Identifier,
/// wallet to node client
@ -144,6 +150,7 @@ where
db: store,
data_file_dir: data_file_dir.to_owned(),
keychain: None,
master_checksum: Box::new(None),
parent_key_id: LMDBBackend::<C, K>::default_path(),
w2n_client: n_client,
_phantom: &PhantomData,
@ -172,23 +179,68 @@ where
K: Keychain + 'ck,
{
/// Set the keychain, which should already have been opened
fn set_keychain(&mut self, k: Box<K>) {
fn set_keychain(
&mut self,
mut k: Box<K>,
mask: bool,
use_test_rng: bool,
) -> Result<Option<SecretKey>, Error> {
// store hash of master key, so it can be verified later after unmasking
let root_key = k.derive_key(0, &K::root_key_id(), &SwitchCommitmentType::Regular)?;
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
hasher.update(&root_key.0[..]);
self.master_checksum = Box::new(Some(hasher.finalize()));
let mask_value = {
match mask {
true => {
// Random value that must be XORed against the stored wallet seed
// before it is used
let mask_value = match use_test_rng {
true => {
let mut test_rng = StepRng::new(1234567890u64, 1);
secp::key::SecretKey::new(&k.secp(), &mut test_rng)
}
false => secp::key::SecretKey::new(&k.secp(), &mut thread_rng()),
};
k.mask_master_key(&mask_value)?;
Some(mask_value)
}
false => None,
}
};
self.keychain = Some(*k);
Ok(mask_value)
}
/// Close wallet
fn close(&mut self) -> Result<(), Error> {
//TODO: Ensure this is zeroed?
self.keychain = None;
Ok(())
}
/// Return the keychain being used
fn keychain(&mut self) -> Result<&mut K, Error> {
if self.keychain.is_some() {
Ok(self.keychain.as_mut().unwrap())
} else {
Err(ErrorKind::KeychainDoesntExist.into())
/// Return the keychain being used, cloned with XORed token value
/// for temporary use
fn keychain(&self, mask: Option<&SecretKey>) -> Result<K, Error> {
match self.keychain.as_ref() {
Some(k) => {
let mut k_masked = k.clone();
if let Some(m) = mask {
k_masked.mask_master_key(m)?;
}
// Check if master seed is what is expected (especially if it's been xored)
let root_key =
k_masked.derive_key(0, &K::root_key_id(), &SwitchCommitmentType::Regular)?;
let mut hasher = Blake2b::new(SECRET_KEY_SIZE);
hasher.update(&root_key.0[..]);
if *self.master_checksum != Some(hasher.finalize()) {
error!("Supplied keychain mask is invalid");
return Err(ErrorKind::InvalidKeychainMask.into());
}
Ok(k_masked)
}
None => Err(ErrorKind::KeychainDoesntExist.into()),
}
}
@ -200,6 +252,7 @@ where
/// return the version of the commit for caching
fn calc_commit_for_cache(
&mut self,
keychain_mask: Option<&SecretKey>,
amount: u64,
id: &Identifier,
) -> Result<Option<String>, Error> {
@ -209,7 +262,7 @@ where
Ok(None)
} else {*/
Ok(Some(util::to_hex(
self.keychain()?
self.keychain(keychain_mask)?
.commit(amount, &id, &SwitchCommitmentType::Regular)?
.0
.to_vec(), // TODO: proper support for different switch commitment schemes
@ -261,6 +314,7 @@ where
fn get_private_context(
&mut self,
keychain_mask: Option<&SecretKey>,
slate_id: &[u8],
participant_id: usize,
) -> Result<Context, Error> {
@ -269,7 +323,8 @@ where
&mut slate_id.to_vec(),
participant_id as u64,
);
let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain()?, slate_id)?;
let (blind_xor_key, nonce_xor_key) =
private_ctx_xor_keys(&self.keychain(keychain_mask)?, slate_id)?;
let mut ctx: Context = option_to_not_found(
self.db.get_ser(&ctx_key),
@ -305,7 +360,7 @@ where
.join(filename);
let path_buf = Path::new(&path).to_path_buf();
let mut stored_tx = File::create(path_buf)?;
let tx_hex = util::to_hex(ser::ser_vec(tx).unwrap());;
let tx_hex = util::to_hex(ser::ser_vec(tx, ser::ProtocolVersion::local()).unwrap());;
stored_tx.write_all(&tx_hex.as_bytes())?;
stored_tx.sync_all()?;
Ok(())
@ -325,19 +380,23 @@ where
tx_f.read_to_string(&mut content)?;
let tx_bin = util::from_hex(content).unwrap();
Ok(Some(
ser::deserialize::<Transaction>(&mut &tx_bin[..]).unwrap(),
ser::deserialize::<Transaction>(&mut &tx_bin[..], ser::ProtocolVersion::local())
.unwrap(),
))
}
fn batch<'a>(&'a mut self) -> Result<Box<dyn WalletOutputBatch<K> + 'a>, Error> {
fn batch<'a>(
&'a mut self,
keychain_mask: Option<&SecretKey>,
) -> Result<Box<dyn WalletOutputBatch<K> + 'a>, Error> {
Ok(Box::new(Batch {
_store: self,
db: RefCell::new(Some(self.db.batch()?)),
keychain: self.keychain.clone(),
keychain: Some(self.keychain(keychain_mask)?),
}))
}
fn next_child<'a>(&mut self) -> Result<Identifier, Error> {
fn next_child<'a>(&mut self, keychain_mask: Option<&SecretKey>) -> Result<Identifier, Error> {
let parent_key_id = self.parent_key_id.clone();
let mut deriv_idx = {
let batch = self.db.batch()?;
@ -351,7 +410,7 @@ where
return_path.depth = return_path.depth + 1;
return_path.path[return_path.depth as usize - 1] = ChildNumber::from(deriv_idx);
deriv_idx = deriv_idx + 1;
let mut batch = self.batch()?;
let mut batch = self.batch(keychain_mask)?;
batch.save_child_index(&parent_key_id, deriv_idx)?;
batch.commit()?;
Ok(Identifier::from_path(&return_path))
@ -370,13 +429,17 @@ where
Ok(last_confirmed_height)
}
fn restore(&mut self) -> Result<(), Error> {
restore(self).context(ErrorKind::Restore)?;
fn restore(&mut self, keychain_mask: Option<&SecretKey>) -> Result<(), Error> {
restore(self, keychain_mask).context(ErrorKind::Restore)?;
Ok(())
}
fn check_repair(&mut self, delete_unconfirmed: bool) -> Result<(), Error> {
check_repair(self, delete_unconfirmed).context(ErrorKind::Restore)?;
fn check_repair(
&mut self,
keychain_mask: Option<&SecretKey>,
delete_unconfirmed: bool,
) -> Result<(), Error> {
check_repair(self, keychain_mask, delete_unconfirmed).context(ErrorKind::Restore)?;
Ok(())
}
}

View file

@ -19,6 +19,7 @@ use crate::core::global;
use crate::keychain::Keychain;
use crate::libwallet::{Error, ErrorKind, NodeClient, WalletBackend, WalletLCProvider};
use crate::lifecycle::seed::WalletSeed;
use crate::util::secp::key::SecretKey;
use crate::util::ZeroingString;
use crate::LMDBBackend;
use failure::ResultExt;
@ -131,7 +132,13 @@ where
Ok(())
}
fn open_wallet(&mut self, _name: Option<&str>, password: ZeroingString) -> Result<(), Error> {
fn open_wallet(
&mut self,
_name: Option<&str>,
password: ZeroingString,
create_mask: bool,
use_test_rng: bool,
) -> Result<Option<SecretKey>, Error> {
let mut data_dir_name = PathBuf::from(self.data_dir.clone());
data_dir_name.push(GRIN_WALLET_DIR);
let data_dir_name = data_dir_name.to_str().unwrap();
@ -148,9 +155,10 @@ where
let keychain = wallet_seed
.derive_keychain(global::is_floonet())
.context(ErrorKind::Lifecycle("Error deriving keychain".into()))?;
wallet.set_keychain(Box::new(keychain));
let mask = wallet.set_keychain(Box::new(keychain), create_mask, use_test_rng)?;
self.backend = Some(Box::new(wallet));
Ok(())
Ok(mask)
}
fn close_wallet(&mut self, _name: Option<&str>) -> Result<(), Error> {

View file

@ -24,6 +24,7 @@ use crate::libwallet::api_impl::{foreign, owner};
use crate::libwallet::{
BlockFees, CbData, InitTxArgs, NodeClient, WalletInfo, WalletInst, WalletLCProvider,
};
use crate::util::secp::key::SecretKey;
use crate::util::secp::pedersen;
use crate::util::Mutex;
use chrono::Duration;
@ -105,6 +106,7 @@ pub fn award_block_to_wallet<'a, L, C, K>(
chain: &Chain,
txs: Vec<&Transaction>,
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>,
keychain_mask: Option<&SecretKey>,
) -> Result<(), libwallet::Error>
where
L: WalletLCProvider<'a, C, K>,
@ -123,7 +125,7 @@ where
let coinbase_tx = {
let mut w_lock = wallet.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
let res = foreign::build_coinbase(&mut **w, &block_fees, false)?;
let res = foreign::build_coinbase(&mut **w, keychain_mask, &block_fees, false)?;
res
};
add_block_with_reward(chain, txs, coinbase_tx.clone());
@ -134,6 +136,7 @@ where
pub fn award_blocks_to_wallet<'a, L, C, K>(
chain: &Chain,
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>,
keychain_mask: Option<&SecretKey>,
number: usize,
pause_between: bool,
) -> Result<(), libwallet::Error>
@ -143,7 +146,7 @@ where
K: keychain::Keychain + 'a,
{
for _ in 0..number {
award_block_to_wallet(chain, vec![], wallet.clone())?;
award_block_to_wallet(chain, vec![], wallet.clone(), keychain_mask)?;
if pause_between {
thread::sleep(std::time::Duration::from_millis(100));
}
@ -154,6 +157,7 @@ where
/// send an amount to a destination
pub fn send_to_dest<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
client: LocalWalletClient,
dest: &str,
amount: u64,
@ -176,10 +180,10 @@ where
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = owner::init_send_tx(&mut **w, args, test_mode)?;
let slate_i = owner::init_send_tx(&mut **w, keychain_mask, args, test_mode)?;
let slate = client.send_tx_slate_direct(dest, &slate_i)?;
owner::tx_lock_outputs(&mut **w, &slate, 0)?;
let slate = owner::finalize_tx(&mut **w, &slate)?;
owner::tx_lock_outputs(&mut **w, keychain_mask, &slate, 0)?;
let slate = owner::finalize_tx(&mut **w, keychain_mask, &slate)?;
slate
};
let client = {
@ -194,6 +198,7 @@ where
/// get wallet info totals
pub fn wallet_info<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
) -> Result<WalletInfo, libwallet::Error>
where
L: WalletLCProvider<'a, C, K>,
@ -202,7 +207,8 @@ where
{
let mut w_lock = wallet.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
let (wallet_refreshed, wallet_info) = owner::retrieve_summary_info(&mut **w, true, 1)?;
let (wallet_refreshed, wallet_info) =
owner::retrieve_summary_info(&mut **w, keychain_mask, true, 1)?;
assert!(wallet_refreshed);
Ok(wallet_info)
}

View file

@ -30,6 +30,7 @@ use crate::libwallet::{
NodeClient, NodeVersionInfo, Slate, TxWrapper, WalletInst, WalletLCProvider,
};
use crate::util;
use crate::util::secp::key::SecretKey;
use crate::util::secp::pedersen;
use crate::util::secp::pedersen::Commitment;
use crate::util::{Mutex, RwLock};
@ -73,6 +74,7 @@ where
(
Sender<WalletProxyMessage>,
Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>,
Option<SecretKey>,
),
>,
/// simulate json send to another client
@ -123,8 +125,10 @@ where
addr: &str,
tx: Sender<WalletProxyMessage>,
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>,
keychain_mask: Option<SecretKey>,
) {
self.wallets.insert(addr.to_owned(), (tx, wallet));
self.wallets
.insert(addr.to_owned(), (tx, wallet, keychain_mask));
}
pub fn stop(&mut self) {
@ -170,6 +174,7 @@ where
/// post transaction to the chain (and mine it, taking the reward)
fn post_tx(&mut self, m: WalletProxyMessage) -> Result<WalletProxyMessage, libwallet::Error> {
let dest_wallet = self.wallets.get_mut(&m.sender_id).unwrap().1.clone();
let dest_wallet_mask = self.wallets.get_mut(&m.sender_id).unwrap().2.clone();
let wrapper: TxWrapper = serde_json::from_str(&m.body).context(
libwallet::ErrorKind::ClientCallback("Error parsing TxWrapper".to_owned()),
)?;
@ -178,11 +183,17 @@ where
libwallet::ErrorKind::ClientCallback("Error parsing TxWrapper: tx_bin".to_owned()),
)?;
let tx: Transaction = ser::deserialize(&mut &tx_bin[..]).context(
libwallet::ErrorKind::ClientCallback("Error parsing TxWrapper: tx".to_owned()),
)?;
let tx: Transaction = ser::deserialize(&mut &tx_bin[..], ser::ProtocolVersion::local())
.context(libwallet::ErrorKind::ClientCallback(
"Error parsing TxWrapper: tx".to_owned(),
))?;
super::award_block_to_wallet(&self.chain, vec![&tx], dest_wallet)?;
super::award_block_to_wallet(
&self.chain,
vec![&tx],
dest_wallet,
(&dest_wallet_mask).as_ref(),
)?;
Ok(WalletProxyMessage {
sender_id: "node".to_owned(),
@ -210,8 +221,9 @@ where
{
let mut w_lock = wallet.1.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
let mask = wallet.2.clone();
// receive tx
slate = foreign::receive_tx(&mut **w, &slate, None, None, false)?;
slate = foreign::receive_tx(&mut **w, (&mask).as_ref(), &slate, None, None, false)?;
}
Ok(WalletProxyMessage {

View file

@ -16,6 +16,7 @@
use strum::IntoEnumIterator;
use crate::grin_keychain::Keychain;
use crate::grin_util::secp::key::SecretKey;
use crate::internal::{tx, updater};
use crate::slate_versions::SlateVersion;
use crate::{
@ -37,6 +38,7 @@ pub fn check_version() -> VersionInfo {
/// Build a coinbase transaction
pub fn build_coinbase<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
block_fees: &BlockFees,
test_mode: bool,
) -> Result<CbData, Error>
@ -45,7 +47,7 @@ where
C: NodeClient + 'a,
K: Keychain + 'a,
{
updater::build_coinbase(&mut *w, block_fees, test_mode)
updater::build_coinbase(&mut *w, keychain_mask, block_fees, test_mode)
}
/// verify slate messages
@ -56,6 +58,7 @@ pub fn verify_slate_messages(slate: &Slate) -> Result<(), Error> {
/// Receive a tx as recipient
pub fn receive_tx<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
dest_acct_name: Option<&str>,
message: Option<String>,
@ -101,6 +104,7 @@ where
tx::add_output_to_slate(
&mut *w,
keychain_mask,
&mut ret_slate,
&parent_key_id,
1,
@ -108,24 +112,28 @@ where
false,
use_test_rng,
)?;
tx::update_message(&mut *w, &mut ret_slate)?;
tx::update_message(&mut *w, keychain_mask, &mut ret_slate)?;
Ok(ret_slate)
}
/// Receive an tx that this wallet has issued
pub fn finalize_invoice_tx<'a, T: ?Sized, C, K>(w: &mut T, slate: &Slate) -> Result<Slate, Error>
pub fn finalize_invoice_tx<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
) -> Result<Slate, Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut sl = slate.clone();
let context = w.get_private_context(sl.id.as_bytes(), 1)?;
tx::complete_tx(&mut *w, &mut sl, 1, &context)?;
let context = w.get_private_context(keychain_mask, sl.id.as_bytes(), 1)?;
tx::complete_tx(&mut *w, keychain_mask, &mut sl, 1, &context)?;
tx::update_stored_tx(&mut *w, &mut sl, true)?;
tx::update_message(&mut *w, &mut sl)?;
tx::update_message(&mut *w, keychain_mask, &mut sl)?;
{
let mut batch = w.batch()?;
let mut batch = w.batch(keychain_mask)?;
batch.delete_private_context(sl.id.as_bytes(), 1)?;
batch.commit()?;
}

View file

@ -20,6 +20,7 @@ use crate::grin_core::core::hash::Hashed;
use crate::grin_core::core::Transaction;
use crate::grin_core::ser;
use crate::grin_util;
use crate::grin_util::secp::key::SecretKey;
use crate::grin_keychain::{Identifier, Keychain};
use crate::internal::{keys, selection, tx, updater};
@ -43,13 +44,17 @@ where
}
/// new account path
pub fn create_account_path<'a, T: ?Sized, C, K>(w: &mut T, label: &str) -> Result<Identifier, Error>
pub fn create_account_path<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
label: &str,
) -> Result<Identifier, Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
keys::new_acct_path(&mut *w, label)
keys::new_acct_path(&mut *w, keychain_mask, label)
}
/// set active account
@ -65,6 +70,7 @@ where
/// retrieve outputs
pub fn retrieve_outputs<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
include_spent: bool,
refresh_from_node: bool,
tx_id: Option<u32>,
@ -78,18 +84,25 @@ where
let mut validated = false;
if refresh_from_node {
validated = update_outputs(w, false);
validated = update_outputs(w, keychain_mask, false);
}
Ok((
validated,
updater::retrieve_outputs(&mut *w, include_spent, tx_id, Some(&parent_key_id))?,
updater::retrieve_outputs(
&mut *w,
keychain_mask,
include_spent,
tx_id,
Some(&parent_key_id),
)?,
))
}
/// Retrieve txs
pub fn retrieve_txs<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
refresh_from_node: bool,
tx_id: Option<u32>,
tx_slate_id: Option<Uuid>,
@ -103,7 +116,7 @@ where
let mut validated = false;
if refresh_from_node {
validated = update_outputs(w, false);
validated = update_outputs(w, keychain_mask, false);
}
Ok((
@ -115,6 +128,7 @@ where
/// Retrieve summary info
pub fn retrieve_summary_info<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
refresh_from_node: bool,
minimum_confirmations: u64,
) -> Result<(bool, WalletInfo), Error>
@ -127,7 +141,7 @@ where
let mut validated = false;
if refresh_from_node {
validated = update_outputs(w, false);
validated = update_outputs(w, keychain_mask, false);
}
let wallet_info = updater::retrieve_info(&mut *w, &parent_key_id, minimum_confirmations)?;
@ -137,6 +151,7 @@ where
/// Initiate tx as sender
pub fn init_send_tx<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
args: InitTxArgs,
use_test_rng: bool,
) -> Result<Slate, Error>
@ -171,6 +186,7 @@ where
if let Some(true) = args.estimate_only {
let (total, fee) = tx::estimate_send_tx(
&mut *w,
keychain_mask,
args.amount,
args.minimum_confirmations,
args.max_outputs as usize,
@ -185,6 +201,7 @@ where
let context = tx::add_inputs_to_slate(
&mut *w,
keychain_mask,
&mut slate,
args.minimum_confirmations,
args.max_outputs as usize,
@ -200,7 +217,7 @@ where
// Save the aggsig context in our DB for when we
// recieve the transaction back
{
let mut batch = w.batch()?;
let mut batch = w.batch(keychain_mask)?;
batch.save_private_context(slate.id.as_bytes(), 0, &context)?;
batch.commit()?;
}
@ -213,6 +230,7 @@ where
/// Initiate a transaction as the recipient (invoicing)
pub fn issue_invoice_tx<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
args: IssueInvoiceTxArgs,
use_test_rng: bool,
) -> Result<Slate, Error>
@ -243,6 +261,7 @@ where
let mut slate = tx::new_tx_slate(&mut *w, args.amount, 2, use_test_rng)?;
let context = tx::add_output_to_slate(
&mut *w,
keychain_mask,
&mut slate,
&parent_key_id,
1,
@ -254,7 +273,7 @@ where
// Save the aggsig context in our DB for when we
// recieve the transaction back
{
let mut batch = w.batch()?;
let mut batch = w.batch(keychain_mask)?;
batch.save_private_context(slate.id.as_bytes(), 1, &context)?;
batch.commit()?;
}
@ -270,6 +289,7 @@ where
/// output was specified
pub fn process_invoice_tx<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
args: InitTxArgs,
use_test_rng: bool,
@ -317,6 +337,7 @@ where
let context = tx::add_inputs_to_slate(
&mut *w,
keychain_mask,
&mut ret_slate,
args.minimum_confirmations,
args.max_outputs as usize,
@ -332,7 +353,7 @@ where
// Save the aggsig context in our DB for when we
// recieve the transaction back
{
let mut batch = w.batch()?;
let mut batch = w.batch(keychain_mask)?;
batch.save_private_context(slate.id.as_bytes(), 0, &context)?;
batch.commit()?;
}
@ -347,6 +368,7 @@ where
/// Lock sender outputs
pub fn tx_lock_outputs<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
participant_id: usize,
) -> Result<(), Error>
@ -355,24 +377,28 @@ where
C: NodeClient + 'a,
K: Keychain + 'a,
{
let context = w.get_private_context(slate.id.as_bytes(), participant_id)?;
selection::lock_tx_context(&mut *w, slate, &context)
let context = w.get_private_context(keychain_mask, slate.id.as_bytes(), participant_id)?;
selection::lock_tx_context(&mut *w, keychain_mask, slate, &context)
}
/// Finalize slate
pub fn finalize_tx<'a, T: ?Sized, C, K>(w: &mut T, slate: &Slate) -> Result<Slate, Error>
pub fn finalize_tx<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
) -> Result<Slate, Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut sl = slate.clone();
let context = w.get_private_context(sl.id.as_bytes(), 0)?;
tx::complete_tx(&mut *w, &mut sl, 0, &context)?;
let context = w.get_private_context(keychain_mask, sl.id.as_bytes(), 0)?;
tx::complete_tx(&mut *w, keychain_mask, &mut sl, 0, &context)?;
tx::update_stored_tx(&mut *w, &mut sl, false)?;
tx::update_message(&mut *w, &mut sl)?;
tx::update_message(&mut *w, keychain_mask, &mut sl)?;
{
let mut batch = w.batch()?;
let mut batch = w.batch(keychain_mask)?;
batch.delete_private_context(sl.id.as_bytes(), 0)?;
batch.commit()?;
}
@ -382,6 +408,7 @@ where
/// cancel tx
pub fn cancel_tx<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
tx_id: Option<u32>,
tx_slate_id: Option<Uuid>,
) -> Result<(), Error>
@ -391,12 +418,12 @@ where
K: Keychain + 'a,
{
let parent_key_id = w.parent_key_id();
if !update_outputs(w, false) {
if !update_outputs(w, keychain_mask, false) {
return Err(ErrorKind::TransactionCancellationError(
"Can't contact running Grin node. Not Cancelling.",
))?;
}
tx::cancel_tx(&mut *w, &parent_key_id, tx_id, tx_slate_id)
tx::cancel_tx(&mut *w, keychain_mask, &parent_key_id, tx_id, tx_slate_id)
}
/// get stored tx
@ -418,7 +445,7 @@ pub fn post_tx<'a, C>(client: &C, tx: &Transaction, fluff: bool) -> Result<(), E
where
C: NodeClient + 'a,
{
let tx_hex = grin_util::to_hex(ser::ser_vec(tx).unwrap());
let tx_hex = grin_util::to_hex(ser::ser_vec(tx, ser::ProtocolVersion::local()).unwrap());
let res = client.post_tx(&TxWrapper { tx_hex: tx_hex }, fluff);
if let Err(e) = res {
error!("api: post_tx: failed with error: {}", e);
@ -439,28 +466,38 @@ pub fn verify_slate_messages(slate: &Slate) -> Result<(), Error> {
}
/// Attempt to restore contents of wallet
pub fn restore<'a, T: ?Sized, C, K>(w: &mut T) -> Result<(), Error>
pub fn restore<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
) -> Result<(), Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
w.restore()
w.restore(keychain_mask)
}
/// check repair
pub fn check_repair<'a, T: ?Sized, C, K>(w: &mut T, delete_unconfirmed: bool) -> Result<(), Error>
pub fn check_repair<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
delete_unconfirmed: bool,
) -> Result<(), Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
update_outputs(w, true);
w.check_repair(delete_unconfirmed)
update_outputs(w, keychain_mask, true);
w.check_repair(keychain_mask, delete_unconfirmed)
}
/// node height
pub fn node_height<'a, T: ?Sized, C, K>(w: &mut T) -> Result<NodeHeightResult, Error>
pub fn node_height<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
) -> Result<NodeHeightResult, Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
@ -473,7 +510,7 @@ where
updated_from_node: true,
}),
Err(_) => {
let outputs = retrieve_outputs(w, true, false, None)?;
let outputs = retrieve_outputs(w, keychain_mask, true, false, None)?;
let height = match outputs.1.iter().map(|m| m.output.height).max() {
Some(height) => height,
None => 0,
@ -487,14 +524,18 @@ where
}
/// Attempt to update outputs in wallet, return whether it was successful
fn update_outputs<'a, T: ?Sized, C, K>(w: &mut T, update_all: bool) -> bool
fn update_outputs<'a, T: ?Sized, C, K>(
w: &mut T,
keychain_mask: Option<&SecretKey>,
update_all: bool,
) -> bool
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let parent_key_id = w.parent_key_id();
match updater::refresh_outputs(&mut *w, &parent_key_id, update_all) {
match updater::refresh_outputs(&mut *w, keychain_mask, &parent_key_id, update_all) {
Ok(_) => true,
Err(_) => false,
}

View file

@ -205,6 +205,10 @@ pub enum ErrorKind {
#[fail(display = "Lifecycle Error: {}", _0)]
Lifecycle(String),
/// Invalid Keychain Mask Error
#[fail(display = "Supplied Keychain Mask Token is incorrect")]
InvalidKeychainMask,
/// Other
#[fail(display = "Generic error: {}", _0)]
GenericError(String),

View file

@ -15,16 +15,20 @@
//! Wallet key management functions
use crate::error::{Error, ErrorKind};
use crate::grin_keychain::{ChildNumber, ExtKeychain, Identifier, Keychain};
use crate::grin_util::secp::key::SecretKey;
use crate::types::{AcctPathMapping, NodeClient, WalletBackend};
/// Get next available key in the wallet for a given parent
pub fn next_available_key<'a, T: ?Sized, C, K>(wallet: &mut T) -> Result<Identifier, Error>
pub fn next_available_key<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
) -> Result<Identifier, Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let child = wallet.next_child()?;
let child = wallet.next_child(keychain_mask)?;
Ok(child)
}
@ -56,7 +60,11 @@ where
}
/// Adds an new parent account path with a given label
pub fn new_acct_path<'a, T: ?Sized, C, K>(wallet: &mut T, label: &str) -> Result<Identifier, Error>
pub fn new_acct_path<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
label: &str,
) -> Result<Identifier, Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
@ -90,7 +98,7 @@ where
path: return_id.clone(),
};
let mut batch = wallet.batch()?;
let mut batch = wallet.batch(keychain_mask)?;
batch.save_acct_path(save_path)?;
batch.commit()?;
Ok(return_id)
@ -99,6 +107,7 @@ where
/// Adds/sets a particular account path with a given label
pub fn set_acct_path<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
label: &str,
path: &Identifier,
) -> Result<(), Error>
@ -113,7 +122,7 @@ where
path: path.clone(),
};
let mut batch = wallet.batch()?;
let mut batch = wallet.batch(keychain_mask)?;
batch.save_acct_path(save_path)?;
batch.commit()?;
Ok(())

View file

@ -18,6 +18,7 @@ use crate::grin_core::core::HeaderVersion;
use crate::grin_core::global;
use crate::grin_core::libtx::proof;
use crate::grin_keychain::{ExtKeychain, Identifier, Keychain, SwitchCommitmentType};
use crate::grin_util::secp::key::SecretKey;
use crate::grin_util::secp::pedersen;
use crate::internal::{keys, updater};
use crate::types::*;
@ -60,6 +61,7 @@ struct RestoredTxStats {
fn identify_utxo_outputs<'a, T, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
) -> Result<Vec<OutputResult>, Error>
where
@ -74,9 +76,9 @@ where
outputs.len(),
);
let keychain = wallet.keychain()?;
let legacy_builder = proof::LegacyProofBuilder::new(keychain);
let builder = proof::ProofBuilder::new(keychain);
let keychain = wallet.keychain(keychain_mask)?;
let legacy_builder = proof::LegacyProofBuilder::new(&keychain);
let builder = proof::ProofBuilder::new(&keychain);
let legacy_version = HeaderVersion(1);
for output in outputs.iter() {
@ -136,7 +138,10 @@ where
Ok(wallet_outputs)
}
fn collect_chain_outputs<'a, T, C, K>(wallet: &mut T) -> Result<Vec<OutputResult>, Error>
fn collect_chain_outputs<'a, T, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
) -> Result<Vec<OutputResult>, Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
@ -156,7 +161,11 @@ where
last_retrieved_index,
);
result_vec.append(&mut identify_utxo_outputs(wallet, outputs.clone())?);
result_vec.append(&mut identify_utxo_outputs(
wallet,
keychain_mask,
outputs.clone(),
)?);
if highest_index == last_retrieved_index {
break;
@ -169,6 +178,7 @@ where
///
fn restore_missing_output<'a, T, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
output: OutputResult,
found_parents: &mut HashMap<Identifier, u32>,
tx_stats: &mut Option<&mut HashMap<Identifier, RestoredTxStats>>,
@ -178,8 +188,8 @@ where
C: NodeClient + 'a,
K: Keychain + 'a,
{
let commit = wallet.calc_commit_for_cache(output.value, &output.key_id)?;
let mut batch = wallet.batch()?;
let commit = wallet.calc_commit_for_cache(keychain_mask, output.value, &output.key_id)?;
let mut batch = wallet.batch(keychain_mask)?;
let parent_key_id = output.key_id.parent_path();
if !found_parents.contains_key(&parent_key_id) {
@ -250,7 +260,11 @@ where
}
///
fn cancel_tx_log_entry<'a, T, C, K>(wallet: &mut T, output: &OutputData) -> Result<(), Error>
fn cancel_tx_log_entry<'a, T, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
output: &OutputData,
) -> Result<(), Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
@ -279,7 +293,7 @@ where
} else {
None
};
let mut batch = wallet.batch()?;
let mut batch = wallet.batch(keychain_mask)?;
if let Some(t) = updated_tx_entry {
batch.save_tx_log_entry(t, &parent_key_id)?;
}
@ -290,7 +304,11 @@ where
/// Check / repair wallet contents
/// assume wallet contents have been freshly updated with contents
/// of latest block
pub fn check_repair<'a, T, C, K>(wallet: &mut T, delete_unconfirmed: bool) -> Result<(), Error>
pub fn check_repair<'a, T, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
delete_unconfirmed: bool,
) -> Result<(), Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
@ -298,7 +316,7 @@ where
{
// First, get a definitive list of outputs we own from the chain
warn!("Starting wallet check.");
let chain_outs = collect_chain_outputs(wallet)?;
let chain_outs = collect_chain_outputs(wallet, keychain_mask)?;
warn!(
"Identified {} wallet_outputs as belonging to this wallet",
chain_outs.len(),
@ -306,7 +324,7 @@ where
// Now, get all outputs owned by this wallet (regardless of account)
let wallet_outputs = {
let res = updater::retrieve_outputs(&mut *wallet, true, None, None)?;
let res = updater::retrieve_outputs(&mut *wallet, keychain_mask, true, None, None)?;
res
};
@ -340,8 +358,8 @@ where
);
o.status = OutputStatus::Unspent;
// any transactions associated with this should be cancelled
cancel_tx_log_entry(wallet, &o)?;
let mut batch = wallet.batch()?;
cancel_tx_log_entry(wallet, keychain_mask, &o)?;
let mut batch = wallet.batch(keychain_mask)?;
batch.save(o)?;
batch.commit()?;
}
@ -355,7 +373,7 @@ where
Restoring.",
m.value, m.key_id, m.commit,
);
restore_missing_output(wallet, m, &mut found_parents, &mut None)?;
restore_missing_output(wallet, keychain_mask, m, &mut found_parents, &mut None)?;
}
if delete_unconfirmed {
@ -368,8 +386,8 @@ where
o.value, o.key_id, m.1.commit,
);
o.status = OutputStatus::Unspent;
cancel_tx_log_entry(wallet, &o)?;
let mut batch = wallet.batch()?;
cancel_tx_log_entry(wallet, keychain_mask, &o)?;
let mut batch = wallet.batch(keychain_mask)?;
batch.save(o)?;
batch.commit()?;
}
@ -386,8 +404,8 @@ where
Deleting and cancelling associated transaction log entries.",
o.value, o.key_id, m.commit,
);
cancel_tx_log_entry(wallet, &o)?;
let mut batch = wallet.batch()?;
cancel_tx_log_entry(wallet, keychain_mask, &o)?;
let mut batch = wallet.batch(keychain_mask)?;
batch.delete(&o.key_id, &o.mmr_index)?;
batch.commit()?;
}
@ -400,10 +418,10 @@ where
// default path already exists
if *path != ExtKeychain::derive_key_id(2, 0, 0, 0, 0) {
let label = format!("{}_{}", label_base, acct_index);
keys::set_acct_path(wallet, &label, path)?;
keys::set_acct_path(wallet, keychain_mask, &label, path)?;
acct_index += 1;
}
let mut batch = wallet.batch()?;
let mut batch = wallet.batch(keychain_mask)?;
debug!("Next child for account {} is {}", path, max_child_index + 1);
batch.save_child_index(path, max_child_index + 1)?;
batch.commit()?;
@ -412,7 +430,7 @@ where
}
/// Restore a wallet
pub fn restore<'a, T, C, K>(wallet: &mut T) -> Result<(), Error>
pub fn restore<'a, T, C, K>(wallet: &mut T, keychain_mask: Option<&SecretKey>) -> Result<(), Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
@ -428,7 +446,7 @@ where
let now = Instant::now();
warn!("Starting restore.");
let result_vec = collect_chain_outputs(wallet)?;
let result_vec = collect_chain_outputs(wallet, keychain_mask)?;
warn!(
"Identified {} wallet_outputs as belonging to this wallet",
@ -442,6 +460,7 @@ where
for output in result_vec {
restore_missing_output(
wallet,
keychain_mask,
output,
&mut found_parents,
&mut Some(&mut restore_stats),
@ -455,12 +474,12 @@ where
// default path already exists
if *path != ExtKeychain::derive_key_id(2, 0, 0, 0, 0) {
let label = format!("{}_{}", label_base, acct_index);
keys::set_acct_path(wallet, &label, path)?;
keys::set_acct_path(wallet, keychain_mask, &label, path)?;
acct_index += 1;
}
// restore tx log entry for non-coinbase outputs
if let Some(s) = restore_stats.get(path) {
let mut batch = wallet.batch()?;
let mut batch = wallet.batch(keychain_mask)?;
let mut t = TxLogEntry::new(path.clone(), TxLogEntryType::TxReceived, s.log_id);
t.confirmed = true;
t.amount_credited = s.amount_credited;
@ -469,7 +488,7 @@ where
batch.save_tx_log_entry(t, &path)?;
batch.commit()?;
}
let mut batch = wallet.batch()?;
let mut batch = wallet.batch(keychain_mask)?;
batch.save_child_index(path, max_child_index + 1)?;
debug!("Next child for account {} is {}", path, max_child_index + 1);
batch.commit()?;

View file

@ -22,6 +22,7 @@ use crate::grin_core::libtx::{
tx_fee,
};
use crate::grin_keychain::{Identifier, Keychain};
use crate::grin_util::secp::key::SecretKey;
use crate::internal::keys;
use crate::slate::Slate;
use crate::types::*;
@ -34,6 +35,8 @@ use std::collections::HashMap;
pub fn build_send_tx<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain: &K,
keychain_mask: Option<&SecretKey>,
slate: &mut Slate,
minimum_confirmations: u64,
max_outputs: usize,
@ -49,6 +52,7 @@ where
{
let (elems, inputs, change_amounts_derivations, fee) = select_send_tx(
wallet,
keychain_mask,
slate.amount,
slate.height,
minimum_confirmations,
@ -58,7 +62,6 @@ where
selection_strategy_is_use_all,
&parent_key_id,
)?;
let keychain = wallet.keychain()?;
let blinding = slate.add_transaction_elements(keychain, &ProofBuilder::new(keychain), elems)?;
slate.fee = fee;
@ -86,7 +89,7 @@ where
context.add_output(&id, &mmr_index, *change_amount);
commits.insert(
id.clone(),
wallet.calc_commit_for_cache(*change_amount, &id)?,
wallet.calc_commit_for_cache(keychain_mask, *change_amount, &id)?,
);
}
@ -97,6 +100,7 @@ where
/// change outputs and tx log entry
pub fn lock_tx_context<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
context: &Context,
) -> Result<(), Error>
@ -111,7 +115,7 @@ where
output_commits.insert(
id.clone(),
(
wallet.calc_commit_for_cache(*change_amount, &id)?,
wallet.calc_commit_for_cache(keychain_mask, *change_amount, &id)?,
*change_amount,
),
);
@ -123,7 +127,7 @@ where
let slate_id = slate.id;
let height = slate.height;
let parent_key_id = context.parent_key_id.clone();
let mut batch = wallet.batch()?;
let mut batch = wallet.batch(keychain_mask)?;
let log_id = batch.next_tx_log_id(&parent_key_id)?;
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxSent, log_id);
t.tx_slate_id = Some(slate_id.clone());
@ -174,6 +178,7 @@ where
/// Also creates a new transaction containing the output
pub fn build_recipient_output<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
slate: &mut Slate,
parent_key_id: Identifier,
use_test_rng: bool,
@ -184,8 +189,8 @@ where
K: Keychain + 'a,
{
// Create a potential output for this transaction
let key_id = keys::next_available_key(wallet).unwrap();
let keychain = wallet.keychain()?.clone();
let key_id = keys::next_available_key(wallet, keychain_mask).unwrap();
let keychain = wallet.keychain(keychain_mask)?;
let key_id_inner = key_id.clone();
let amount = slate.amount;
let height = slate.height;
@ -201,7 +206,7 @@ where
let mut context = Context::new(
keychain.secp(),
blinding
.secret_key(wallet.keychain()?.clone().secp())
.secret_key(wallet.keychain(keychain_mask)?.secp())
.unwrap(),
&parent_key_id,
use_test_rng,
@ -210,8 +215,8 @@ where
context.add_output(&key_id, &None, amount);
let messages = Some(slate.participant_messages());
let commit = wallet.calc_commit_for_cache(amount, &key_id_inner)?;
let mut batch = wallet.batch()?;
let commit = wallet.calc_commit_for_cache(keychain_mask, amount, &key_id_inner)?;
let mut batch = wallet.batch(keychain_mask)?;
let log_id = batch.next_tx_log_id(&parent_key_id)?;
let mut t = TxLogEntry::new(parent_key_id.clone(), TxLogEntryType::TxReceived, log_id);
t.tx_slate_id = Some(slate_id);
@ -242,6 +247,7 @@ where
/// selecting outputs to spend and building the change.
pub fn select_send_tx<'a, T: ?Sized, C, K, B>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
amount: u64,
current_height: u64,
minimum_confirmations: u64,
@ -278,7 +284,7 @@ where
// build transaction skeleton with inputs and change
let (mut parts, change_amounts_derivations) =
inputs_and_change(&coins, wallet, amount, fee, change_outputs)?;
inputs_and_change(&coins, wallet, keychain_mask, amount, fee, change_outputs)?;
// This is more proof of concept than anything but here we set lock_height
// on tx being sent (based on current chain height via api).
@ -396,6 +402,7 @@ where
pub fn inputs_and_change<'a, T: ?Sized, C, K, B>(
coins: &Vec<OutputData>,
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
amount: u64,
fee: u64,
num_change_outputs: usize,
@ -454,7 +461,7 @@ where
part_change
};
let change_key = wallet.next_child().unwrap();
let change_key = wallet.next_child(keychain_mask).unwrap();
change_amounts_derivations.push((change_amount, change_key.clone(), None));
parts.push(build::output(change_amount, change_key));

View file

@ -19,6 +19,7 @@ use uuid::Uuid;
use crate::grin_core::consensus::valid_header_version;
use crate::grin_core::core::HeaderVersion;
use crate::grin_keychain::{Identifier, Keychain};
use crate::grin_util::secp::key::SecretKey;
use crate::grin_util::Mutex;
use crate::internal::{selection, updater};
use crate::slate::Slate;
@ -70,6 +71,7 @@ where
/// Estimates locked amount and fee for the transaction without creating one
pub fn estimate_send_tx<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
amount: u64,
minimum_confirmations: u64,
max_outputs: usize,
@ -91,7 +93,7 @@ where
// Get lock height
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, keychain_mask, parent_key_id, false)?;
// 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
@ -116,6 +118,7 @@ where
/// Add inputs to the slate (effectively becoming the sender)
pub fn add_inputs_to_slate<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
slate: &mut Slate,
minimum_confirmations: u64,
max_outputs: usize,
@ -133,7 +136,7 @@ where
K: Keychain + 'a,
{
// sender should always refresh outputs
updater::refresh_outputs(wallet, parent_key_id, false)?;
updater::refresh_outputs(wallet, keychain_mask, parent_key_id, false)?;
// 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
@ -144,6 +147,8 @@ where
// this process can be split up in any way
let mut context = selection::build_send_tx(
wallet,
&wallet.keychain(keychain_mask)?,
keychain_mask,
slate,
minimum_confirmations,
max_outputs,
@ -157,7 +162,7 @@ where
// the offset in the slate's transaction kernel, and adds our public key
// information to the slate
let _ = slate.fill_round_1(
wallet.keychain()?,
&wallet.keychain(keychain_mask)?,
&mut context.sec_key,
&context.sec_nonce,
participant_id,
@ -168,7 +173,7 @@ where
if !is_initator {
// perform partial sig
let _ = slate.fill_round_2(
wallet.keychain()?,
&wallet.keychain(keychain_mask)?,
&context.sec_key,
&context.sec_nonce,
participant_id,
@ -181,6 +186,7 @@ where
/// Add receiver output to the slate
pub fn add_output_to_slate<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
slate: &mut Slate,
parent_key_id: &Identifier,
participant_id: usize,
@ -194,12 +200,17 @@ where
K: Keychain + 'a,
{
// create an output using the amount in the slate
let (_, mut context) =
selection::build_recipient_output(wallet, slate, parent_key_id.clone(), use_test_rng)?;
let (_, mut context) = selection::build_recipient_output(
wallet,
keychain_mask,
slate,
parent_key_id.clone(),
use_test_rng,
)?;
// fill public keys
let _ = slate.fill_round_1(
wallet.keychain()?,
&wallet.keychain(keychain_mask)?,
&mut context.sec_key,
&context.sec_nonce,
1,
@ -210,7 +221,7 @@ where
if !is_initiator {
// perform partial sig
let _ = slate.fill_round_2(
wallet.keychain()?,
&wallet.keychain(keychain_mask)?,
&context.sec_key,
&context.sec_nonce,
participant_id,
@ -223,6 +234,7 @@ where
/// Complete a transaction
pub fn complete_tx<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
slate: &mut Slate,
participant_id: usize,
context: &Context,
@ -233,20 +245,21 @@ where
K: Keychain + 'a,
{
let _ = slate.fill_round_2(
wallet.keychain()?,
&wallet.keychain(keychain_mask)?,
&context.sec_key,
&context.sec_nonce,
participant_id,
)?;
// Final transaction can be built by anyone at this stage
slate.finalize(wallet.keychain()?)?;
slate.finalize(&wallet.keychain(keychain_mask)?)?;
Ok(())
}
/// Rollback outputs associated with a transaction in the wallet
pub fn cancel_tx<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
parent_key_id: &Identifier,
tx_id: Option<u32>,
tx_slate_id: Option<Uuid>,
@ -274,9 +287,15 @@ where
return Err(ErrorKind::TransactionNotCancellable(tx_id_string))?;
}
// get outputs associated with tx
let res = updater::retrieve_outputs(wallet, false, Some(tx.id), Some(&parent_key_id))?;
let res = updater::retrieve_outputs(
wallet,
keychain_mask,
false,
Some(tx.id),
Some(&parent_key_id),
)?;
let outputs = res.iter().map(|m| m.output.clone()).collect();
updater::cancel_tx_and_outputs(wallet, tx, outputs, parent_key_id)?;
updater::cancel_tx_and_outputs(wallet, keychain_mask, tx, outputs, parent_key_id)?;
Ok(())
}
@ -314,7 +333,11 @@ where
}
/// Update the transaction participant messages
pub fn update_message<'a, T: ?Sized, C, K>(wallet: &mut T, slate: &Slate) -> Result<(), Error>
pub fn update_message<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
) -> Result<(), Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
@ -324,7 +347,7 @@ where
if tx_vec.is_empty() {
return Err(ErrorKind::TransactionDoesntExist(slate.id.to_string()))?;
}
let mut batch = wallet.batch()?;
let mut batch = wallet.batch(keychain_mask)?;
for mut tx in tx_vec.into_iter() {
tx.messages = Some(slate.participant_messages());
let parent_key = tx.parent_key_id.clone();

View file

@ -26,6 +26,7 @@ use crate::grin_core::libtx::proof::ProofBuilder;
use crate::grin_core::libtx::reward;
use crate::grin_keychain::{Identifier, Keychain, SwitchCommitmentType};
use crate::grin_util as util;
use crate::grin_util::secp::key::SecretKey;
use crate::grin_util::secp::pedersen;
use crate::internal::keys;
use crate::types::{
@ -36,6 +37,7 @@ use crate::{BlockFees, CbData, OutputCommitMapping};
/// Retrieve all of the outputs (doesn't attempt to update from node)
pub fn retrieve_outputs<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
show_spent: bool,
tx_id: Option<u32>,
parent_key_id: Option<&Identifier>,
@ -68,7 +70,7 @@ where
}
outputs.sort_by_key(|out| out.n_child);
let keychain = wallet.keychain()?.clone();
let keychain = wallet.keychain(keychain_mask)?;
let res = outputs
.into_iter()
@ -133,6 +135,7 @@ where
/// from a node
pub fn refresh_outputs<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
parent_key_id: &Identifier,
update_all: bool,
) -> Result<(), Error>
@ -142,7 +145,7 @@ where
K: Keychain + 'a,
{
let height = wallet.w2n_client().get_chain_height()?;
refresh_output_state(wallet, height, parent_key_id, update_all)?;
refresh_output_state(wallet, keychain_mask, height, parent_key_id, update_all)?;
Ok(())
}
@ -150,6 +153,7 @@ where
/// and a list of outputs we want to query the node for
pub fn map_wallet_outputs<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
parent_key_id: &Identifier,
update_all: bool,
) -> Result<HashMap<pedersen::Commitment, (Identifier, Option<u64>)>, Error>
@ -160,7 +164,7 @@ where
{
let mut wallet_outputs: HashMap<pedersen::Commitment, (Identifier, Option<u64>)> =
HashMap::new();
let keychain = wallet.keychain()?.clone();
let keychain = wallet.keychain(keychain_mask)?;
let unspents: Vec<OutputData> = wallet
.iter()
.filter(|x| x.root_key_id == *parent_key_id && x.status != OutputStatus::Spent)
@ -201,6 +205,7 @@ where
/// Cancel transaction and associated outputs
pub fn cancel_tx_and_outputs<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
tx: TxLogEntry,
outputs: Vec<OutputData>,
parent_key_id: &Identifier,
@ -210,7 +215,7 @@ where
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut batch = wallet.batch()?;
let mut batch = wallet.batch(keychain_mask)?;
for mut o in outputs {
// unlock locked outputs
@ -237,6 +242,7 @@ where
/// Apply refreshed API output data to the wallet
pub fn apply_api_outputs<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
wallet_outputs: &HashMap<pedersen::Commitment, (Identifier, Option<u64>)>,
api_outputs: &HashMap<pedersen::Commitment, (String, u64, u64)>,
height: u64,
@ -262,7 +268,7 @@ where
warn!("Please wait for sync on node to complete or fork to resolve and try again.");
return Ok(());
}
let mut batch = wallet.batch()?;
let mut batch = wallet.batch(keychain_mask)?;
for (commit, (id, mmr_index)) in wallet_outputs.iter() {
if let Ok(mut output) = batch.get(id, mmr_index) {
match api_outputs.get(&commit) {
@ -317,6 +323,7 @@ where
/// So we can refresh the local wallet outputs.
fn refresh_output_state<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
height: u64,
parent_key_id: &Identifier,
update_all: bool,
@ -330,19 +337,30 @@ where
// build a local map of wallet outputs keyed by commit
// and a list of outputs we want to query the node for
let wallet_outputs = map_wallet_outputs(wallet, parent_key_id, update_all)?;
let wallet_outputs = map_wallet_outputs(wallet, keychain_mask, parent_key_id, update_all)?;
let wallet_output_keys = wallet_outputs.keys().map(|commit| commit.clone()).collect();
let api_outputs = wallet
.w2n_client()
.get_outputs_from_node(wallet_output_keys)?;
apply_api_outputs(wallet, &wallet_outputs, &api_outputs, height, parent_key_id)?;
clean_old_unconfirmed(wallet, height)?;
apply_api_outputs(
wallet,
keychain_mask,
&wallet_outputs,
&api_outputs,
height,
parent_key_id,
)?;
clean_old_unconfirmed(wallet, keychain_mask, height)?;
Ok(())
}
fn clean_old_unconfirmed<'a, T: ?Sized, C, K>(wallet: &mut T, height: u64) -> Result<(), Error>
fn clean_old_unconfirmed<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
height: u64,
) -> Result<(), Error>
where
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
@ -361,7 +379,7 @@ where
ids_to_del.push(out.key_id.clone())
}
}
let mut batch = wallet.batch()?;
let mut batch = wallet.batch(keychain_mask)?;
for id in ids_to_del {
batch.delete(&id, &None)?;
}
@ -436,6 +454,7 @@ where
/// Build a coinbase output and insert into wallet
pub fn build_coinbase<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
block_fees: &BlockFees,
test_mode: bool,
) -> Result<CbData, Error>
@ -444,7 +463,7 @@ where
C: NodeClient + 'a,
K: Keychain + 'a,
{
let (out, kern, block_fees) = receive_coinbase(wallet, block_fees, test_mode)?;
let (out, kern, block_fees) = receive_coinbase(wallet, keychain_mask, block_fees, test_mode)?;
Ok(CbData {
output: out,
@ -457,6 +476,7 @@ where
/// Build a coinbase output and the corresponding kernel
pub fn receive_coinbase<'a, T: ?Sized, C, K>(
wallet: &mut T,
keychain_mask: Option<&SecretKey>,
block_fees: &BlockFees,
test_mode: bool,
) -> Result<(Output, TxKernel, BlockFees), Error>
@ -473,16 +493,16 @@ where
let key_id = match key_id {
Some(key_id) => match keys::retrieve_existing_key(wallet, key_id, None) {
Ok(k) => k.0,
Err(_) => keys::next_available_key(wallet)?,
Err(_) => keys::next_available_key(wallet, keychain_mask)?,
},
None => keys::next_available_key(wallet)?,
None => keys::next_available_key(wallet, keychain_mask)?,
};
{
// Now acquire the wallet lock and write the new output.
let amount = reward(block_fees.fees);
let commit = wallet.calc_commit_for_cache(amount, &key_id)?;
let mut batch = wallet.batch()?;
let commit = wallet.calc_commit_for_cache(keychain_mask, amount, &key_id)?;
let mut batch = wallet.batch(keychain_mask)?;
batch.save(OutputData {
root_key_id: parent_key_id,
key_id: key_id.clone(),
@ -510,10 +530,10 @@ where
debug!("receive_coinbase: {:?}", block_fees);
let keychain = wallet.keychain()?;
let keychain = wallet.keychain(keychain_mask)?;
let (out, kern) = reward::output(
keychain,
&ProofBuilder::new(keychain),
&keychain,
&ProofBuilder::new(&keychain),
&key_id,
block_fees.fees,
test_mode,

View file

@ -67,7 +67,13 @@ where
) -> Result<(), Error>;
///
fn open_wallet(&mut self, name: Option<&str>, password: ZeroingString) -> Result<(), Error>;
fn open_wallet(
&mut self,
name: Option<&str>,
password: ZeroingString,
create_mask: bool,
use_test_rng: bool,
) -> Result<Option<SecretKey>, Error>;
///
fn close_wallet(&mut self, name: Option<&str>) -> Result<(), Error>;
@ -113,13 +119,22 @@ where
K: Keychain + 'ck,
{
/// Set the keychain, which should already be initialized
fn set_keychain(&mut self, k: Box<K>);
/// Optionally return a token value used to XOR the stored
/// key value
fn set_keychain(
&mut self,
k: Box<K>,
mask: bool,
use_test_rng: bool,
) -> Result<Option<SecretKey>, Error>;
/// Close wallet and remove any stored credentials (TBD)
fn close(&mut self) -> Result<(), Error>;
/// Return the keychain being used
fn keychain(&mut self) -> Result<&mut K, Error>;
/// Return the keychain being used. Ensure a cloned copy so it will be dropped
/// and zeroized by the caller
/// Can optionally take a mask value
fn keychain(&self, mask: Option<&SecretKey>) -> Result<K, Error>;
/// Return the client being used to communicate with the node
fn w2n_client(&mut self) -> &mut C;
@ -127,6 +142,7 @@ where
/// return the commit for caching if allowed, none otherwise
fn calc_commit_for_cache(
&mut self,
keychain_mask: Option<&SecretKey>,
amount: u64,
id: &Identifier,
) -> Result<Option<String>, Error>;
@ -153,6 +169,7 @@ where
/// Retrieves the private context associated with a given slate id
fn get_private_context(
&mut self,
keychain_mask: Option<&SecretKey>,
slate_id: &[u8],
participant_id: usize,
) -> Result<Context, Error>;
@ -173,19 +190,26 @@ where
fn get_stored_tx(&self, entry: &TxLogEntry) -> Result<Option<Transaction>, Error>;
/// Create a new write batch to update or remove output data
fn batch<'a>(&'a mut self) -> Result<Box<dyn WalletOutputBatch<K> + 'a>, Error>;
fn batch<'a>(
&'a mut self,
keychain_mask: Option<&SecretKey>,
) -> Result<Box<dyn WalletOutputBatch<K> + 'a>, Error>;
/// Next child ID when we want to create a new output, based on current parent
fn next_child<'a>(&mut self) -> Result<Identifier, Error>;
fn next_child<'a>(&mut self, keychain_mask: Option<&SecretKey>) -> Result<Identifier, Error>;
/// last verified height of outputs directly descending from the given parent key
fn last_confirmed_height<'a>(&mut self) -> Result<u64, Error>;
/// Attempt to restore the contents of a wallet from seed
fn restore(&mut self) -> Result<(), Error>;
fn restore(&mut self, keychain_mask: Option<&SecretKey>) -> Result<(), Error>;
/// Attempt to check and fix wallet state
fn check_repair(&mut self, delete_unconfirmed: bool) -> Result<(), Error>;
fn check_repair(
&mut self,
keychain_mask: Option<&SecretKey>,
delete_unconfirmed: bool,
) -> Result<(), Error>;
}
/// Batch trait to update the output data backend atomically. Trying to use a

View file

@ -806,19 +806,27 @@ where
}
// don't open wallet for certain lifecycle commands
match wallet_args.subcommand() {
("init", Some(_)) => {}
("recover", _) => {}
let keychain_mask = match wallet_args.subcommand() {
("init", Some(_)) => None,
("recover", _) => None,
_ => {
let mut wallet_lock = wallet.lock();
let lc = wallet_lock.lc_provider().unwrap();
lc.open_wallet(None, prompt_password(&global_wallet_args.password))?;
let mask = lc.open_wallet(
None,
prompt_password(&global_wallet_args.password),
true,
false,
)?;
if let Some(account) = wallet_args.value_of("account") {
let wallet_inst = lc.wallet_inst()?;
wallet_inst.set_parent_key_id_by_name(account)?;
}
mask
}
}
};
let km = (&keychain_mask).as_ref();
let res = match wallet_args.subcommand() {
("init", Some(args)) => {
@ -841,42 +849,46 @@ where
("listen", Some(args)) => {
let mut c = wallet_config.clone();
let a = arg_parse!(parse_listen_args(&mut c, &args));
command::listen(wallet, &c, &a, &global_wallet_args.clone())
command::listen(wallet, keychain_mask, &c, &a, &global_wallet_args.clone())
}
("owner_api", Some(_)) => {
let mut g = global_wallet_args.clone();
g.tls_conf = None;
command::owner_api(wallet, &wallet_config, &g)
command::owner_api(wallet, keychain_mask, &wallet_config, &g)
}
("web", Some(_)) => {
command::owner_api(wallet, keychain_mask, &wallet_config, &global_wallet_args)
}
("web", Some(_)) => command::owner_api(wallet, &wallet_config, &global_wallet_args),
("account", Some(args)) => {
let a = arg_parse!(parse_account_args(&args));
command::account(wallet, a)
command::account(wallet, km, a)
}
("send", Some(args)) => {
let a = arg_parse!(parse_send_args(&args));
command::send(
wallet,
km,
a,
wallet_config.dark_background_color_scheme.unwrap_or(true),
)
}
("receive", Some(args)) => {
let a = arg_parse!(parse_receive_args(&args));
command::receive(wallet, &global_wallet_args, a)
command::receive(wallet, km, &global_wallet_args, a)
}
("finalize", Some(args)) => {
let a = arg_parse!(parse_finalize_args(&args));
command::finalize(wallet, a)
command::finalize(wallet, km, a)
}
("invoice", Some(args)) => {
let a = arg_parse!(parse_issue_invoice_args(&args));
command::issue_invoice_tx(wallet, a)
command::issue_invoice_tx(wallet, km, a)
}
("pay", Some(args)) => {
let a = arg_parse!(parse_process_invoice_args(&args));
command::process_invoice(
wallet,
km,
a,
wallet_config.dark_background_color_scheme.unwrap_or(true),
)
@ -885,6 +897,7 @@ where
let a = arg_parse!(parse_info_args(&args));
command::info(
wallet,
km,
&global_wallet_args,
a,
wallet_config.dark_background_color_scheme.unwrap_or(true),
@ -892,6 +905,7 @@ where
}
("outputs", Some(_)) => command::outputs(
wallet,
km,
&global_wallet_args,
wallet_config.dark_background_color_scheme.unwrap_or(true),
),
@ -899,6 +913,7 @@ where
let a = arg_parse!(parse_txs_args(&args));
command::txs(
wallet,
km,
&global_wallet_args,
a,
wallet_config.dark_background_color_scheme.unwrap_or(true),
@ -906,16 +921,16 @@ where
}
("repost", Some(args)) => {
let a = arg_parse!(parse_repost_args(&args));
command::repost(wallet, a)
command::repost(wallet, km, a)
}
("cancel", Some(args)) => {
let a = arg_parse!(parse_cancel_args(&args));
command::cancel(wallet, a)
command::cancel(wallet, km, a)
}
("restore", Some(_)) => command::restore(wallet),
("restore", Some(_)) => command::restore(wallet, km),
("check", Some(args)) => {
let a = arg_parse!(parse_check_args(&args));
command::check_repair(wallet, a)
command::check_repair(wallet, km, a)
}
_ => {
let msg = format!("Unknown wallet command, use 'grin help wallet' for details");

View file

@ -32,6 +32,7 @@ mod wallet_tests {
use grin_wallet_libwallet::WalletInst;
use grin_wallet_util::grin_core::global::{self, ChainTypes};
use grin_wallet_util::grin_keychain::ExtKeychain;
use util::secp::key::SecretKey;
use super::super::wallet_args;
@ -123,18 +124,21 @@ mod wallet_tests {
passphrase: &str,
account: &str,
) -> Result<
Arc<
Mutex<
Box<
WalletInst<
'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
(
Arc<
Mutex<
Box<
WalletInst<
'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>,
>,
>,
>,
Option<SecretKey>,
),
grin_wallet_controller::Error,
> {
wallet_config.chain_type = None;
@ -156,11 +160,12 @@ mod wallet_tests {
wallet_config.data_file_dir = top_level_wallet_dir.to_str().unwrap().into();
}
lc.set_wallet_directory(&wallet_config.data_file_dir);
lc.open_wallet(None, ZeroingString::from(passphrase))
let keychain_mask = lc
.open_wallet(None, ZeroingString::from(passphrase), true, false)
.unwrap();
let wallet_inst = lc.wallet_inst()?;
wallet_inst.set_parent_key_id_by_name(account)?;
Ok(Arc::new(Mutex::new(wallet)))
Ok((Arc::new(Mutex::new(wallet)), keychain_mask))
}
fn execute_command(
@ -206,16 +211,28 @@ mod wallet_tests {
// add wallet to proxy
//let wallet1 = test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
let config1 = initial_setup_wallet(test_dir, "wallet1");
let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
let (wallet1, mask1_i) =
instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
wallet_proxy.add_wallet(
"wallet1",
client1.get_send_instance(),
wallet1.clone(),
mask1_i.clone(),
);
// Create wallet 2
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
execute_command(&app, test_dir, "wallet2", &client2, arg_vec.clone())?;
let config2 = initial_setup_wallet(test_dir, "wallet2");
let wallet2 = instantiate_wallet(config2.clone(), client2.clone(), "password", "default")?;
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
let (wallet2, mask2_i) =
instantiate_wallet(config2.clone(), client2.clone(), "password", "default")?;
wallet_proxy.add_wallet(
"wallet2",
client2.get_send_instance(),
wallet2.clone(),
mask2_i.clone(),
);
// Set the wallet proxy listener running
thread::spawn(move || {
@ -271,14 +288,22 @@ mod wallet_tests {
// Mine a bit into wallet 1 so we have something to send
// (TODO: Be able to stop listeners so we can test this better)
let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
grin_wallet_controller::controller::owner_single_use(wallet1.clone(), |api| {
api.set_active_account("mining")?;
let (wallet1, mask1_i) =
instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
let mask1 = (&mask1_i).as_ref();
grin_wallet_controller::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.set_active_account(m, "mining")?;
Ok(())
})?;
let mut bh = 10u64;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), bh as usize, false);
let _ = test_framework::award_blocks_to_wallet(
&chain,
wallet1.clone(),
mask1,
bh as usize,
false,
);
let very_long_message = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
@ -342,18 +367,20 @@ mod wallet_tests {
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
bh += 1;
let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
let (wallet1, mask1_i) =
instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
let mask1 = (&mask1_i).as_ref();
// Check our transaction log, should have 10 entries
grin_wallet_controller::controller::owner_single_use(wallet1.clone(), |api| {
api.set_active_account("mining")?;
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
grin_wallet_controller::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.set_active_account(m, "mining")?;
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
assert!(refreshed);
assert_eq!(txs.len(), bh as usize);
Ok(())
})?;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 10, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 10, false);
bh += 10;
// update info for each
@ -364,10 +391,13 @@ mod wallet_tests {
execute_command(&app, test_dir, "wallet2", &client1, arg_vec)?;
// check results in wallet 2
let wallet2 = instantiate_wallet(config2.clone(), client2.clone(), "password", "default")?;
grin_wallet_controller::controller::owner_single_use(wallet2.clone(), |api| {
api.set_active_account("account_1")?;
let (_, wallet1_info) = api.retrieve_summary_info(true, 1)?;
let (wallet2, mask2_i) =
instantiate_wallet(config2.clone(), client2.clone(), "password", "default")?;
let mask2 = (&mask2_i).as_ref();
grin_wallet_controller::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
api.set_active_account(m, "account_1")?;
let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.amount_currently_spendable, 10_000_000_000);
Ok(())
@ -419,11 +449,13 @@ mod wallet_tests {
bh += 1;
// Check our transaction log, should have bh entries + one for the self receive
let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
let (wallet1, mask1_i) =
instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
let mask1 = (&mask1_i).as_ref();
grin_wallet_controller::controller::owner_single_use(wallet1.clone(), |api| {
api.set_active_account("mining")?;
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
grin_wallet_controller::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.set_active_account(m, "mining")?;
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
assert!(refreshed);
assert_eq!(txs.len(), bh as usize + 1);
Ok(())
@ -453,11 +485,13 @@ mod wallet_tests {
bh += 1;
// Check our transaction log, should have bh entries + 2 for the self receives
let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
let (wallet1, mask1_i) =
instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?;
let mask1 = (&mask1_i).as_ref();
grin_wallet_controller::controller::owner_single_use(wallet1.clone(), |api| {
api.set_active_account("mining")?;
let (refreshed, txs) = api.retrieve_txs(true, None, None)?;
grin_wallet_controller::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
api.set_active_account(m, "mining")?;
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
assert!(refreshed);
assert_eq!(txs.len(), bh as usize + 2);
Ok(())
@ -559,7 +593,7 @@ mod wallet_tests {
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
// bit more mining
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 5, false);
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 5, false);
//bh += 5;
// txs and outputs (mostly spit out for a visual in test logs)
@ -591,9 +625,9 @@ mod wallet_tests {
// get tx output via -tx parameter
let mut tx_id = "".to_string();
grin_wallet_controller::controller::owner_single_use(wallet2.clone(), |api| {
api.set_active_account("default")?;
let (_, txs) = api.retrieve_txs(true, None, None)?;
grin_wallet_controller::controller::owner_single_use(wallet2.clone(), mask2, |api, m| {
api.set_active_account(m, "default")?;
let (_, txs) = api.retrieve_txs(m, true, None, None)?;
let some_tx_id = txs[0].tx_slate_id.clone();
assert!(some_tx_id.is_some());
tx_id = some_tx_id.unwrap().to_hyphenated().to_string().clone();

View file

@ -41,12 +41,12 @@ grin_api = { git = "https://github.com/mimblewimble/grin", branch = "master" }
grin_store = { git = "https://github.com/mimblewimble/grin", branch = "master" }
# For local testing
#grin_core = { path = "../../grin/core", version= "2.0.0-beta.2"}
#grin_keychain = { path = "../../grin/keychain", version= "2.0.0-beta.2"}
#grin_chain = { path = "../../grin/chain", version= "2.0.0-beta.2"}
#grin_util = { path = "../../grin/util", version= "2.0.0-beta.2"}
#grin_api = { path = "../../grin/api", version= "2.0.0-beta.2"}
#grin_store = { path = "../../grin/store", version= "2.0.0-beta.2"}
#grin_core = { path = "../../grin/core", version= "2.0.1-beta.1"}
#grin_keychain = { path = "../../grin/keychain", version= "2.0.1-beta.1"}
#grin_chain = { path = "../../grin/chain", version= "2.0.1-beta.1"}
#grin_util = { path = "../../grin/util", version= "2.0.1-beta.1"}
#grin_api = { path = "../../grin/api", version= "2.0.1-beta.1"}
#grin_store = { path = "../../grin/store", version= "2.0.1-beta.1"}
[dev-dependencies]
pretty_assertions = "0.5.1"