mirror of
https://github.com/jwinterm/grin-wallet.git
synced 2026-10-17 15:21:39 +02:00
Add init_api_secure function (#206)
* adding initial version of init_secure_api * rustfmt * fix ECDH algo * rustfmt * trying to figure out best way of doing encryption * refactor secure requests and responses into json-rpc responses, with base64 payload for encrypted messages * rustfmt * return proper errors from encrypted api, include tests covering encrypted API error cases * rustfmt * add test for normal error (unencrypted) * rustfmt * change ports for test, add foreign listener to V2 sanity tests, add ability to select owner api port via command line * rustfmt * turn it to 11 * explicit teardown after rpc tests * update tests with explicit teardowns * update tests to perform explicit teardown * fix warnings, ensure all tests teardown * log output to diagnose CI windows build failures * disable owner api doctests on windows * rustfmt
This commit is contained in:
@@ -29,7 +29,7 @@ tokio-retry = "0.1"
|
||||
uuid = { version = "0.7", features = ["serde", "v4"] }
|
||||
url = "1.7.0"
|
||||
chrono = { version = "0.4.4", features = ["serde"] }
|
||||
easy-jsonrpc = "0.5.1"
|
||||
easy-jsonrpc-mw = "0.5.3"
|
||||
lazy_static = "1"
|
||||
|
||||
grin_wallet_util = { path = "../util", version = "2.1.0-beta.1" }
|
||||
|
||||
@@ -32,15 +32,22 @@ use serde_json;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::apiwallet::{Foreign, ForeignCheckMiddlewareFn, ForeignRpc, Owner, OwnerRpc, OwnerRpcS};
|
||||
use easy_jsonrpc;
|
||||
use easy_jsonrpc::{Handler, MaybeReply};
|
||||
use crate::apiwallet::{
|
||||
EncryptedRequest, EncryptedResponse, EncryptionErrorResponse, Foreign,
|
||||
ForeignCheckMiddlewareFn, ForeignRpc, Owner, OwnerRpc, OwnerRpcS,
|
||||
};
|
||||
use easy_jsonrpc_mw;
|
||||
use easy_jsonrpc_mw::{Handler, MaybeReply};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref GRIN_OWNER_BASIC_REALM: HeaderValue =
|
||||
HeaderValue::from_str("Basic realm=GrinOwnerAPI").unwrap();
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref OWNER_API_SHARED_KEY: Arc<Mutex<Option<SecretKey>>> = Arc::new(Mutex::new(None));
|
||||
}
|
||||
|
||||
fn check_middleware(
|
||||
name: ForeignCheckMiddlewareFn,
|
||||
node_version_info: Option<NodeVersionInfo>,
|
||||
@@ -138,7 +145,6 @@ where
|
||||
}
|
||||
|
||||
let api_handler_v2 = OwnerAPIHandlerV2::new(wallet.clone());
|
||||
|
||||
let api_handler_v3 = OwnerAPIHandlerV3::new(wallet.clone());
|
||||
|
||||
router
|
||||
@@ -295,6 +301,106 @@ where
|
||||
pub wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
|
||||
}
|
||||
|
||||
pub struct OwnerV3Helpers;
|
||||
|
||||
impl OwnerV3Helpers {
|
||||
/// Checks whether a request is to init the secure API
|
||||
pub fn is_init_secure_api(val: &serde_json::Value) -> bool {
|
||||
if let Some(m) = val["method"].as_str() {
|
||||
match m {
|
||||
"init_secure_api" => true,
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether a request is an encrypted request
|
||||
pub fn is_encrypted_request(val: &serde_json::Value) -> bool {
|
||||
if let Some(m) = val["method"].as_str() {
|
||||
match m {
|
||||
"encrypted_request_v3" => true,
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// whether encryption is enabled
|
||||
pub fn encryption_enabled() -> bool {
|
||||
let share_key_ref = OWNER_API_SHARED_KEY.lock();
|
||||
share_key_ref.is_some()
|
||||
}
|
||||
|
||||
/// If incoming is an encrypted request, check there is a shared key,
|
||||
/// Otherwise return an error value
|
||||
pub fn check_encryption_started() -> Result<(), serde_json::Value> {
|
||||
match OwnerV3Helpers::encryption_enabled() {
|
||||
true => Ok(()),
|
||||
false => Err(EncryptionErrorResponse::new(
|
||||
1,
|
||||
-32001,
|
||||
"Encryption must be enabled. Please call 'init_secure_api` first",
|
||||
)
|
||||
.as_json_value()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the statically held owner API shared key
|
||||
pub fn update_owner_api_shared_key(val: &serde_json::Value, new_key: Option<SecretKey>) {
|
||||
if let Some(_) = val["result"]["Ok"].as_str() {
|
||||
let mut share_key_ref = OWNER_API_SHARED_KEY.lock();
|
||||
*share_key_ref = new_key;
|
||||
}
|
||||
}
|
||||
|
||||
/// Decrypt an encrypted request
|
||||
pub fn decrypt_request(
|
||||
req: &serde_json::Value,
|
||||
) -> Result<(u32, serde_json::Value), serde_json::Value> {
|
||||
let share_key_ref = OWNER_API_SHARED_KEY.lock();
|
||||
let shared_key = share_key_ref.as_ref().unwrap();
|
||||
let enc_req: EncryptedRequest = serde_json::from_value(req.clone()).map_err(|e| {
|
||||
EncryptionErrorResponse::new(
|
||||
1,
|
||||
-32002,
|
||||
&format!("Encrypted request format error: {}", e),
|
||||
)
|
||||
.as_json_value()
|
||||
})?;
|
||||
let id = enc_req.id;
|
||||
let res = enc_req.decrypt(&shared_key).map_err(|e| {
|
||||
EncryptionErrorResponse::new(1, -32002, &format!("Decryption error: {}", e.kind()))
|
||||
.as_json_value()
|
||||
})?;
|
||||
Ok((id, res))
|
||||
}
|
||||
|
||||
/// Encrypt a response
|
||||
pub fn encrypt_response(
|
||||
id: u32,
|
||||
res: &serde_json::Value,
|
||||
) -> Result<serde_json::Value, serde_json::Value> {
|
||||
let share_key_ref = OWNER_API_SHARED_KEY.lock();
|
||||
let shared_key = share_key_ref.as_ref().unwrap();
|
||||
let enc_res = EncryptedResponse::from_json(id, res, &shared_key).map_err(|e| {
|
||||
EncryptionErrorResponse::new(1, -32003, &format!("EncryptionError: {}", e.kind()))
|
||||
.as_json_value()
|
||||
})?;
|
||||
let res = enc_res.as_json_value().map_err(|e| {
|
||||
EncryptionErrorResponse::new(
|
||||
1,
|
||||
-32002,
|
||||
&format!("Encrypted response format error: {}", e),
|
||||
)
|
||||
.as_json_value()
|
||||
})?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, C, K> OwnerAPIHandlerV3<L, C, K>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K>,
|
||||
@@ -314,9 +420,47 @@ where
|
||||
api: Owner<'static, L, C, K>,
|
||||
) -> Box<dyn Future<Item = serde_json::Value, Error = Error> + Send> {
|
||||
Box::new(parse_body(req).and_then(move |val: serde_json::Value| {
|
||||
let mut val = val;
|
||||
let owner_api_s = &api as &dyn OwnerRpcS;
|
||||
let mut is_init_secure_api = OwnerV3Helpers::is_init_secure_api(&val);
|
||||
let mut was_encrypted = false;
|
||||
let mut encrypted_req_id = 0;
|
||||
if !is_init_secure_api {
|
||||
if let Err(v) = OwnerV3Helpers::check_encryption_started() {
|
||||
return ok(v);
|
||||
}
|
||||
let res = OwnerV3Helpers::decrypt_request(&val);
|
||||
match res {
|
||||
Err(e) => return ok(e),
|
||||
Ok(v) => {
|
||||
encrypted_req_id = v.0;
|
||||
val = v.1;
|
||||
}
|
||||
}
|
||||
was_encrypted = true;
|
||||
}
|
||||
// check again, in case it was an encrypted call to init_secure_api
|
||||
is_init_secure_api = OwnerV3Helpers::is_init_secure_api(&val);
|
||||
match owner_api_s.handle_request(val) {
|
||||
MaybeReply::Reply(r) => ok(r),
|
||||
MaybeReply::Reply(mut r) => {
|
||||
let unencrypted_intercept = r.clone();
|
||||
if was_encrypted {
|
||||
let res = OwnerV3Helpers::encrypt_response(encrypted_req_id, &r);
|
||||
r = match res {
|
||||
Ok(v) => v,
|
||||
Err(v) => return ok(v),
|
||||
}
|
||||
}
|
||||
// intercept init_secure_api response (after encryption,
|
||||
// in case it was an encrypted call to 'init_api_secure')
|
||||
if is_init_secure_api {
|
||||
OwnerV3Helpers::update_owner_api_shared_key(
|
||||
&unencrypted_intercept,
|
||||
api.shared_key.lock().clone(),
|
||||
);
|
||||
}
|
||||
ok(r)
|
||||
}
|
||||
MaybeReply::DontReply => {
|
||||
// Since it's http, we need to return something. We return [] because jsonrpc
|
||||
// clients will parse it as an empty batch response.
|
||||
|
||||
@@ -30,12 +30,10 @@ use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{create_wallet_proxy, setup};
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// Various tests on accounts within the same wallet
|
||||
fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
setup(test_dir);
|
||||
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
@@ -265,7 +263,9 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
#[test]
|
||||
fn accounts() {
|
||||
let test_dir = "test_output/accounts";
|
||||
setup(test_dir);
|
||||
if let Err(e) = accounts_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ use util::ZeroingString;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{create_wallet_proxy, setup};
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
macro_rules! send_to_dest {
|
||||
($a:expr, $m: expr, $b:expr, $c:expr, $d:expr) => {
|
||||
@@ -48,8 +48,6 @@ macro_rules! wallet_info {
|
||||
|
||||
/// Various tests on checking functionality
|
||||
fn check_repair_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
setup(test_dir);
|
||||
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
@@ -753,12 +751,15 @@ fn check_repair() {
|
||||
if let Err(e) = check_repair_impl(test_dir) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_wallets_one_seed() {
|
||||
let test_dir = "test_output/two_wallets_one_seed";
|
||||
setup(test_dir);
|
||||
if let Err(e) = two_wallets_one_seed_impl(test_dir) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ pub fn create_local_wallet(
|
||||
Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
WalletInst<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
@@ -114,7 +114,7 @@ pub fn create_local_wallet(
|
||||
) {
|
||||
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
|
||||
as Box<
|
||||
WalletInst<
|
||||
dyn WalletInst<
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
@@ -130,6 +130,7 @@ pub fn create_local_wallet(
|
||||
(Arc::new(Mutex::new(wallet)), mask)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn open_local_wallet(
|
||||
test_dir: &str,
|
||||
name: &str,
|
||||
@@ -139,7 +140,7 @@ pub fn open_local_wallet(
|
||||
Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
WalletInst<
|
||||
dyn WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
@@ -152,7 +153,7 @@ pub fn open_local_wallet(
|
||||
) {
|
||||
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
|
||||
as Box<
|
||||
WalletInst<
|
||||
dyn WalletInst<
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
|
||||
@@ -31,12 +31,10 @@ use serde_json;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{create_wallet_proxy, setup};
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
setup(test_dir);
|
||||
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
@@ -224,7 +222,9 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
|
||||
#[test]
|
||||
fn wallet_file_exchange() {
|
||||
let test_dir = "test_output/file_exchange";
|
||||
setup(test_dir);
|
||||
if let Err(e) = file_exchange_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
+168
-175
@@ -31,193 +31,186 @@ use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
{
|
||||
setup(test_dir);
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&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,
|
||||
true
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
create_wallet_and_add!(
|
||||
client1,
|
||||
wallet1,
|
||||
mask1_i,
|
||||
test_dir,
|
||||
"wallet1",
|
||||
None,
|
||||
&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,
|
||||
true
|
||||
);
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// few values to keep things shorter
|
||||
let reward = core::consensus::REWARD;
|
||||
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
let mut bh = 10u64;
|
||||
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(), 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);
|
||||
Ok(())
|
||||
})?;
|
||||
// few values to keep things shorter
|
||||
let reward = core::consensus::REWARD;
|
||||
|
||||
let mut slate = Slate::blank(2);
|
||||
// add some accounts
|
||||
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
|
||||
api.create_account_path(m, "mining")?;
|
||||
api.create_account_path(m, "listener")?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
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(m, args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
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(), 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(), mask1, |api, m| {
|
||||
api.post_tx(m, &slate.tx, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
bh += 1;
|
||||
|
||||
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(), 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!(
|
||||
"last confirmed height: {}, bh: {}",
|
||||
wallet2_info.last_confirmed_height, bh
|
||||
);
|
||||
assert!(refreshed);
|
||||
assert_eq!(wallet2_info.amount_currently_spendable, slate.amount);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Check transaction log for wallet 1, ensure only 1 entry
|
||||
// exists
|
||||
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!(
|
||||
"Wallet 1: last confirmed height: {}, bh: {}",
|
||||
wallet1_info.last_confirmed_height, bh
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Test self-sending
|
||||
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(m, args)?;
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
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(), 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(), mask1, 3, false);
|
||||
//bh += 3;
|
||||
|
||||
// let logging finish
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
// Get some mining done
|
||||
{
|
||||
wallet_inst!(wallet1, w);
|
||||
w.set_parent_key_id_by_name("mining")?;
|
||||
}
|
||||
let mut bh = 10u64;
|
||||
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(), 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);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let mut slate = Slate::blank(2);
|
||||
|
||||
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(m, args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| {
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
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(), 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(), mask1, |api, m| {
|
||||
api.post_tx(m, &slate.tx, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
bh += 1;
|
||||
|
||||
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(), 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!(
|
||||
"last confirmed height: {}, bh: {}",
|
||||
wallet2_info.last_confirmed_height, bh
|
||||
);
|
||||
assert!(refreshed);
|
||||
assert_eq!(wallet2_info.amount_currently_spendable, slate.amount);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Check transaction log for wallet 1, ensure only 1 entry
|
||||
// exists
|
||||
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!(
|
||||
"Wallet 1: last confirmed height: {}, bh: {}",
|
||||
wallet1_info.last_confirmed_height, bh
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Test self-sending
|
||||
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(m, args)?;
|
||||
// Wallet 1 receives the invoice transaction
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
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(), 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(), mask1, 3, false);
|
||||
//bh += 3;
|
||||
|
||||
// let logging finish
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
|
||||
clean_output_dir(test_dir);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_invoice_tx() -> Result<(), libwallet::Error> {
|
||||
let test_dir = "test_output/invoice_tx";
|
||||
invoice_tx_impl(test_dir)
|
||||
setup(test_dir);
|
||||
invoice_tx_impl(test_dir)?;
|
||||
clean_output_dir(test_dir);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -28,12 +28,10 @@ use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{create_wallet_proxy, setup};
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
setup(test_dir);
|
||||
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
@@ -257,7 +255,9 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
#[test]
|
||||
fn wallet_file_repost() {
|
||||
let test_dir = "test_output/file_repost";
|
||||
setup(test_dir);
|
||||
if let Err(e) = file_repost_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{create_wallet_proxy, setup};
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
fn restore_wallet(base_dir: &'static str, wallet_dir: &str) -> Result<(), libwallet::Error> {
|
||||
let source_seed = format!("{}/{}/wallet_data/wallet.seed", base_dir, wallet_dir);
|
||||
@@ -196,8 +196,6 @@ fn compare_wallet_restore(
|
||||
/// Build up 2 wallets, perform a few transactions on them
|
||||
/// Then attempt to restore them in separate directories and check contents are the same
|
||||
fn setup_restore(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
setup(test_dir);
|
||||
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
@@ -417,6 +415,7 @@ fn perform_restore(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
#[test]
|
||||
fn wallet_restore() {
|
||||
let test_dir = "test_output/wallet_restore";
|
||||
setup(test_dir);
|
||||
if let Err(e) = setup_restore(test_dir) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
@@ -425,4 +424,5 @@ fn wallet_restore() {
|
||||
}
|
||||
// let logging finish
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
@@ -27,11 +27,10 @@ use std::time::Duration;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{create_wallet_proxy, setup};
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
setup(test_dir);
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
@@ -138,7 +137,9 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
#[test]
|
||||
fn wallet_self_send() {
|
||||
let test_dir = "test_output/self_send";
|
||||
setup(test_dir);
|
||||
if let Err(e) = self_send_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
@@ -28,13 +28,12 @@ use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
mod common;
|
||||
use common::{create_wallet_proxy, setup};
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// Exercises the Transaction API fully with a test NodeClient operating
|
||||
/// directly on a chain instance
|
||||
/// Callable with any type of wallet
|
||||
fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
setup(test_dir);
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
@@ -350,7 +349,6 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
|
||||
/// Test rolling back transactions and outputs when a transaction is never
|
||||
/// posted to a chain
|
||||
fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
setup(test_dir);
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
@@ -528,15 +526,19 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
#[test]
|
||||
fn db_wallet_basic_transaction_api() {
|
||||
let test_dir = "test_output/basic_transaction_api";
|
||||
setup(test_dir);
|
||||
if let Err(e) = basic_transaction_api(test_dir) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn db_wallet_tx_rollback() {
|
||||
let test_dir = "test_output/tx_rollback";
|
||||
setup(test_dir);
|
||||
if let Err(e) = tx_rollback(test_dir) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user