mirror of
https://github.com/mimblewimble/grin-wallet.git
synced 2025-01-20 11:01:09 +03:00
Test refactoring and additions to support API Testing (#205)
* move wallet tests into integration module in advance of further test creation, add v3 API * rustfmt * fix return value from incorrect mask supplied * rustfmt * rework send request call some * rustfmt * more refactoring, turn proxy startup + wallet init into macros * rustfmt * move sample requests into subdirectories * add missing wallet files, change gitignore
This commit is contained in:
parent
f8c316a351
commit
62d976f9ef
16 changed files with 1011 additions and 671 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -10,5 +10,4 @@ target
|
|||
grin.log
|
||||
wallet.seed
|
||||
test_output
|
||||
wallet*
|
||||
.idea/
|
||||
|
|
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -765,6 +765,7 @@ dependencies = [
|
|||
"built 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"easy-jsonrpc 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"grin_wallet_api 2.1.0-beta.1",
|
||||
|
@ -778,6 +779,10 @@ dependencies = [
|
|||
"prettytable-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rpassword 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -40,3 +40,10 @@ grin_wallet_util = { path = "./util", version = "2.1.0-beta.1" }
|
|||
|
||||
[build-dependencies]
|
||||
built = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
url = "1.7.2"
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
serde_json = "1"
|
||||
easy-jsonrpc = "0.5.1"
|
||||
|
|
|
@ -32,7 +32,7 @@ use serde_json;
|
|||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::apiwallet::{Foreign, ForeignCheckMiddlewareFn, ForeignRpc, Owner, OwnerRpc};
|
||||
use crate::apiwallet::{Foreign, ForeignCheckMiddlewareFn, ForeignRpc, Owner, OwnerRpc, OwnerRpcS};
|
||||
use easy_jsonrpc;
|
||||
use easy_jsonrpc::{Handler, MaybeReply};
|
||||
|
||||
|
@ -126,8 +126,6 @@ where
|
|||
C: NodeClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
let api_handler_v2 = OwnerAPIHandlerV2::new(wallet.clone());
|
||||
|
||||
let mut router = Router::new();
|
||||
if api_secret.is_some() {
|
||||
let api_basic_auth =
|
||||
|
@ -139,10 +137,18 @@ where
|
|||
router.add_middleware(basic_auth_middleware);
|
||||
}
|
||||
|
||||
let api_handler_v2 = OwnerAPIHandlerV2::new(wallet.clone());
|
||||
|
||||
let api_handler_v3 = OwnerAPIHandlerV3::new(wallet.clone());
|
||||
|
||||
router
|
||||
.add_route("/v2/owner", Arc::new(api_handler_v2))
|
||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||
|
||||
router
|
||||
.add_route("/v3/owner", Arc::new(api_handler_v3))
|
||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||
|
||||
// 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);
|
||||
|
@ -277,6 +283,79 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// V3 API Handler/Wrapper for owner functions, which include a secure
|
||||
/// mode + lifecycle functions
|
||||
pub struct OwnerAPIHandlerV3<L, C, K>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
/// Wallet instance
|
||||
pub wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
|
||||
}
|
||||
|
||||
impl<L, C, K> OwnerAPIHandlerV3<L, C, K>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K>,
|
||||
C: NodeClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
/// Create a new owner API handler for GET methods
|
||||
pub fn new(
|
||||
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
|
||||
) -> OwnerAPIHandlerV3<L, C, K> {
|
||||
OwnerAPIHandlerV3 { wallet }
|
||||
}
|
||||
|
||||
fn call_api(
|
||||
&self,
|
||||
req: Request<Body>,
|
||||
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 owner_api_s = &api as &dyn OwnerRpcS;
|
||||
match owner_api_s.handle_request(val) {
|
||||
MaybeReply::Reply(r) => 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.
|
||||
ok(serde_json::json!([]))
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn handle_post_request(&self, req: Request<Body>) -> WalletResponseFuture {
|
||||
let api = Owner::new(self.wallet.clone());
|
||||
Box::new(
|
||||
self.call_api(req, api)
|
||||
.and_then(|resp| ok(json_response_pretty(&resp))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, C, K> api::Handler for OwnerAPIHandlerV3<L, C, K>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(
|
||||
self.handle_post_request(req)
|
||||
.and_then(|r| ok(r))
|
||||
.or_else(|e| {
|
||||
error!("Request Error: {:?}", e);
|
||||
ok(create_error_response(e))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn options(&self, _req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(ok(create_ok_response("{}")))
|
||||
}
|
||||
}
|
||||
/// V2 API Handler/Wrapper for foreign functions
|
||||
pub struct ForeignAPIHandlerV2<L, C, K>
|
||||
where
|
||||
|
|
|
@ -84,7 +84,7 @@ where
|
|||
|
||||
let mut validated = false;
|
||||
if refresh_from_node {
|
||||
validated = update_outputs(w, keychain_mask, false);
|
||||
validated = update_outputs(w, keychain_mask, false)?;
|
||||
}
|
||||
|
||||
Ok((
|
||||
|
@ -116,7 +116,7 @@ where
|
|||
|
||||
let mut validated = false;
|
||||
if refresh_from_node {
|
||||
validated = update_outputs(w, keychain_mask, false);
|
||||
validated = update_outputs(w, keychain_mask, false)?;
|
||||
}
|
||||
|
||||
Ok((
|
||||
|
@ -141,7 +141,7 @@ where
|
|||
|
||||
let mut validated = false;
|
||||
if refresh_from_node {
|
||||
validated = update_outputs(w, keychain_mask, false);
|
||||
validated = update_outputs(w, keychain_mask, false)?;
|
||||
}
|
||||
|
||||
let wallet_info = updater::retrieve_info(&mut *w, &parent_key_id, minimum_confirmations)?;
|
||||
|
@ -418,7 +418,7 @@ where
|
|||
K: Keychain + 'a,
|
||||
{
|
||||
let parent_key_id = w.parent_key_id();
|
||||
if !update_outputs(w, keychain_mask, false) {
|
||||
if !update_outputs(w, keychain_mask, false)? {
|
||||
return Err(ErrorKind::TransactionCancellationError(
|
||||
"Can't contact running Grin node. Not Cancelling.",
|
||||
))?;
|
||||
|
@ -489,7 +489,7 @@ where
|
|||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
update_outputs(w, keychain_mask, true);
|
||||
update_outputs(w, keychain_mask, true)?;
|
||||
w.check_repair(keychain_mask, delete_unconfirmed)
|
||||
}
|
||||
|
||||
|
@ -528,7 +528,7 @@ fn update_outputs<'a, T: ?Sized, C, K>(
|
|||
w: &mut T,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
update_all: bool,
|
||||
) -> bool
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
|
@ -536,7 +536,12 @@ where
|
|||
{
|
||||
let parent_key_id = w.parent_key_id();
|
||||
match updater::refresh_outputs(&mut *w, keychain_mask, &parent_key_id, update_all) {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
Ok(_) => Ok(true),
|
||||
Err(e) => {
|
||||
if let ErrorKind::InvalidKeychainMask = e.kind() {
|
||||
return Err(e);
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,651 +0,0 @@
|
|||
// Copyright 2018 The Grin Developers
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Test wallet command line works as expected
|
||||
#[cfg(test)]
|
||||
mod wallet_tests {
|
||||
use clap;
|
||||
use grin_wallet_util::grin_util as util;
|
||||
|
||||
use grin_wallet_impls::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
|
||||
use clap::{App, ArgMatches};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::{env, fs};
|
||||
use util::{Mutex, ZeroingString};
|
||||
|
||||
use grin_wallet_config::{GlobalWalletConfig, WalletConfig, GRIN_WALLET_DIR};
|
||||
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl};
|
||||
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;
|
||||
|
||||
fn clean_output_dir(test_dir: &str) {
|
||||
let _ = fs::remove_dir_all(test_dir);
|
||||
}
|
||||
|
||||
fn setup(test_dir: &str) {
|
||||
util::init_test_logger();
|
||||
clean_output_dir(test_dir);
|
||||
global::set_mining_mode(ChainTypes::AutomatedTesting);
|
||||
}
|
||||
|
||||
/// Create a wallet config file in the given current directory
|
||||
pub fn config_command_wallet(
|
||||
dir_name: &str,
|
||||
wallet_name: &str,
|
||||
) -> Result<(), grin_wallet_controller::Error> {
|
||||
let mut current_dir;
|
||||
let mut default_config = GlobalWalletConfig::default();
|
||||
current_dir = env::current_dir().unwrap_or_else(|e| {
|
||||
panic!("Error creating config file: {}", e);
|
||||
});
|
||||
current_dir.push(dir_name);
|
||||
current_dir.push(wallet_name);
|
||||
let _ = fs::create_dir_all(current_dir.clone());
|
||||
let mut config_file_name = current_dir.clone();
|
||||
config_file_name.push("grin-wallet.toml");
|
||||
if config_file_name.exists() {
|
||||
return Err(grin_wallet_controller::ErrorKind::ArgumentError(
|
||||
"grin-wallet.toml already exists in the target directory. Please remove it first"
|
||||
.to_owned(),
|
||||
))?;
|
||||
}
|
||||
default_config.update_paths(¤t_dir);
|
||||
default_config
|
||||
.write_to_file(config_file_name.to_str().unwrap())
|
||||
.unwrap_or_else(|e| {
|
||||
panic!("Error creating config file: {}", e);
|
||||
});
|
||||
|
||||
println!(
|
||||
"File {} configured and created",
|
||||
config_file_name.to_str().unwrap(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles setup and detection of paths for wallet
|
||||
pub fn initial_setup_wallet(dir_name: &str, wallet_name: &str) -> WalletConfig {
|
||||
let mut current_dir;
|
||||
current_dir = env::current_dir().unwrap_or_else(|e| {
|
||||
panic!("Error creating config file: {}", e);
|
||||
});
|
||||
current_dir.push(dir_name);
|
||||
current_dir.push(wallet_name);
|
||||
let _ = fs::create_dir_all(current_dir.clone());
|
||||
let mut config_file_name = current_dir.clone();
|
||||
config_file_name.push("grin-wallet.toml");
|
||||
GlobalWalletConfig::new(config_file_name.to_str().unwrap())
|
||||
.unwrap()
|
||||
.members
|
||||
.unwrap()
|
||||
.wallet
|
||||
}
|
||||
|
||||
fn get_wallet_subcommand<'a>(
|
||||
wallet_dir: &str,
|
||||
wallet_name: &str,
|
||||
args: ArgMatches<'a>,
|
||||
) -> ArgMatches<'a> {
|
||||
match args.subcommand() {
|
||||
("init", Some(init_args)) => {
|
||||
// wallet init command should spit out its config file then continue
|
||||
// (if desired)
|
||||
if init_args.is_present("here") {
|
||||
let _ = config_command_wallet(wallet_dir, wallet_name);
|
||||
}
|
||||
init_args.to_owned()
|
||||
}
|
||||
_ => ArgMatches::new(),
|
||||
}
|
||||
}
|
||||
//
|
||||
// Helper to create an instance of the LMDB wallet
|
||||
fn instantiate_wallet(
|
||||
mut wallet_config: WalletConfig,
|
||||
node_client: LocalWalletClient,
|
||||
passphrase: &str,
|
||||
account: &str,
|
||||
) -> Result<
|
||||
(
|
||||
Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
Option<SecretKey>,
|
||||
),
|
||||
grin_wallet_controller::Error,
|
||||
> {
|
||||
wallet_config.chain_type = None;
|
||||
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(node_client).unwrap())
|
||||
as Box<
|
||||
WalletInst<
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>;
|
||||
let lc = wallet.lc_provider().unwrap();
|
||||
// legacy hack to avoid the need for changes in existing grin-wallet.toml files
|
||||
// remove `wallet_data` from end of path as
|
||||
// new lifecycle provider assumes grin_wallet.toml is in root of data directory
|
||||
let mut top_level_wallet_dir = PathBuf::from(wallet_config.clone().data_file_dir);
|
||||
if top_level_wallet_dir.ends_with(GRIN_WALLET_DIR) {
|
||||
top_level_wallet_dir.pop();
|
||||
wallet_config.data_file_dir = top_level_wallet_dir.to_str().unwrap().into();
|
||||
}
|
||||
lc.set_wallet_directory(&wallet_config.data_file_dir);
|
||||
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)), keychain_mask))
|
||||
}
|
||||
|
||||
fn execute_command(
|
||||
app: &App,
|
||||
test_dir: &str,
|
||||
wallet_name: &str,
|
||||
client: &LocalWalletClient,
|
||||
arg_vec: Vec<&str>,
|
||||
) -> Result<String, grin_wallet_controller::Error> {
|
||||
let args = app.clone().get_matches_from(arg_vec);
|
||||
let _ = get_wallet_subcommand(test_dir, wallet_name, args.clone());
|
||||
let mut config = initial_setup_wallet(test_dir, wallet_name);
|
||||
//unset chain type so it doesn't get reset
|
||||
config.chain_type = None;
|
||||
wallet_args::wallet_command(&args, config.clone(), client.clone())
|
||||
}
|
||||
|
||||
/// command line tests
|
||||
fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::Error> {
|
||||
setup(test_dir);
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy: WalletProxy<
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
> = WalletProxy::new(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
|
||||
// load app yaml. If it don't exist, just say so and exit
|
||||
let yml = load_yaml!("../grin-wallet.yml");
|
||||
let app = App::from_yaml(yml);
|
||||
|
||||
// wallet init
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "init", "-h"];
|
||||
// should create new wallet file
|
||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec.clone())?;
|
||||
|
||||
// trying to init twice - should fail
|
||||
assert!(execute_command(&app, test_dir, "wallet1", &client1, arg_vec.clone()).is_err());
|
||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||
|
||||
// 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, 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, 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 || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// Create some accounts in wallet 1
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "account", "-c", "mining"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"account",
|
||||
"-c",
|
||||
"account_1",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// Create some accounts in wallet 2
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"account",
|
||||
"-c",
|
||||
"account_1",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec.clone())?;
|
||||
// already exists
|
||||
assert!(execute_command(&app, test_dir, "wallet2", &client2, arg_vec).is_err());
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"account",
|
||||
"-c",
|
||||
"account_2",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// let's see those accounts
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "account"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// let's see those accounts
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "account"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// 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, 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(),
|
||||
mask1,
|
||||
bh as usize,
|
||||
false,
|
||||
);
|
||||
|
||||
let very_long_message = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
This part should all be truncated";
|
||||
|
||||
// Update info and check
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "info"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// try a file exchange
|
||||
let file_name = format!("{}/tx1.part_tx", test_dir);
|
||||
let response_file_name = format!("{}/tx1.part_tx.response", test_dir);
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
very_long_message,
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"account_1",
|
||||
"receive",
|
||||
"-i",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Thanks, Yeast!",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec.clone())?;
|
||||
|
||||
// shouldn't be allowed to receive twice
|
||||
assert!(execute_command(&app, test_dir, "wallet2", &client2, arg_vec).is_err());
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"finalize",
|
||||
"-i",
|
||||
&response_file_name,
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
bh += 1;
|
||||
|
||||
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(), 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(), mask1, 10, false);
|
||||
bh += 10;
|
||||
|
||||
// update info for each
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "info"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "account_1", "info"];
|
||||
execute_command(&app, test_dir, "wallet2", &client1, arg_vec)?;
|
||||
|
||||
// check results in wallet 2
|
||||
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(())
|
||||
})?;
|
||||
|
||||
// Self-send to same account, using smallest strategy
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Love, Yeast, Smallest",
|
||||
"-s",
|
||||
"smallest",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"receive",
|
||||
"-i",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Thanks, Yeast!",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec.clone())?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"finalize",
|
||||
"-i",
|
||||
&response_file_name,
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
bh += 1;
|
||||
|
||||
// Check our transaction log, should have bh entries + one for the self receive
|
||||
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")?;
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(txs.len(), bh as usize + 1);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Try using the self-send method, splitting up outputs for the fun of it
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"self",
|
||||
"-d",
|
||||
"mining",
|
||||
"-g",
|
||||
"Self love",
|
||||
"-o",
|
||||
"3",
|
||||
"-s",
|
||||
"smallest",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
bh += 1;
|
||||
|
||||
// Check our transaction log, should have bh entries + 2 for the self receives
|
||||
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")?;
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(txs.len(), bh as usize + 2);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Another file exchange, don't send, but unlock with repair command
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Ain't sending",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "check", "-d"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// Another file exchange, cancel this time
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Ain't sending 2",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"cancel",
|
||||
"-i",
|
||||
"26",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// issue an invoice tx, wallet 2
|
||||
let file_name = format!("{}/invoice.slate", test_dir);
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"invoice",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Please give me your precious grins. Love, Yeast",
|
||||
"65",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
let output_file_name = format!("{}/invoice.slate.paid", test_dir);
|
||||
|
||||
// now pay the invoice tx, wallet 1
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-a",
|
||||
"mining",
|
||||
"-p",
|
||||
"password",
|
||||
"pay",
|
||||
"-i",
|
||||
&file_name,
|
||||
"-d",
|
||||
&output_file_name,
|
||||
"-g",
|
||||
"Here you go",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// and finalize, wallet 2
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"finalize",
|
||||
"-i",
|
||||
&output_file_name,
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// bit more mining
|
||||
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)
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "txs"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// message output (mostly spit out for a visual in test logs)
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"txs",
|
||||
"-i",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// txs and outputs (mostly spit out for a visual in test logs)
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "outputs"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "txs"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "outputs"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// get tx output via -tx parameter
|
||||
let mut tx_id = "".to_string();
|
||||
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();
|
||||
Ok(())
|
||||
})?;
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "txs", "-t", &tx_id[..]];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// let logging finish
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_command_line() {
|
||||
let test_dir = "target/test_output/command_line";
|
||||
if let Err(e) = command_line_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,12 +23,11 @@ use crate::core::global;
|
|||
use crate::util::init_logger;
|
||||
use clap::App;
|
||||
use grin_wallet_config as config;
|
||||
use grin_wallet_util::grin_api as api;
|
||||
use grin_wallet_util::grin_core as core;
|
||||
use grin_wallet_util::grin_util as util;
|
||||
use std::env;
|
||||
|
||||
mod cmd;
|
||||
use grin_wallet::cmd;
|
||||
|
||||
// include build information
|
||||
pub mod built_info {
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
// limitations under the License.
|
||||
|
||||
mod wallet;
|
||||
mod wallet_args;
|
||||
mod wallet_tests;
|
||||
pub mod wallet_args;
|
||||
|
||||
pub use self::wallet::wallet_command;
|
|
@ -51,7 +51,7 @@ pub fn wallet_command(wallet_args: &ArgMatches<'_>, config: GlobalWalletConfig)
|
|||
}
|
||||
// ... if node isn't available, allow offline functions
|
||||
|
||||
let res = wallet_args::wallet_command(wallet_args, wallet_config, node_client);
|
||||
let res = wallet_args::wallet_command(wallet_args, wallet_config, node_client, false);
|
||||
|
||||
// we need to give log output a chance to catch up before exiting
|
||||
thread::sleep(Duration::from_millis(100));
|
|
@ -582,6 +582,7 @@ pub fn parse_issue_invoice_args(
|
|||
|
||||
pub fn parse_process_invoice_args(
|
||||
args: &ArgMatches,
|
||||
prompt: bool,
|
||||
) -> Result<command::ProcessInvoiceArgs, ParseError> {
|
||||
// TODO: display and prompt for confirmation of what we're doing
|
||||
// message
|
||||
|
@ -636,7 +637,7 @@ pub fn parse_process_invoice_args(
|
|||
// file input only
|
||||
let tx_file = parse_required(args, "input")?;
|
||||
|
||||
if cfg!(not(test)) {
|
||||
if prompt {
|
||||
// Now we need to prompt the user whether they want to do this,
|
||||
// which requires reading the slate
|
||||
|
||||
|
@ -754,6 +755,7 @@ pub fn wallet_command<C>(
|
|||
wallet_args: &ArgMatches,
|
||||
mut wallet_config: WalletConfig,
|
||||
mut node_client: C,
|
||||
test_mode: bool,
|
||||
) -> Result<String, Error>
|
||||
where
|
||||
C: NodeClient + 'static + Clone,
|
||||
|
@ -815,7 +817,7 @@ where
|
|||
let mask = lc.open_wallet(
|
||||
None,
|
||||
prompt_password(&global_wallet_args.password),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
)?;
|
||||
if let Some(account) = wallet_args.value_of("account") {
|
||||
|
@ -854,6 +856,7 @@ where
|
|||
("owner_api", Some(_)) => {
|
||||
let mut g = global_wallet_args.clone();
|
||||
g.tls_conf = None;
|
||||
print!("mask: {:?}", keychain_mask);
|
||||
command::owner_api(wallet, keychain_mask, &wallet_config, &g)
|
||||
}
|
||||
("web", Some(_)) => {
|
||||
|
@ -885,7 +888,7 @@ where
|
|||
command::issue_invoice_tx(wallet, km, a)
|
||||
}
|
||||
("pay", Some(args)) => {
|
||||
let a = arg_parse!(parse_process_invoice_args(&args));
|
||||
let a = arg_parse!(parse_process_invoice_args(&args, !test_mode));
|
||||
command::process_invoice(
|
||||
wallet,
|
||||
km,
|
18
src/lib.rs
Normal file
18
src/lib.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2019 The Grin Developers
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use grin_wallet_config as config;
|
||||
use grin_wallet_util::grin_api as api;
|
||||
use grin_wallet_util::grin_util as util;
|
||||
|
||||
pub mod cmd;
|
494
tests/cmd_line_basic.rs
Normal file
494
tests/cmd_line_basic.rs
Normal file
|
@ -0,0 +1,494 @@
|
|||
// Copyright 2019 The Grin Developers
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Test wallet command line works as expected
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
extern crate grin_wallet;
|
||||
|
||||
use grin_wallet_impls::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
|
||||
use clap::App;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use grin_wallet_impls::DefaultLCProvider;
|
||||
use grin_wallet_util::grin_keychain::ExtKeychain;
|
||||
|
||||
mod common;
|
||||
use common::{execute_command, initial_setup_wallet, instantiate_wallet, setup};
|
||||
|
||||
/// command line tests
|
||||
fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::Error> {
|
||||
setup(test_dir);
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy: WalletProxy<
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
> = WalletProxy::new(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
|
||||
// load app yaml. If it don't exist, just say so and exit
|
||||
let yml = load_yaml!("../src/bin/grin-wallet.yml");
|
||||
let app = App::from_yaml(yml);
|
||||
|
||||
// wallet init
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "init", "-h"];
|
||||
// should create new wallet file
|
||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec.clone())?;
|
||||
|
||||
// trying to init twice - should fail
|
||||
assert!(execute_command(&app, test_dir, "wallet1", &client1, arg_vec.clone()).is_err());
|
||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||
|
||||
// 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, 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, 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 || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// Create some accounts in wallet 1
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "account", "-c", "mining"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"account",
|
||||
"-c",
|
||||
"account_1",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// Create some accounts in wallet 2
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"account",
|
||||
"-c",
|
||||
"account_1",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec.clone())?;
|
||||
// already exists
|
||||
assert!(execute_command(&app, test_dir, "wallet2", &client2, arg_vec).is_err());
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"account",
|
||||
"-c",
|
||||
"account_2",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// let's see those accounts
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "account"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// let's see those accounts
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "account"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// 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, 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(), mask1, bh as usize, false);
|
||||
|
||||
let very_long_message = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
This part should all be truncated";
|
||||
|
||||
// Update info and check
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "info"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// try a file exchange
|
||||
let file_name = format!("{}/tx1.part_tx", test_dir);
|
||||
let response_file_name = format!("{}/tx1.part_tx.response", test_dir);
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
very_long_message,
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"account_1",
|
||||
"receive",
|
||||
"-i",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Thanks, Yeast!",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec.clone())?;
|
||||
|
||||
// shouldn't be allowed to receive twice
|
||||
assert!(execute_command(&app, test_dir, "wallet2", &client2, arg_vec).is_err());
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"finalize",
|
||||
"-i",
|
||||
&response_file_name,
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
bh += 1;
|
||||
|
||||
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(), 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(), mask1, 10, false);
|
||||
bh += 10;
|
||||
|
||||
// update info for each
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "info"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "account_1", "info"];
|
||||
execute_command(&app, test_dir, "wallet2", &client1, arg_vec)?;
|
||||
|
||||
// check results in wallet 2
|
||||
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(())
|
||||
})?;
|
||||
|
||||
// Self-send to same account, using smallest strategy
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Love, Yeast, Smallest",
|
||||
"-s",
|
||||
"smallest",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"receive",
|
||||
"-i",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Thanks, Yeast!",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec.clone())?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"finalize",
|
||||
"-i",
|
||||
&response_file_name,
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
bh += 1;
|
||||
|
||||
// Check our transaction log, should have bh entries + one for the self receive
|
||||
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")?;
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(txs.len(), bh as usize + 1);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Try using the self-send method, splitting up outputs for the fun of it
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"self",
|
||||
"-d",
|
||||
"mining",
|
||||
"-g",
|
||||
"Self love",
|
||||
"-o",
|
||||
"3",
|
||||
"-s",
|
||||
"smallest",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
bh += 1;
|
||||
|
||||
// Check our transaction log, should have bh entries + 2 for the self receives
|
||||
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")?;
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(txs.len(), bh as usize + 2);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Another file exchange, don't send, but unlock with repair command
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Ain't sending",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "check", "-d"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// Another file exchange, cancel this time
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Ain't sending 2",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"cancel",
|
||||
"-i",
|
||||
"26",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// issue an invoice tx, wallet 2
|
||||
let file_name = format!("{}/invoice.slate", test_dir);
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"invoice",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Please give me your precious grins. Love, Yeast",
|
||||
"65",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
let output_file_name = format!("{}/invoice.slate.paid", test_dir);
|
||||
|
||||
// now pay the invoice tx, wallet 1
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-a",
|
||||
"mining",
|
||||
"-p",
|
||||
"password",
|
||||
"pay",
|
||||
"-i",
|
||||
&file_name,
|
||||
"-d",
|
||||
&output_file_name,
|
||||
"-g",
|
||||
"Here you go",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// and finalize, wallet 2
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"finalize",
|
||||
"-i",
|
||||
&output_file_name,
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// bit more mining
|
||||
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)
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "txs"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// message output (mostly spit out for a visual in test logs)
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"txs",
|
||||
"-i",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// txs and outputs (mostly spit out for a visual in test logs)
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "outputs"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "txs"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "outputs"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// get tx output via -tx parameter
|
||||
let mut tx_id = "".to_string();
|
||||
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();
|
||||
Ok(())
|
||||
})?;
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "txs", "-t", &tx_id[..]];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// let logging finish
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_command_line() {
|
||||
let test_dir = "target/test_output/command_line";
|
||||
if let Err(e) = command_line_test_impl(test_dir) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
}
|
297
tests/common/mod.rs
Normal file
297
tests/common/mod.rs
Normal file
|
@ -0,0 +1,297 @@
|
|||
// Copyright 2019 The Grin Developers
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Common functions for wallet integration tests
|
||||
extern crate grin_wallet;
|
||||
|
||||
use grin_wallet_impls::test_framework::LocalWalletClient;
|
||||
use grin_wallet_util::grin_util as util;
|
||||
|
||||
use clap::{App, ArgMatches};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::{env, fs};
|
||||
use util::{Mutex, ZeroingString};
|
||||
|
||||
use grin_wallet_config::{GlobalWalletConfig, WalletConfig, GRIN_WALLET_DIR};
|
||||
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl};
|
||||
use grin_wallet_libwallet::{WalletInfo, WalletInst};
|
||||
use grin_wallet_util::grin_core::global::{self, ChainTypes};
|
||||
use grin_wallet_util::grin_keychain::ExtKeychain;
|
||||
use util::secp::key::SecretKey;
|
||||
|
||||
use grin_wallet::cmd::wallet_args;
|
||||
use grin_wallet_util::grin_api as api;
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
// Set up 2 wallets and launch the test proxy behind them
|
||||
#[macro_export]
|
||||
macro_rules! setup_proxy {
|
||||
($test_dir: expr, $chain: ident, $wallet1: ident, $client1: ident, $mask1: ident, $wallet2: ident, $client2: ident, $mask2: ident) => {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy: WalletProxy<
|
||||
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
> = WalletProxy::new($test_dir);
|
||||
let $chain = wallet_proxy.chain.clone();
|
||||
|
||||
// load app yaml. If it don't exist, just say so and exit
|
||||
let yml = load_yaml!("../src/bin/grin-wallet.yml");
|
||||
let app = App::from_yaml(yml);
|
||||
|
||||
// wallet init
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "init", "-h"];
|
||||
// should create new wallet file
|
||||
let $client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||
execute_command(&app, $test_dir, "wallet1", &$client1, arg_vec.clone())?;
|
||||
|
||||
// add wallet to proxy
|
||||
let config1 = initial_setup_wallet($test_dir, "wallet1");
|
||||
//config1.owner_api_listen_port = Some(13420);
|
||||
let ($wallet1, mask1_i) =
|
||||
instantiate_wallet(config1.clone(), $client1.clone(), "password", "default")?;
|
||||
let $mask1 = (&mask1_i).as_ref();
|
||||
wallet_proxy.add_wallet(
|
||||
"wallet1",
|
||||
$client1.get_send_instance(),
|
||||
$wallet1.clone(),
|
||||
mask1_i.clone(),
|
||||
);
|
||||
|
||||
// Create wallet 2, which will run a listener
|
||||
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");
|
||||
//config2.api_listen_port = 23415;
|
||||
let ($wallet2, mask2_i) =
|
||||
instantiate_wallet(config2.clone(), $client2.clone(), "password", "default")?;
|
||||
let $mask2 = (&mask2_i).as_ref();
|
||||
wallet_proxy.add_wallet(
|
||||
"wallet2",
|
||||
$client2.get_send_instance(),
|
||||
$wallet2.clone(),
|
||||
mask2_i.clone(),
|
||||
);
|
||||
|
||||
// Set the wallet proxy listener running
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = wallet_proxy.run() {
|
||||
error!("Wallet Proxy error: {}", e);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
fn clean_output_dir(test_dir: &str) {
|
||||
let _ = fs::remove_dir_all(test_dir);
|
||||
}
|
||||
|
||||
pub fn setup(test_dir: &str) {
|
||||
util::init_test_logger();
|
||||
clean_output_dir(test_dir);
|
||||
global::set_mining_mode(ChainTypes::AutomatedTesting);
|
||||
}
|
||||
|
||||
/// Create a wallet config file in the given current directory
|
||||
pub fn config_command_wallet(
|
||||
dir_name: &str,
|
||||
wallet_name: &str,
|
||||
) -> Result<(), grin_wallet_controller::Error> {
|
||||
let mut current_dir;
|
||||
let mut default_config = GlobalWalletConfig::default();
|
||||
current_dir = env::current_dir().unwrap_or_else(|e| {
|
||||
panic!("Error creating config file: {}", e);
|
||||
});
|
||||
current_dir.push(dir_name);
|
||||
current_dir.push(wallet_name);
|
||||
let _ = fs::create_dir_all(current_dir.clone());
|
||||
let mut config_file_name = current_dir.clone();
|
||||
config_file_name.push("grin-wallet.toml");
|
||||
if config_file_name.exists() {
|
||||
return Err(grin_wallet_controller::ErrorKind::ArgumentError(
|
||||
"grin-wallet.toml already exists in the target directory. Please remove it first"
|
||||
.to_owned(),
|
||||
))?;
|
||||
}
|
||||
default_config.update_paths(¤t_dir);
|
||||
default_config
|
||||
.write_to_file(config_file_name.to_str().unwrap())
|
||||
.unwrap_or_else(|e| {
|
||||
panic!("Error creating config file: {}", e);
|
||||
});
|
||||
|
||||
println!(
|
||||
"File {} configured and created",
|
||||
config_file_name.to_str().unwrap(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles setup and detection of paths for wallet
|
||||
pub fn initial_setup_wallet(dir_name: &str, wallet_name: &str) -> WalletConfig {
|
||||
let mut current_dir;
|
||||
current_dir = env::current_dir().unwrap_or_else(|e| {
|
||||
panic!("Error creating config file: {}", e);
|
||||
});
|
||||
current_dir.push(dir_name);
|
||||
current_dir.push(wallet_name);
|
||||
let _ = fs::create_dir_all(current_dir.clone());
|
||||
let mut config_file_name = current_dir.clone();
|
||||
config_file_name.push("grin-wallet.toml");
|
||||
GlobalWalletConfig::new(config_file_name.to_str().unwrap())
|
||||
.unwrap()
|
||||
.members
|
||||
.unwrap()
|
||||
.wallet
|
||||
}
|
||||
|
||||
fn get_wallet_subcommand<'a>(
|
||||
wallet_dir: &str,
|
||||
wallet_name: &str,
|
||||
args: ArgMatches<'a>,
|
||||
) -> ArgMatches<'a> {
|
||||
match args.subcommand() {
|
||||
("init", Some(init_args)) => {
|
||||
// wallet init command should spit out its config file then continue
|
||||
// (if desired)
|
||||
if init_args.is_present("here") {
|
||||
let _ = config_command_wallet(wallet_dir, wallet_name);
|
||||
}
|
||||
init_args.to_owned()
|
||||
}
|
||||
_ => ArgMatches::new(),
|
||||
}
|
||||
}
|
||||
//
|
||||
// Helper to create an instance of the LMDB wallet
|
||||
pub fn instantiate_wallet(
|
||||
mut wallet_config: WalletConfig,
|
||||
node_client: LocalWalletClient,
|
||||
passphrase: &str,
|
||||
account: &str,
|
||||
) -> Result<
|
||||
(
|
||||
Arc<
|
||||
Mutex<
|
||||
Box<
|
||||
WalletInst<
|
||||
'static,
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
Option<SecretKey>,
|
||||
),
|
||||
grin_wallet_controller::Error,
|
||||
> {
|
||||
wallet_config.chain_type = None;
|
||||
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(node_client).unwrap())
|
||||
as Box<
|
||||
WalletInst<
|
||||
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
|
||||
LocalWalletClient,
|
||||
ExtKeychain,
|
||||
>,
|
||||
>;
|
||||
let lc = wallet.lc_provider().unwrap();
|
||||
// legacy hack to avoid the need for changes in existing grin-wallet.toml files
|
||||
// remove `wallet_data` from end of path as
|
||||
// new lifecycle provider assumes grin_wallet.toml is in root of data directory
|
||||
let mut top_level_wallet_dir = PathBuf::from(wallet_config.clone().data_file_dir);
|
||||
if top_level_wallet_dir.ends_with(GRIN_WALLET_DIR) {
|
||||
top_level_wallet_dir.pop();
|
||||
wallet_config.data_file_dir = top_level_wallet_dir.to_str().unwrap().into();
|
||||
}
|
||||
lc.set_wallet_directory(&wallet_config.data_file_dir);
|
||||
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)), keychain_mask))
|
||||
}
|
||||
|
||||
pub fn execute_command(
|
||||
app: &App,
|
||||
test_dir: &str,
|
||||
wallet_name: &str,
|
||||
client: &LocalWalletClient,
|
||||
arg_vec: Vec<&str>,
|
||||
) -> Result<String, grin_wallet_controller::Error> {
|
||||
let args = app.clone().get_matches_from(arg_vec);
|
||||
let _ = get_wallet_subcommand(test_dir, wallet_name, args.clone());
|
||||
let mut config = initial_setup_wallet(test_dir, wallet_name);
|
||||
//unset chain type so it doesn't get reset
|
||||
config.chain_type = None;
|
||||
wallet_args::wallet_command(&args, config.clone(), client.clone(), true)
|
||||
}
|
||||
|
||||
pub fn post<IN>(url: &Url, api_secret: Option<String>, input: &IN) -> Result<String, api::Error>
|
||||
where
|
||||
IN: Serialize,
|
||||
{
|
||||
// TODO: change create_post_request to accept a url instead of a &str
|
||||
let req = api::client::create_post_request(url.as_str(), api_secret, input)?;
|
||||
let res = api::client::send_request(req)?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn send_request<OUT>(
|
||||
id: u64,
|
||||
dest: &str,
|
||||
req: &str,
|
||||
) -> Result<Result<OUT, WalletAPIReturnError>, api::Error>
|
||||
where
|
||||
OUT: DeserializeOwned,
|
||||
{
|
||||
let url = Url::parse(dest).unwrap();
|
||||
let req: Value = serde_json::from_str(req).unwrap();
|
||||
let res: String = post(&url, None, &req).map_err(|e| {
|
||||
let err_string = format!("{}", e);
|
||||
println!("{}", err_string);
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
e
|
||||
})?;
|
||||
let res = serde_json::from_str(&res).unwrap();
|
||||
let res = easy_jsonrpc::Response::from_json_response(res).unwrap();
|
||||
let res = res.outputs.get(&id).unwrap().clone().unwrap();
|
||||
if res["Err"] != json!(null) {
|
||||
Ok(Err(WalletAPIReturnError {
|
||||
message: res["Err"].as_str().unwrap().to_owned(),
|
||||
}))
|
||||
} else {
|
||||
// deserialize result into expected type
|
||||
let value: OUT = serde_json::from_value(res["Ok"].clone()).unwrap();
|
||||
Ok(Ok(value))
|
||||
}
|
||||
}
|
||||
|
||||
// Types to make working with json responses easier
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct WalletAPIReturnError {
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RetrieveSummaryInfoResp(pub bool, pub WalletInfo);
|
0
tests/data/v3_reqs/open_wallet.req.json
Normal file
0
tests/data/v3_reqs/open_wallet.req.json
Normal file
10
tests/data/v3_reqs/retrieve_info.req.json
Normal file
10
tests/data/v3_reqs/retrieve_info.req.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "retrieve_summary_info",
|
||||
"params": {
|
||||
"token": null,
|
||||
"refresh_from_node": true,
|
||||
"minimum_confirmations": 1
|
||||
},
|
||||
"id": 1
|
||||
}
|
76
tests/owner_v3.rs
Normal file
76
tests/owner_v3.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2019 The Grin Developers
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
extern crate grin_wallet;
|
||||
|
||||
use grin_wallet_impls::test_framework::{self, LocalWalletClient, WalletProxy};
|
||||
|
||||
use clap::App;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use grin_wallet_impls::DefaultLCProvider;
|
||||
use grin_wallet_util::grin_keychain::ExtKeychain;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::RetrieveSummaryInfoResp;
|
||||
use common::{execute_command, initial_setup_wallet, instantiate_wallet, send_request, setup};
|
||||
|
||||
#[test]
|
||||
fn owner_v3() -> Result<(), grin_wallet_controller::Error> {
|
||||
let test_dir = "target/test_output/owner_v3";
|
||||
setup(test_dir);
|
||||
|
||||
setup_proxy!(test_dir, chain, wallet1, client1, mask1, wallet2, client2, _mask2);
|
||||
|
||||
// add some blocks manually
|
||||
let bh = 10u64;
|
||||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
// run the owner listener on wallet 1
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "owner_api"];
|
||||
// Set running
|
||||
thread::spawn(move || {
|
||||
let yml = load_yaml!("../src/bin/grin-wallet.yml");
|
||||
let app = App::from_yaml(yml);
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec.clone()).unwrap();
|
||||
});
|
||||
|
||||
// run the foreign listener for wallet 2
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "listen"];
|
||||
// Set owner listener running
|
||||
thread::spawn(move || {
|
||||
let yml = load_yaml!("../src/bin/grin-wallet.yml");
|
||||
let app = App::from_yaml(yml);
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec.clone()).unwrap();
|
||||
});
|
||||
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
|
||||
// Send simple retrieve_info request to owner listener
|
||||
let req = include_str!("data/v3_reqs/retrieve_info.req.json");
|
||||
let res = send_request(1, "http://127.0.0.1:3420/v3/owner", req)?;
|
||||
assert!(res.is_ok());
|
||||
let value: RetrieveSummaryInfoResp = res.unwrap();
|
||||
assert_eq!(value.1.amount_currently_spendable, 420000000000);
|
||||
println!("Response: {:?}", value);
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue