From 2d264db91a6bc8a713a474cd52f54e1240ace77f Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Thu, 6 Feb 2020 09:08:24 +0000 Subject: [PATCH] Wallet Interactive-mode, Part 1 (#295) * cli addition to yml * first experiments with CLI mode * rustfmt * modify owner_single_use to take optional instantiated API * refactor command functions to take owner api instance * modify commands to use single owner api instance * add ability to pass customised message handler to API * prevent update wallet attempt if wallet hasn't been opened * fix output when internal updater is running * add termion, use cli for status updates, remove logging output from CLI * some attempts to clean up the command prompt and accomodate scanning intervals * rework listener slightly to allow for cli mode, add close command * remove termion dependency --- Cargo.lock | 88 +++++++ Cargo.toml | 3 + api/src/foreign.rs | 2 +- api/src/owner.rs | 88 ++++--- api/src/owner_rpc.rs | 2 +- controller/src/command.rs | 163 ++++++++----- controller/src/controller.rs | 29 ++- controller/src/error.rs | 4 + controller/tests/accounts.rs | 22 +- controller/tests/check.rs | 56 ++--- controller/tests/file.rs | 18 +- controller/tests/invoice.rs | 16 +- controller/tests/no_change.rs | 12 +- controller/tests/payment_proofs.rs | 6 +- controller/tests/repost.rs | 22 +- controller/tests/self_send.rs | 8 +- controller/tests/transaction.rs | 34 +-- controller/tests/ttl_cutoff.rs | 12 +- controller/tests/updater_thread.rs | 6 +- libwallet/src/api_impl/owner_updater.rs | 22 +- src/bin/grin-wallet.rs | 8 +- src/bin/grin-wallet.yml | 6 + src/cli/cli.rs | 312 ++++++++++++++++++++++++ src/cli/mod.rs | 17 ++ src/cmd/wallet_args.rs | 128 +++++++--- src/lib.rs | 6 + tests/cmd_line_basic.rs | 116 +++++---- tests/owner_v3_lifecycle.rs | 39 +-- 28 files changed, 930 insertions(+), 315 deletions(-) create mode 100644 src/cli/cli.rs create mode 100644 src/cli/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 556c93825..d363a2586 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,6 +314,16 @@ dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "colored" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -488,6 +498,26 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "doc-comment" version = "0.3.1" @@ -913,6 +943,7 @@ version = "3.1.0-beta.1" dependencies = [ "built 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "colored 1.9.2 (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.3 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -923,14 +954,17 @@ dependencies = [ "grin_wallet_impls 3.1.0-beta.1", "grin_wallet_libwallet 3.1.0-beta.1", "grin_wallet_util 3.1.0-beta.1", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "linefeed 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "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)", + "rustyline 6.0.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.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1710,6 +1744,11 @@ dependencies = [ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "odds" version = "0.2.26" @@ -2136,6 +2175,14 @@ name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "redox_users" version = "0.3.4" @@ -2251,6 +2298,23 @@ dependencies = [ "webpki 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rustyline" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ryu" version = "1.0.2" @@ -2564,6 +2628,17 @@ dependencies = [ "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "termion" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -2941,6 +3016,11 @@ dependencies = [ "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "utf8parse" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "uuid" version = "0.7.4" @@ -3154,6 +3234,7 @@ dependencies = [ "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum colored 1.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8815e2ab78f3a59928fc32e141fbeece88320a240e43f47b2fd64ea3a88a5b3d" "checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum croaring 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "71152d60cec9dfdc5d9d793bccfa9ad95927372b80cd00e983db5eb2ce103e3b" @@ -3174,6 +3255,8 @@ dependencies = [ "checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" "checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97" "checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" "checksum easy-jsonrpc 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "07e05c6cb07c5bb6fdedd8de84a96c9e0aafc5a9d4e725b735ca5eddb770ae33" @@ -3274,6 +3357,7 @@ dependencies = [ "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" "checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" "checksum odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" @@ -3323,6 +3407,7 @@ dependencies = [ "checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" "checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" "checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" @@ -3336,6 +3421,7 @@ dependencies = [ "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustls 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "942b71057b31981152970d57399c25f72e27a6ee0d207a669d8304cabf44705b" +"checksum rustyline 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de64be8eecbe428b6924f1d8430369a01719fbb182c26fa431ddbb0a95f5315d" "checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" "checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" "checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" @@ -3375,6 +3461,7 @@ dependencies = [ "checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" "checksum terminfo 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8e51065bafd2abe106b6036483b69d1741f4a1ec56ce8a2378de341637de689e" +"checksum termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c22cec9d8978d906be5ac94bceb5a010d885c626c4c8855721a4dbd20e3ac905" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" @@ -3414,6 +3501,7 @@ dependencies = [ "checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" "checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +"checksum utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" "checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" "checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" diff --git a/Cargo.toml b/Cargo.toml index 71ccdbd10..ebb489a72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,9 @@ prettytable-rs = "0.7" log = "0.4" linefeed = "0.5" semver = "0.9" +rustyline = "6" +colored = "1.6" +lazy_static = "1" grin_wallet_api = { path = "./api", version = "3.1.0-beta.1" } grin_wallet_impls = { path = "./impls", version = "3.1.0-beta.1" } diff --git a/api/src/foreign.rs b/api/src/foreign.rs index 79db1bd97..7e6613c56 100644 --- a/api/src/foreign.rs +++ b/api/src/foreign.rs @@ -427,7 +427,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config); /// - /// let mut api_owner = Owner::new(wallet.clone()); + /// let mut api_owner = Owner::new(wallet.clone(), None); /// let mut api_foreign = Foreign::new(wallet.clone(), None, None); /// /// // . . . diff --git a/api/src/owner.rs b/api/src/owner.rs index f68fadd92..9619fb5ce 100644 --- a/api/src/owner.rs +++ b/api/src/owner.rs @@ -97,6 +97,8 @@ where /// /// # Arguments /// * `wallet_in` - A reference-counted mutex containing an implementation of the + /// * `custom_channel` - A custom MPSC Tx/Rx pair to capture status + /// updates /// [`WalletBackend`](../grin_wallet_libwallet/types/trait.WalletBackend.html) trait. /// /// # Returns @@ -157,22 +159,30 @@ where /// // All wallet functions operate on an Arc::Mutex to allow multithreading where needed /// let mut wallet = Arc::new(Mutex::new(wallet)); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// // .. perform wallet operations /// /// ``` - pub fn new(wallet_inst: Arc>>>) -> Self { - let (tx, rx) = channel(); - + pub fn new( + wallet_inst: Arc>>>, + custom_channel: Option>, + ) -> Self { let updater_running = Arc::new(AtomicBool::new(false)); let updater = Arc::new(Mutex::new(owner_updater::Updater::new( wallet_inst.clone(), updater_running.clone(), ))); - let updater_messages = Arc::new(Mutex::new(vec![])); - let _ = start_updater_log_thread(rx, updater_messages.clone()); + + let tx = match custom_channel { + Some(c) => c, + None => { + let (tx, rx) = channel(); + let _ = start_updater_log_thread(rx, updater_messages.clone()); + tx + } + }; Owner { wallet_inst, @@ -221,7 +231,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// /// let result = api_owner.accounts(None); /// @@ -272,7 +282,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// /// let result = api_owner.create_account_path(None, "account1"); /// @@ -319,7 +329,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// /// let result = api_owner.create_account_path(None, "account1"); /// @@ -375,7 +385,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// let show_spent = false; /// let update_from_node = true; /// let tx_id = None; @@ -443,7 +453,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// let update_from_node = true; /// let tx_id = None; /// let tx_slate_id = None; @@ -520,7 +530,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let mut api_owner = Owner::new(wallet.clone()); + /// let mut api_owner = Owner::new(wallet.clone(), None); /// let update_from_node = true; /// let minimum_confirmations=10; /// @@ -606,7 +616,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let mut api_owner = Owner::new(wallet.clone()); + /// let mut api_owner = Owner::new(wallet.clone(), None); /// // Attempt to create a transaction using the 'default' account /// let args = InitTxArgs { /// src_acct_name: None, @@ -697,7 +707,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let mut api_owner = Owner::new(wallet.clone()); + /// let mut api_owner = Owner::new(wallet.clone(), None); /// /// let args = IssueInvoiceTxArgs { /// amount: 60_000_000_000, @@ -752,7 +762,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let mut api_owner = Owner::new(wallet.clone()); + /// let mut api_owner = Owner::new(wallet.clone(), None); /// /// // . . . /// // The slate has been recieved from the invoicer, somehow @@ -817,7 +827,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let mut api_owner = Owner::new(wallet.clone()); + /// let mut api_owner = Owner::new(wallet.clone(), None); /// let args = InitTxArgs { /// src_acct_name: None, /// amount: 2_000_000_000, @@ -881,7 +891,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let mut api_owner = Owner::new(wallet.clone()); + /// let mut api_owner = Owner::new(wallet.clone(), None); /// let args = InitTxArgs { /// src_acct_name: None, /// amount: 2_000_000_000, @@ -941,7 +951,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let mut api_owner = Owner::new(wallet.clone()); + /// let mut api_owner = Owner::new(wallet.clone(), None); /// let args = InitTxArgs { /// src_acct_name: None, /// amount: 2_000_000_000, @@ -1013,7 +1023,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let mut api_owner = Owner::new(wallet.clone()); + /// let mut api_owner = Owner::new(wallet.clone(), None); /// let args = InitTxArgs { /// src_acct_name: None, /// amount: 2_000_000_000, @@ -1079,7 +1089,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// let update_from_node = true; /// let tx_id = None; /// let tx_slate_id = None; @@ -1129,7 +1139,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let mut api_owner = Owner::new(wallet.clone()); + /// let mut api_owner = Owner::new(wallet.clone(), None); /// let args = InitTxArgs { /// src_acct_name: None, /// amount: 2_000_000_000, @@ -1209,7 +1219,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let mut api_owner = Owner::new(wallet.clone()); + /// let mut api_owner = Owner::new(wallet.clone(), None); /// let result = api_owner.scan( /// None, /// Some(20000), @@ -1267,7 +1277,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// let result = api_owner.node_height(None); /// /// if let Ok(node_height_result) = result { @@ -1324,7 +1334,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// let result = api_owner.get_top_level_directory(); /// /// if let Ok(dir) = result { @@ -1372,7 +1382,7 @@ where /// # .ok_or("Failed to convert tmpdir path to string.".to_owned()) /// # .unwrap(); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// let result = api_owner.set_top_level_directory(dir); /// /// if let Ok(dir) = result { @@ -1422,7 +1432,7 @@ where /// # .ok_or("Failed to convert tmpdir path to string.".to_owned()) /// # .unwrap(); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// let _ = api_owner.set_top_level_directory(dir); /// /// let result = api_owner.create_config(&ChainTypes::Mainnet, None, None, None); @@ -1491,7 +1501,7 @@ where /// # .to_str() /// # .ok_or("Failed to convert tmpdir path to string.".to_owned()) /// # .unwrap(); - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// let _ = api_owner.set_top_level_directory(dir); /// /// // Create configuration @@ -1558,7 +1568,7 @@ where /// # .to_str() /// # .ok_or("Failed to convert tmpdir path to string.".to_owned()) /// # .unwrap(); - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// let _ = api_owner.set_top_level_directory(dir); /// /// // Create configuration @@ -1617,7 +1627,7 @@ where /// use grin_core::global::ChainTypes; /// /// // Set up as above - /// # let api_owner = Owner::new(wallet.clone()); + /// # let api_owner = Owner::new(wallet.clone(), None); /// /// let res = api_owner.close_wallet(None); /// @@ -1653,7 +1663,7 @@ where /// use grin_core::global::ChainTypes; /// /// // Set up as above - /// # let api_owner = Owner::new(wallet.clone()); + /// # let api_owner = Owner::new(wallet.clone(), None); /// /// let pw = ZeroingString::from("my_password"); /// let res = api_owner.get_mnemonic(None, pw); @@ -1698,7 +1708,7 @@ where /// use grin_core::global::ChainTypes; /// /// // Set up as above - /// # let api_owner = Owner::new(wallet.clone()); + /// # let api_owner = Owner::new(wallet.clone(), None); /// /// let old = ZeroingString::from("my_password"); /// let new = ZeroingString::from("new_password"); @@ -1741,7 +1751,7 @@ where /// use grin_core::global::ChainTypes; /// /// // Set up as above - /// # let api_owner = Owner::new(wallet.clone()); + /// # let api_owner = Owner::new(wallet.clone(), None); /// /// let res = api_owner.delete_wallet(None); /// @@ -1797,7 +1807,7 @@ where /// use std::time::Duration; /// /// // Set up as above - /// # let api_owner = Owner::new(wallet.clone()); + /// # let api_owner = Owner::new(wallet.clone(), None); /// /// let res = api_owner.start_updater(None, Duration::from_secs(60)); /// @@ -1852,7 +1862,7 @@ where /// use std::time::Duration; /// /// // Set up as above - /// # let api_owner = Owner::new(wallet.clone()); + /// # let api_owner = Owner::new(wallet.clone(), None); /// /// let res = api_owner.start_updater(None, Duration::from_secs(60)); /// @@ -1894,7 +1904,7 @@ where /// use std::time::Duration; /// /// // Set up as above - /// # let api_owner = Owner::new(wallet.clone()); + /// # let api_owner = Owner::new(wallet.clone(), None); /// /// let res = api_owner.start_updater(None, Duration::from_secs(60)); /// @@ -1958,7 +1968,7 @@ where /// use std::time::Duration; /// /// // Set up as above - /// # let api_owner = Owner::new(wallet.clone()); + /// # let api_owner = Owner::new(wallet.clone(), None); /// /// let res = api_owner.get_public_proof_address(None, 0); /// @@ -1998,7 +2008,7 @@ where /// use std::time::Duration; /// /// // Set up as above - /// # let api_owner = Owner::new(wallet.clone()); + /// # let api_owner = Owner::new(wallet.clone(), None); /// /// let res = api_owner.proof_address_from_onion_v3( /// "2a6at2obto3uvkpkitqp4wxcg6u36qf534eucbskqciturczzc5suyid" @@ -2047,7 +2057,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// let update_from_node = true; /// let tx_id = None; /// let tx_slate_id = Some(Uuid::parse_str("0436430c-2b02-624c-2032-570501212b00").unwrap()); @@ -2116,7 +2126,7 @@ where /// ``` /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); /// - /// let api_owner = Owner::new(wallet.clone()); + /// let api_owner = Owner::new(wallet.clone(), None); /// let update_from_node = true; /// let tx_id = None; /// let tx_slate_id = Some(Uuid::parse_str("0436430c-2b02-624c-2032-570501212b00").unwrap()); diff --git a/api/src/owner_rpc.rs b/api/src/owner_rpc.rs index 9cc3b6bc3..81cc84c78 100644 --- a/api/src/owner_rpc.rs +++ b/api/src/owner_rpc.rs @@ -1572,7 +1572,7 @@ pub fn run_doctest_owner( ); } - let mut api_owner = Owner::new(wallet1); + let mut api_owner = Owner::new(wallet1, None); api_owner.doctest_mode = true; let res = if use_token { let owner_api = &api_owner as &dyn OwnerRpcS; diff --git a/controller/src/command.rs b/controller/src/command.rs index 0b9dc206f..f2f5d8788 100644 --- a/controller/src/command.rs +++ b/controller/src/command.rs @@ -15,6 +15,7 @@ //! Grin wallet command-line function implementations use crate::api::TLSConfig; +use crate::apiwallet::Owner; use crate::config::{TorConfig, WalletConfig, WALLET_CONFIG_FILE_NAME}; use crate::core::{core, global}; use crate::error::{Error, ErrorKind}; @@ -22,7 +23,7 @@ use crate::impls::{create_sender, KeybaseAllChannels, SlateGetter as _, SlateRec use crate::impls::{PathToSlate, SlatePutter}; use crate::keychain; use crate::libwallet::{ - self, InitTxArgs, IssueInvoiceTxArgs, NodeClient, PaymentProof, WalletInst, WalletLCProvider, + self, InitTxArgs, IssueInvoiceTxArgs, NodeClient, PaymentProof, WalletLCProvider, }; use crate::util::secp::key::SecretKey; use crate::util::{Mutex, ZeroingString}; @@ -31,6 +32,7 @@ use grin_wallet_util::OnionV3Address; use serde_json as json; use std::fs::File; use std::io::{Read, Write}; +use std::sync::atomic::Ordering; use std::sync::Arc; use std::thread; use std::time::Duration; @@ -67,7 +69,7 @@ pub struct InitArgs { } pub fn init( - wallet: Arc>>>, + owner_api: &mut Owner, g_args: &GlobalArgs, args: InitArgs, ) -> Result<(), Error> @@ -76,7 +78,7 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { - let mut w_lock = wallet.lock(); + let mut w_lock = owner_api.wallet_inst.lock(); let p = w_lock.lc_provider()?; p.create_config( &g_args.chain_type, @@ -103,16 +105,13 @@ pub struct RecoverArgs { pub passphrase: ZeroingString, } -pub fn recover( - wallet: Arc>>>, - args: RecoverArgs, -) -> Result<(), Error> +pub fn recover(owner_api: &mut Owner, args: RecoverArgs) -> Result<(), Error> where L: WalletLCProvider<'static, C, K> + 'static, C: NodeClient + 'static, K: keychain::Keychain + 'static, { - let mut w_lock = wallet.lock(); + let mut w_lock = owner_api.wallet_inst.lock(); let p = w_lock.lc_provider()?; let m = p.get_mnemonic(None, args.passphrase)?; show_recovery_phrase(m); @@ -125,12 +124,13 @@ pub struct ListenArgs { } pub fn listen( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Arc>>, config: &WalletConfig, tor_config: &TorConfig, args: &ListenArgs, g_args: &GlobalArgs, + cli_mode: bool, ) -> Result<(), Error> where L: WalletLCProvider<'static, C, K> + 'static, @@ -138,13 +138,36 @@ where K: keychain::Keychain + 'static, { let res = match args.method.as_str() { - "http" => controller::foreign_listener( - wallet.clone(), - keychain_mask, - &config.api_listen_addr(), - g_args.tls_conf.clone(), - tor_config.use_tor_listener, - ), + "http" => { + let wallet_inst = owner_api.wallet_inst.clone(); + let config = config.clone(); + let tor_config = tor_config.clone(); + let g_args = g_args.clone(); + let api_thread = thread::Builder::new() + .name("wallet-http-listener".to_string()) + .spawn(move || { + let res = controller::foreign_listener( + wallet_inst, + keychain_mask, + &config.api_listen_addr(), + g_args.tls_conf.clone(), + tor_config.use_tor_listener, + ); + if let Err(e) = res { + error!("Error starting listener: {}", e); + } + }); + if let Ok(t) = api_thread { + if !cli_mode { + let r = t.join(); + if let Err(_) = r { + error!("Error starting listener"); + return Err(ErrorKind::ListenerError.into()); + } + } + } + Ok(()) + } "keybase" => KeybaseAllChannels::new()?.listen( config.clone(), g_args.password.clone().unwrap(), @@ -167,7 +190,7 @@ where } pub fn owner_api( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option, config: &WalletConfig, tor_config: &TorConfig, @@ -182,7 +205,7 @@ where // also being run at the same time let km = Arc::new(Mutex::new(keychain_mask)); let res = controller::owner_listener( - wallet, + owner_api.wallet_inst.clone(), km, config.owner_api_listen_addr().as_str(), g_args.api_secret.clone(), @@ -202,7 +225,7 @@ pub struct AccountArgs { } pub fn account( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, args: AccountArgs, ) -> Result<(), Error> @@ -212,7 +235,7 @@ where K: keychain::Keychain + 'static, { if args.create.is_none() { - let res = controller::owner_single_use(wallet, keychain_mask, |api, m| { + let res = controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { let acct_mappings = api.accounts(m)?; // give logging thread a moment to catch up thread::sleep(Duration::from_millis(200)); @@ -225,7 +248,7 @@ where } } else { let label = args.create.unwrap(); - let res = controller::owner_single_use(wallet, keychain_mask, |api, m| { + let res = controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { api.create_account_path(m, &label)?; thread::sleep(Duration::from_millis(200)); info!("Account: '{}' Created!", label); @@ -258,7 +281,7 @@ pub struct SendArgs { } pub fn send( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, tor_config: Option, args: SendArgs, @@ -269,7 +292,8 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + let wallet_inst = owner_api.wallet_inst.clone(); + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { if args.estimate_selection_strategies { let strategies = vec!["smallest", "all"] .into_iter() @@ -333,7 +357,7 @@ where None => None, Some(&m) => Some(m.to_owned()), }; - controller::foreign_single_use(wallet, km, |api| { + controller::foreign_single_use(wallet_inst, km, |api| { slate = api.receive_tx(&slate, Some(&args.dest), None)?; Ok(()) })?; @@ -374,7 +398,7 @@ pub struct ReceiveArgs { } pub fn receive( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, g_args: &GlobalArgs, args: ReceiveArgs, @@ -389,7 +413,7 @@ where None => None, Some(&m) => Some(m.to_owned()), }; - controller::foreign_single_use(wallet, km, |api| { + controller::foreign_single_use(owner_api.wallet_inst.clone(), km, |api| { if let Err(e) = api.verify_slate_messages(&slate) { error!("Error validating participant messages: {}", e); return Err(e); @@ -414,7 +438,7 @@ pub struct FinalizeArgs { } pub fn finalize( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, args: FinalizeArgs, ) -> Result<(), Error> @@ -448,7 +472,7 @@ where None => None, Some(&m) => Some(m.to_owned()), }; - controller::foreign_single_use(wallet.clone(), km, |api| { + controller::foreign_single_use(owner_api.wallet_inst.clone(), km, |api| { if let Err(e) = api.verify_slate_messages(&slate) { error!("Error validating participant messages: {}", e); return Err(e); @@ -457,7 +481,7 @@ where Ok(()) })?; } else { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { if let Err(e) = api.verify_slate_messages(m, &slate) { error!("Error validating participant messages: {}", e); return Err(e); @@ -468,7 +492,7 @@ where } if !args.nopost { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { let result = api.post_tx(m, &slate.tx, args.fluff); match result { Ok(_) => { @@ -501,7 +525,7 @@ pub struct IssueInvoiceArgs { } pub fn issue_invoice_tx( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, args: IssueInvoiceArgs, ) -> Result<(), Error> @@ -510,7 +534,7 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { let slate = api.issue_invoice_tx(m, args.issue_args)?; PathToSlate((&args.dest).into()).put_tx(&slate)?; Ok(()) @@ -533,7 +557,7 @@ pub struct ProcessInvoiceArgs { /// Process invoice pub fn process_invoice( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, tor_config: Option, args: ProcessInvoiceArgs, @@ -545,7 +569,8 @@ where K: keychain::Keychain + 'static, { let slate = PathToSlate((&args.input).into()).get_tx()?; - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + let wallet_inst = owner_api.wallet_inst.clone(); + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { if args.estimate_selection_strategies { let strategies = vec!["smallest", "all"] .into_iter() @@ -611,7 +636,7 @@ where None => None, Some(&m) => Some(m.to_owned()), }; - controller::foreign_single_use(wallet, km, |api| { + controller::foreign_single_use(wallet_inst, km, |api| { slate = api.finalize_invoice_tx(&slate)?; Ok(()) })?; @@ -633,7 +658,7 @@ pub struct InfoArgs { } pub fn info( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, g_args: &GlobalArgs, args: InfoArgs, @@ -644,17 +669,23 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + let updater_running = owner_api.updater_running.load(Ordering::Relaxed); + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { let (validated, wallet_info) = api.retrieve_summary_info(m, true, args.minimum_confirmations)?; - display::info(&g_args.account, &wallet_info, validated, dark_scheme); + display::info( + &g_args.account, + &wallet_info, + validated || updater_running, + dark_scheme, + ); Ok(()) })?; Ok(()) } pub fn outputs( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, g_args: &GlobalArgs, dark_scheme: bool, @@ -664,10 +695,17 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + let updater_running = owner_api.updater_running.load(Ordering::Relaxed); + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { let res = api.node_height(m)?; let (validated, outputs) = api.retrieve_outputs(m, g_args.show_spent, true, None)?; - display::outputs(&g_args.account, res.height, validated, outputs, dark_scheme)?; + display::outputs( + &g_args.account, + res.height, + validated || updater_running, + outputs, + dark_scheme, + )?; Ok(()) })?; Ok(()) @@ -680,7 +718,7 @@ pub struct TxsArgs { } pub fn txs( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, g_args: &GlobalArgs, args: TxsArgs, @@ -691,14 +729,15 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + let updater_running = owner_api.updater_running.load(Ordering::Relaxed); + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { let res = api.node_height(m)?; let (validated, txs) = api.retrieve_txs(m, true, args.id, args.tx_slate_id)?; let include_status = !args.id.is_some() && !args.tx_slate_id.is_some(); display::txs( &g_args.account, res.height, - validated, + validated || updater_running, &txs, include_status, dark_scheme, @@ -721,7 +760,13 @@ where if id.is_some() { let (_, outputs) = api.retrieve_outputs(m, true, false, id)?; - display::outputs(&g_args.account, res.height, validated, outputs, dark_scheme)?; + display::outputs( + &g_args.account, + res.height, + validated || updater_running, + outputs, + dark_scheme, + )?; // should only be one here, but just in case for tx in txs { display::tx_messages(&tx, dark_scheme)?; @@ -741,7 +786,7 @@ pub struct PostArgs { } pub fn post( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, args: PostArgs, ) -> Result<(), Error> @@ -752,7 +797,7 @@ where { let slate = PathToSlate((&args.input).into()).get_tx()?; - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { api.post_tx(m, &slate.tx, args.fluff)?; info!("Posted transaction"); return Ok(()); @@ -768,7 +813,7 @@ pub struct RepostArgs { } pub fn repost( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, args: RepostArgs, ) -> Result<(), Error> @@ -777,7 +822,7 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { let (_, txs) = api.retrieve_txs(m, true, Some(args.id), None)?; let stored_tx = api.get_stored_tx(m, &txs[0])?; if stored_tx.is_none() { @@ -820,7 +865,7 @@ pub struct CancelArgs { } pub fn cancel( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, args: CancelArgs, ) -> Result<(), Error> @@ -829,7 +874,7 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { let result = api.cancel_tx(m, args.tx_id, args.tx_slate_id); match result { Ok(_) => { @@ -852,7 +897,7 @@ pub struct CheckArgs { } pub fn scan( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, args: CheckArgs, ) -> Result<(), Error> @@ -861,7 +906,7 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { warn!("Starting output scan ...",); let result = api.scan(m, args.start_height, args.delete_unconfirmed); match result { @@ -881,7 +926,7 @@ where /// Payment Proof Address pub fn address( - wallet: Arc>>>, + owner_api: &mut Owner, g_args: &GlobalArgs, keychain_mask: Option<&SecretKey>, ) -> Result<(), Error> @@ -890,7 +935,7 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { // Just address at derivation index 0 for now let pub_key = api.get_public_proof_address(m, 0)?; let addr = OnionV3Address::from_bytes(pub_key.to_bytes()); @@ -912,7 +957,7 @@ pub struct ProofExportArgs { } pub fn proof_export( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, args: ProofExportArgs, ) -> Result<(), Error> @@ -921,7 +966,7 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { let result = api.retrieve_payment_proof(m, true, args.id, args.tx_slate_id); match result { Ok(p) => { @@ -947,7 +992,7 @@ pub struct ProofVerifyArgs { } pub fn proof_verify( - wallet: Arc>>>, + owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, args: ProofVerifyArgs, ) -> Result<(), Error> @@ -956,7 +1001,7 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { - controller::owner_single_use(wallet.clone(), keychain_mask, |api, m| { + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { let mut proof_f = match File::open(&args.input_file) { Ok(p) => p, Err(e) => { diff --git a/controller/src/controller.rs b/controller/src/controller.rs index 5cb9b4806..b7eef9cea 100644 --- a/controller/src/controller.rs +++ b/controller/src/controller.rs @@ -121,8 +121,9 @@ where /// Instantiate wallet Owner API for a single-use (command line) call /// Return a function containing a loaded API context to call pub fn owner_single_use( - wallet: Arc>>>, + wallet: Option>>>>, keychain_mask: Option<&SecretKey>, + api_context: Option<&mut Owner>, f: F, ) -> Result<(), Error> where @@ -131,7 +132,21 @@ where C: NodeClient + 'static, K: Keychain + 'static, { - f(&mut Owner::new(wallet), keychain_mask)?; + match api_context { + Some(c) => f(c, keychain_mask)?, + None => { + let wallet = match wallet { + Some(w) => w, + None => { + return Err(ErrorKind::GenericError(format!( + "Instantiated wallet or Owner API context must be provided" + )) + .into()) + } + }; + f(&mut Owner::new(wallet, None), keychain_mask)? + } + } Ok(()) } @@ -243,6 +258,12 @@ where C: NodeClient + 'static, K: Keychain + 'static, { + // Check if wallet has been opened first + { + let mut w_lock = wallet.lock(); + let lc = w_lock.lc_provider()?; + let _ = lc.wallet_inst()?; + } // need to keep in scope while the main listener is running let _tor_process = match use_tor { true => match init_tor_listener(wallet.clone(), keychain_mask.clone(), addr) { @@ -325,7 +346,7 @@ where } fn handle_post_request(&self, req: Request) -> WalletResponseFuture { - let api = Owner::new(self.wallet.clone()); + let api = Owner::new(self.wallet.clone(), None); Box::new( self.call_api(req, api) .and_then(|resp| ok(json_response_pretty(&resp))), @@ -600,7 +621,7 @@ where tor_config: Option, running_foreign: bool, ) -> OwnerAPIHandlerV3 { - let owner_api = Owner::new(wallet.clone()); + let owner_api = Owner::new(wallet.clone(), None); owner_api.set_tor_config(tor_config); let owner_api = Arc::new(owner_api); OwnerAPIHandlerV3 { diff --git a/controller/src/error.rs b/controller/src/error.rs index d043e4ae0..22ac767b3 100644 --- a/controller/src/error.rs +++ b/controller/src/error.rs @@ -104,6 +104,10 @@ pub enum ErrorKind { #[fail(display = "{}", _0)] ArgumentError(String), + /// Other + #[fail(display = "Listener Startup Error")] + ListenerError, + /// Other #[fail(display = "Generic error: {}", _0)] GenericError(String), diff --git a/controller/tests/accounts.rs b/controller/tests/accounts.rs index 0ad090c7e..799d1b353 100644 --- a/controller/tests/accounts.rs +++ b/controller/tests/accounts.rs @@ -76,7 +76,7 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { let cm = global::coinbase_maturity(); // assume all testing precedes soft fork height // test default accounts exist - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let accounts = api.accounts(m)?; assert_eq!(accounts[0].label, "default"); assert_eq!(accounts[0].path, ExtKeychain::derive_key_id(2, 0, 0, 0, 0)); @@ -84,7 +84,7 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // add some accounts - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let new_path = api.create_account_path(m, "account1").unwrap(); assert_eq!(new_path, ExtKeychain::derive_key_id(2, 1, 0, 0, 0)); let new_path = api.create_account_path(m, "account2").unwrap(); @@ -98,7 +98,7 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // add account to wallet 2 - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { let new_path = api.create_account_path(m, "listener_account").unwrap(); assert_eq!(new_path, ExtKeychain::derive_key_id(2, 1, 0, 0, 0)); Ok(()) @@ -126,7 +126,7 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 5, false); // Should have 5 in account1 (5 spendable), 5 in account (2 spendable) - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; assert!(wallet1_refreshed); assert_eq!(wallet1_info.last_confirmed_height, 12); @@ -147,7 +147,7 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { w.set_parent_key_id_by_name("account1")?; } - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { // check last confirmed height on this account is different from above (should be 0) let (_, wallet1_info) = api.retrieve_summary_info(m, false, 1)?; assert_eq!(wallet1_info.last_confirmed_height, 0); @@ -167,7 +167,7 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { wallet_inst!(wallet1, w); w.set_parent_key_id_by_name("default")?; } - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (_, wallet1_info) = api.retrieve_summary_info(m, false, 1)?; assert_eq!(wallet1_info.last_confirmed_height, 0); let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; @@ -186,7 +186,7 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { wallet_inst!(wallet1, w); w.set_parent_key_id_by_name("account1")?; } - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let args = InitTxArgs { src_acct_name: None, amount: reward, @@ -204,7 +204,7 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { Ok(()) })?; - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; assert!(wallet1_refreshed); assert_eq!(wallet1_info.last_confirmed_height, 13); @@ -218,7 +218,7 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { wallet_inst!(wallet1, w); w.set_parent_key_id_by_name("account2")?; } - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (_, wallet1_info) = api.retrieve_summary_info(m, false, 1)?; assert_eq!(wallet1_info.last_confirmed_height, 12); let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; @@ -230,7 +230,7 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // wallet 2 should only have this tx on the listener account - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?; assert!(wallet2_refreshed); assert_eq!(wallet2_info.last_confirmed_height, 13); @@ -243,7 +243,7 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { wallet_inst!(wallet2, w); w.set_parent_key_id_by_name("default")?; } - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { let (_, wallet2_info) = api.retrieve_summary_info(m, false, 1)?; assert_eq!(wallet2_info.last_confirmed_height, 0); let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?; diff --git a/controller/tests/check.rs b/controller/tests/check.rs index 7aa34d92e..e28c62578 100644 --- a/controller/tests/check.rs +++ b/controller/tests/check.rs @@ -89,7 +89,7 @@ fn scan_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { let cm = global::coinbase_maturity() as u64; // assume all testing precedes soft fork height // add some accounts - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.create_account_path(m, "named_account_1")?; api.create_account_path(m, "account_2")?; api.create_account_path(m, "account_3")?; @@ -98,7 +98,7 @@ fn scan_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // add account to wallet 2 - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { api.create_account_path(m, "account_1")?; api.set_active_account(m, "account_1")?; Ok(()) @@ -110,7 +110,7 @@ fn scan_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false); // Sanity check contents - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |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); @@ -126,7 +126,7 @@ fn scan_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { // Accidentally delete some outputs let mut w1_outputs_commits = vec![]; - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { w1_outputs_commits = api.retrieve_outputs(m, false, true, None)?.1; Ok(()) })?; @@ -146,7 +146,7 @@ fn scan_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { } // check we have a problem now - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; let (_, txs) = api.retrieve_txs(m, true, None, None)?; let (c, _) = libwallet::TxLogEntry::sum_confirmed(&txs); @@ -155,13 +155,13 @@ fn scan_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // this should restore our missing outputs - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.scan(m, None, true)?; Ok(()) })?; // check our outputs match again - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; assert!(wallet1_refreshed); assert_eq!(wallet1_info.total, bh * reward); @@ -174,7 +174,7 @@ fn scan_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // perform a transaction, but don't let it finish - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { // send to send let args = InitTxArgs { src_acct_name: None, @@ -194,7 +194,7 @@ fn scan_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // check we're all locked - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; assert!(wallet1_refreshed); assert!(wallet1_info.amount_currently_spendable == 0); @@ -202,13 +202,13 @@ fn scan_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // unlock/restore - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.scan(m, None, true)?; Ok(()) })?; // check spendable amount again - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; assert_eq!(wallet1_info.amount_currently_spendable, (bh - cm) * reward); Ok(()) @@ -408,7 +408,7 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er bh += 3; // 0) Check repair when all is okay should leave wallet contents alone - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.scan(m, None, true)?; let info = wallet_info!(wallet1.clone(), m)?; assert_eq!(info.amount_currently_spendable, base_amount * 6); @@ -458,12 +458,12 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er // seed + BIP32 path. // 1) a full restore should recover all of them: - wallet::controller::owner_single_use(wallet3.clone(), mask3, |api, m| { + wallet::controller::owner_single_use(Some(wallet3.clone()), mask3, None, |api, m| { api.scan(m, None, false)?; Ok(()) })?; - wallet::controller::owner_single_use(wallet3.clone(), mask3, |api, m| { + wallet::controller::owner_single_use(Some(wallet3.clone()), mask3, None, |api, m| { let info = wallet_info!(wallet3.clone(), m)?; let outputs = api.retrieve_outputs(m, true, false, None)?.1; assert_eq!(outputs.len(), 6); @@ -473,12 +473,12 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er })?; // 2) scan should recover them into a single wallet - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.scan(m, None, true)?; Ok(()) })?; - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let info = wallet_info!(wallet1.clone(), m)?; let outputs = api.retrieve_outputs(m, true, false, None)?.1; assert_eq!(outputs.len(), 6); @@ -516,7 +516,7 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), miner_mask, cm, false); bh += cm as u64; - wallet::controller::owner_single_use(wallet4.clone(), mask4, |api, m| { + wallet::controller::owner_single_use(Some(wallet4.clone()), mask4, None, |api, m| { let info = wallet_info!(wallet4.clone(), m)?; let outputs = api.retrieve_outputs(m, true, false, None)?.1; assert_eq!(outputs.len(), 9); @@ -524,12 +524,12 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er Ok(()) })?; - wallet::controller::owner_single_use(wallet5.clone(), mask5, |api, m| { + wallet::controller::owner_single_use(Some(wallet5.clone()), mask5, None, |api, m| { api.scan(m, None, false)?; Ok(()) })?; - wallet::controller::owner_single_use(wallet5.clone(), mask5, |api, m| { + wallet::controller::owner_single_use(Some(wallet5.clone()), mask5, None, |api, m| { let info = wallet_info!(wallet5.clone(), m)?; let outputs = api.retrieve_outputs(m, true, false, None)?.1; assert_eq!(outputs.len(), 9); @@ -571,7 +571,7 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er ); bh += cm as u64; - wallet::controller::owner_single_use(wallet6.clone(), mask6, |api, m| { + wallet::controller::owner_single_use(Some(wallet6.clone()), mask6, None, |api, m| { let info = wallet_info!(wallet6.clone(), m)?; let outputs = api.retrieve_outputs(m, true, false, None)?.1; assert_eq!(outputs.len(), 12); @@ -579,12 +579,12 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er Ok(()) })?; - wallet::controller::owner_single_use(wallet6.clone(), mask6, |api, m| { + wallet::controller::owner_single_use(Some(wallet6.clone()), mask6, None, |api, m| { api.scan(m, None, true)?; Ok(()) })?; - wallet::controller::owner_single_use(wallet6.clone(), mask6, |api, m| { + wallet::controller::owner_single_use(Some(wallet6.clone()), mask6, None, |api, m| { let info = wallet_info!(wallet6.clone(), m)?; let outputs = api.retrieve_outputs(m, true, false, None)?.1; assert_eq!(outputs.len(), 12); @@ -619,7 +619,7 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er bh += 3; // mix it up a bit - wallet::controller::owner_single_use(wallet7.clone(), mask7, |api, m| { + wallet::controller::owner_single_use(Some(wallet7.clone()), mask7, None, |api, m| { api.create_account_path(m, "account_1")?; api.set_active_account(m, "account_1")?; Ok(()) @@ -652,7 +652,7 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), miner_mask, cm, false); bh += cm as u64; - wallet::controller::owner_single_use(wallet7.clone(), mask7, |api, m| { + wallet::controller::owner_single_use(Some(wallet7.clone()), mask7, None, |api, m| { let info = wallet_info!(wallet7.clone(), m)?; let outputs = api.retrieve_outputs(m, true, false, None)?.1; assert_eq!(outputs.len(), 3); @@ -665,7 +665,7 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er Ok(()) })?; - wallet::controller::owner_single_use(wallet8.clone(), mask8, |api, m| { + wallet::controller::owner_single_use(Some(wallet8.clone()), mask8, None, |api, m| { api.scan(m, None, false)?; let info = wallet_info!(wallet8.clone(), m)?; let outputs = api.retrieve_outputs(m, true, false, None)?.1; @@ -683,7 +683,7 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er // ids on account 2 as well, scan should get all outputs created // to now into 2 accounts - wallet::controller::owner_single_use(wallet9.clone(), mask9, |api, m| { + wallet::controller::owner_single_use(Some(wallet9.clone()), mask9, None, |api, m| { api.create_account_path(m, "account_1")?; api.set_active_account(m, "account_1")?; Ok(()) @@ -713,7 +713,7 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er bh += 3; let _bh = bh; - wallet::controller::owner_single_use(wallet9.clone(), mask9, |api, m| { + wallet::controller::owner_single_use(Some(wallet9.clone()), mask9, None, |api, m| { let info = wallet_info!(wallet9.clone(), m)?; let outputs = api.retrieve_outputs(m, true, false, None)?.1; assert_eq!(outputs.len(), 6); @@ -735,7 +735,7 @@ fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Er let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), miner_mask, cm, false); // 7) Ensure scan creates missing accounts - wallet::controller::owner_single_use(wallet10.clone(), mask10, |api, m| { + wallet::controller::owner_single_use(Some(wallet10.clone()), mask10, None, |api, m| { api.scan(m, None, true)?; api.set_active_account(m, "account_1")?; let info = wallet_info!(wallet10.clone(), m)?; diff --git a/controller/tests/file.rs b/controller/tests/file.rs index 03d6678b1..88a351153 100644 --- a/controller/tests/file.rs +++ b/controller/tests/file.rs @@ -75,14 +75,14 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro let reward = core::consensus::REWARD; // add some accounts - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.create_account_path(m, "mining")?; api.create_account_path(m, "listener")?; Ok(()) })?; // add some accounts - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { api.create_account_path(m, "account1")?; api.create_account_path(m, "account2")?; Ok(()) @@ -104,7 +104,7 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro let message = "sender test message, sender test message"; // Should have 5 in account1 (5 spendable), 5 in account (2 spendable) - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |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); @@ -138,7 +138,7 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro naughty_slate.participant_data[0].message = Some("I changed the message".to_owned()); // verify messages on slate match - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.verify_slate_messages(m, &slate)?; assert!(api.verify_slate_messages(m, &naughty_slate).is_err()); Ok(()) @@ -154,7 +154,7 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro })?; // wallet 1 finalises and posts - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let mut slate = PathToSlate(receive_file.into()).get_tx()?; api.verify_slate_messages(m, &slate)?; slate = api.finalize_tx(m, &slate)?; @@ -167,7 +167,7 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro bh += 3; // Check total in mining account - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |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); @@ -176,7 +176,7 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro })?; // Check total in 'wallet 2' account - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?; assert!(wallet2_refreshed); assert_eq!(wallet2_info.last_confirmed_height, bh); @@ -185,7 +185,7 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro })?; // Check messages, all participants should have both - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (_, tx) = api.retrieve_txs(m, true, None, Some(slate.id))?; assert_eq!( tx[0].clone().messages.unwrap().messages[0].message, @@ -201,7 +201,7 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro Ok(()) })?; - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { let (_, tx) = api.retrieve_txs(m, true, None, Some(slate.id))?; assert_eq!( tx[0].clone().messages.unwrap().messages[0].message, diff --git a/controller/tests/invoice.rs b/controller/tests/invoice.rs index a7057b559..d846e6df5 100644 --- a/controller/tests/invoice.rs +++ b/controller/tests/invoice.rs @@ -69,7 +69,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { let reward = core::consensus::REWARD; // add some accounts - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.create_account_path(m, "mining")?; api.create_account_path(m, "listener")?; Ok(()) @@ -85,7 +85,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { 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| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |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); @@ -95,7 +95,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { let mut slate = Slate::blank(2); - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { // Wallet 2 inititates an invoice transaction, requesting payment let args = IssueInvoiceTxArgs { amount: reward * 2, @@ -105,7 +105,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { Ok(()) })?; - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { // Wallet 1 receives the invoice transaction let args = InitTxArgs { src_acct_name: None, @@ -129,7 +129,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // wallet 1 posts so wallet 2 doesn't get the mined amount - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.post_tx(m, &slate.tx, false)?; Ok(()) })?; @@ -139,7 +139,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { bh += 3; // Check transaction log for wallet 2 - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { let (_, wallet2_info) = api.retrieve_summary_info(m, true, 1)?; let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?; assert!(refreshed); @@ -155,7 +155,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { // Check transaction log for wallet 1, ensure only 1 entry // exists - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?; assert!(refreshed); @@ -168,7 +168,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // Test self-sending - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { // Wallet 1 inititates an invoice transaction, requesting payment let args = IssueInvoiceTxArgs { amount: reward * 2, diff --git a/controller/tests/no_change.rs b/controller/tests/no_change.rs index ab38434ae..e5b35066d 100644 --- a/controller/tests/no_change.rs +++ b/controller/tests/no_change.rs @@ -75,7 +75,7 @@ fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { // send a single block's worth of transactions with minimal strategy let mut slate = Slate::blank(2); - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let args = InitTxArgs { src_acct_name: None, amount: reward - fee, @@ -94,7 +94,7 @@ fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // Refresh and check transaction log for wallet 1 - wallet::controller::owner_single_use(wallet1.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask2, None, |api, m| { let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id))?; assert!(refreshed); let tx = txs[0].clone(); @@ -104,7 +104,7 @@ fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // ensure invoice TX works as well with no change - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { // Wallet 2 inititates an invoice transaction, requesting payment let args = IssueInvoiceTxArgs { amount: reward - fee, @@ -114,7 +114,7 @@ fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { Ok(()) })?; - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { // Wallet 1 receives the invoice transaction let args = InitTxArgs { src_acct_name: None, @@ -136,13 +136,13 @@ fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { slate = api.finalize_invoice_tx(&slate)?; Ok(()) })?; - wallet::controller::owner_single_use(wallet2.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask1, None, |api, m| { api.post_tx(m, &slate.tx, false)?; Ok(()) })?; // Refresh and check transaction log for wallet 1 - wallet::controller::owner_single_use(wallet1.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask2, None, |api, m| { let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id))?; assert!(refreshed); for tx in txs { diff --git a/controller/tests/payment_proofs.rs b/controller/tests/payment_proofs.rs index 85dfba514..3e76d9160 100644 --- a/controller/tests/payment_proofs.rs +++ b/controller/tests/payment_proofs.rs @@ -77,7 +77,7 @@ fn payment_proofs_test_impl(test_dir: &'static str) -> Result<(), libwallet::Err test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false); let mut address = None; - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { address = Some(api.get_public_proof_address(m, 0)?); Ok(()) })?; @@ -86,7 +86,7 @@ fn payment_proofs_test_impl(test_dir: &'static str) -> Result<(), libwallet::Err println!("Public address is: {:?}", address); let amount = 60_000_000_000; let mut slate = Slate::blank(1); - wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| { // note this will increment the block count as part of the transaction "Posting" let args = InitTxArgs { src_acct_name: None, @@ -139,7 +139,7 @@ fn payment_proofs_test_impl(test_dir: &'static str) -> Result<(), libwallet::Err let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 2, false); - wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| { // Check payment proof here let mut pp = sender_api.retrieve_payment_proof(m, true, None, Some(slate.id))?; diff --git a/controller/tests/repost.rs b/controller/tests/repost.rs index e8ec384be..3e1e3ccb1 100644 --- a/controller/tests/repost.rs +++ b/controller/tests/repost.rs @@ -72,14 +72,14 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> let reward = core::consensus::REWARD; // add some accounts - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.create_account_path(m, "mining")?; api.create_account_path(m, "listener")?; Ok(()) })?; // add some accounts - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { api.create_account_path(m, "account1")?; api.create_account_path(m, "account2")?; Ok(()) @@ -100,7 +100,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> let mut slate = Slate::blank(2); // Should have 5 in account1 (5 spendable), 5 in account (2 spendable) - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |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); @@ -144,14 +144,14 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> } // wallet 1 finalize - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { slate = PathToSlate((&receive_file).into()).get_tx()?; slate = api.finalize_tx(m, &slate)?; Ok(()) })?; // Now repost from cached - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (_, txs) = api.retrieve_txs(m, true, None, Some(slate.id))?; let stored_tx = api.get_stored_tx(m, &txs[0])?; api.post_tx(m, &stored_tx.unwrap(), false)?; @@ -163,7 +163,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> bh += 3; // update/test contents of both accounts - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |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); @@ -176,7 +176,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> w.set_parent_key_id_by_name("listener")?; } - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?; assert!(wallet2_refreshed); assert_eq!(wallet2_info.last_confirmed_height, bh); @@ -197,7 +197,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> let mut slate = Slate::blank(2); let amount = 60_000_000_000; - wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| { // note this will increment the block count as part of the transaction "Posting" let args = InitTxArgs { src_acct_name: None, @@ -219,7 +219,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> bh += 3; // Now repost from cached - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (_, txs) = api.retrieve_txs(m, true, None, Some(slate.id))?; let stored_tx = api.get_stored_tx(m, &txs[0])?; api.post_tx(m, &stored_tx.unwrap(), false)?; @@ -231,7 +231,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> bh += 3; // // update/test contents of both accounts - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |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); @@ -239,7 +239,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> Ok(()) })?; - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?; assert!(wallet2_refreshed); assert_eq!(wallet2_info.last_confirmed_height, bh); diff --git a/controller/tests/self_send.rs b/controller/tests/self_send.rs index 6a3b4657c..d95e26bfc 100644 --- a/controller/tests/self_send.rs +++ b/controller/tests/self_send.rs @@ -60,7 +60,7 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { let reward = core::consensus::REWARD; // add some accounts - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.create_account_path(m, "mining")?; api.create_account_path(m, "listener")?; Ok(()) @@ -76,7 +76,7 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false); // Should have 5 in account1 (5 spendable), 5 in account (2 spendable) - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |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); @@ -108,7 +108,7 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { bh += 3; // Check total in mining account - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |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); @@ -121,7 +121,7 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { wallet_inst!(wallet1, w); w.set_parent_key_id_by_name("listener")?; } - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |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); diff --git a/controller/tests/transaction.rs b/controller/tests/transaction.rs index 16e4f4870..64a2e3244 100644 --- a/controller/tests/transaction.rs +++ b/controller/tests/transaction.rs @@ -77,7 +77,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 10, false); // Check wallet 1 contents are as expected - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; debug!( "Wallet 1 Info Pre-Transaction, after {} blocks: {:?}", @@ -96,7 +96,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> // and a single use api for a send command let amount = 60_000_000_000; let mut slate = Slate::blank(1); - wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| { // note this will increment the block count as part of the transaction "Posting" let args = InitTxArgs { src_acct_name: None, @@ -128,7 +128,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> })?; // Check transaction log for wallet 1 - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (_, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?; assert!(refreshed); @@ -151,7 +151,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> })?; // Check transaction log for wallet 2 - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?; assert!(refreshed); // we should have a transaction entry for this slate @@ -167,13 +167,13 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> })?; // post transaction - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.post_tx(m, &slate.tx, false)?; Ok(()) })?; // Check wallet 1 contents are as expected - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; debug!( "Wallet 1 Info Post Transaction, after {} blocks: {:?}", @@ -213,7 +213,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false); // refresh wallets and retrieve info/tests for each wallet after maturity - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; debug!("Wallet 1 Info: {:?}", wallet1_info); assert!(wallet1_refreshed); @@ -228,7 +228,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> Ok(()) })?; - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?; assert!(wallet2_refreshed); assert_eq!(wallet2_info.amount_currently_spendable, amount); @@ -245,7 +245,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> })?; // Estimate fee and locked amount for a transaction - wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| { let init_args = InitTxArgs { src_acct_name: None, amount: amount * 2, @@ -279,7 +279,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> // Send another transaction, but don't post to chain immediately and use // the stored transaction instead - wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| { // note this will increment the block count as part of the transaction "Posting" let args = InitTxArgs { src_acct_name: None, @@ -297,7 +297,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> Ok(()) })?; - wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| { let (refreshed, _wallet1_info) = sender_api.retrieve_summary_info(m, true, 1)?; assert!(refreshed); let (_, txs) = sender_api.retrieve_txs(m, true, None, None)?; @@ -321,7 +321,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false); // check wallet2 has stored transaction - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?; assert!(wallet2_refreshed); assert_eq!(wallet2_info.amount_currently_spendable, amount * 3); @@ -387,7 +387,7 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> { let amount = 30_000_000_000; let mut slate = Slate::blank(1); - wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| { // note this will increment the block count as part of the transaction "Posting" let args = InitTxArgs { src_acct_name: None, @@ -407,7 +407,7 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // Check transaction log for wallet 1 - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { let (refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?; println!( "last confirmed height: {}", @@ -438,7 +438,7 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // Check transaction log for wallet 2 - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?; assert!(refreshed); let mut unconfirmed_count = 0; @@ -465,7 +465,7 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> { let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 5, false); // Wallet 1 decides to roll back instead - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { // can't roll back coinbase let res = api.cancel_tx(m, Some(1), None); assert!(res.is_err()); @@ -495,7 +495,7 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> { })?; // Wallet 2 rolls back - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { let (_, txs) = api.retrieve_txs(m, true, None, None)?; let tx = txs .iter() diff --git a/controller/tests/ttl_cutoff.rs b/controller/tests/ttl_cutoff.rs index 254fdf902..33de904dd 100644 --- a/controller/tests/ttl_cutoff.rs +++ b/controller/tests/ttl_cutoff.rs @@ -76,7 +76,7 @@ fn ttl_cutoff_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> let amount = 60_000_000_000; let mut slate = Slate::blank(1); - wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| { // note this will increment the block count as part of the transaction "Posting" let args = InitTxArgs { src_acct_name: None, @@ -103,7 +103,7 @@ fn ttl_cutoff_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> // Now mine past the block, and check again. Transaction should be gone. let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 2, false); - wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| { let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id))?; let tx = txs[0].clone(); @@ -113,7 +113,7 @@ fn ttl_cutoff_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> })?; // Should also be gone in wallet 2, and output gone - wallet::controller::owner_single_use(wallet2.clone(), mask2, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |sender_api, m| { let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id))?; let tx = txs[0].clone(); let outputs = sender_api.retrieve_outputs(m, false, true, None)?.1; @@ -126,7 +126,7 @@ fn ttl_cutoff_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> // try again, except try and send off the transaction for completion beyond the expiry let mut slate = Slate::blank(1); - wallet::controller::owner_single_use(wallet1.clone(), mask1, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| { // note this will increment the block count as part of the transaction "Posting" let args = InitTxArgs { src_acct_name: None, @@ -153,13 +153,13 @@ fn ttl_cutoff_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 2, false); // Wallet 2 will need to have updated past the TTL - wallet::controller::owner_single_use(wallet2.clone(), mask2, |sender_api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |sender_api, m| { let (_, _) = sender_api.retrieve_txs(m, true, None, Some(slate.id))?; Ok(()) })?; // And when wallet 1 sends, should be rejected - wallet::controller::owner_single_use(wallet1.clone(), mask1, |_sender_api, _m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |_sender_api, _m| { let res = client1.send_tx_slate_direct("wallet2", &slate); println!("Send after TTL result is: {:?}", res); assert!(res.is_err()); diff --git a/controller/tests/updater_thread.rs b/controller/tests/updater_thread.rs index 9f0104926..f9564f09b 100644 --- a/controller/tests/updater_thread.rs +++ b/controller/tests/updater_thread.rs @@ -69,14 +69,14 @@ fn updater_thread_test_impl(test_dir: &'static str) -> Result<(), libwallet::Err }); // add some accounts - wallet::controller::owner_single_use(wallet1.clone(), mask1, |api, m| { + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { api.create_account_path(m, "mining")?; api.create_account_path(m, "listener")?; Ok(()) })?; // add some accounts - wallet::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { + wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { api.create_account_path(m, "account1")?; api.create_account_path(m, "account2")?; Ok(()) @@ -91,7 +91,7 @@ fn updater_thread_test_impl(test_dir: &'static str) -> Result<(), libwallet::Err let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false); - let owner_api = api::Owner::new(wallet1); + let owner_api = api::Owner::new(wallet1, None); owner_api.start_updater(mask1, Duration::from_secs(5))?; // let updater thread run a bit diff --git a/libwallet/src/api_impl/owner_updater.rs b/libwallet/src/api_impl/owner_updater.rs index b49d790e6..ce1331f02 100644 --- a/libwallet/src/api_impl/owner_updater.rs +++ b/libwallet/src/api_impl/owner_updater.rs @@ -122,13 +122,23 @@ where ) -> Result<(), Error> { self.is_running.store(true, Ordering::Relaxed); loop { + let wallet_opened = { + let mut w_lock = self.wallet_inst.lock(); + let w_provider = w_lock.lc_provider()?; + match w_provider.wallet_inst() { + Ok(_) => true, + Err(_) => false, + } + }; // Business goes here - owner::update_wallet_state( - self.wallet_inst.clone(), - (&keychain_mask).as_ref(), - status_send_channel, - false, - )?; + if wallet_opened { + owner::update_wallet_state( + self.wallet_inst.clone(), + (&keychain_mask).as_ref(), + status_send_channel, + false, + )?; + } if !self.is_running.load(Ordering::Relaxed) { break; } diff --git a/src/bin/grin-wallet.rs b/src/bin/grin-wallet.rs index e7c43052c..ac9094a3f 100644 --- a/src/bin/grin-wallet.rs +++ b/src/bin/grin-wallet.rs @@ -16,7 +16,6 @@ #[macro_use] extern crate clap; - #[macro_use] extern crate log; use crate::config::ConfigError; @@ -128,7 +127,12 @@ fn real_main() -> i32 { //config.members.as_mut().unwrap().wallet.chain_type = Some(chain_type); // Load logging config - let l = config.members.as_mut().unwrap().logging.clone().unwrap(); + let mut l = config.members.as_mut().unwrap().logging.clone().unwrap(); + // no logging to stdout if we're running cli + match args.subcommand() { + ("cli", _) => l.log_to_stdout = true, + _ => {} + }; init_logger(Some(l), None); info!( "Using wallet configuration file at {}", diff --git a/src/bin/grin-wallet.yml b/src/bin/grin-wallet.yml index 4fe0c7cbf..a7b98fcca 100644 --- a/src/bin/grin-wallet.yml +++ b/src/bin/grin-wallet.yml @@ -43,6 +43,8 @@ args: long: api_server_address takes_value: true subcommands: + - cli: + about: Start the wallet in interactive CLI mode (EXPERIMENTAL and UNDER DEVELOPMENT) - account: about: List wallet accounts or create a new account args: @@ -346,6 +348,10 @@ subcommands: short: r long: recover takes_value: false + - open: + about: Opens a wallet (interactive mode only) + - close: + about: Closes the wallet (interactive mode only) - recover: about: Displays a recovery phrase for the wallet. (use `init -r` to perform recovery) - address: diff --git a/src/cli/cli.rs b/src/cli/cli.rs new file mode 100644 index 000000000..c458297dd --- /dev/null +++ b/src/cli/cli.rs @@ -0,0 +1,312 @@ +// Copyright 2020 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 crate::cmd::wallet_args; +use crate::util::secp::key::SecretKey; +use crate::util::Mutex; +use clap::App; +//use colored::Colorize; +use grin_wallet_api::Owner; +use grin_wallet_config::{TorConfig, WalletConfig}; +use grin_wallet_controller::command::GlobalArgs; +use grin_wallet_controller::Error; +use grin_wallet_impls::DefaultWalletImpl; +use grin_wallet_libwallet::{NodeClient, StatusMessage, WalletInst, WalletLCProvider}; +use grin_wallet_util::grin_keychain as keychain; +use rustyline::completion::{Completer, FilenameCompleter, Pair}; +use rustyline::error::ReadlineError; +use rustyline::highlight::{Highlighter, MatchingBracketHighlighter}; +use rustyline::hint::Hinter; +use rustyline::validate::Validator; +use rustyline::{CompletionType, Config, Context, EditMode, Editor, Helper, OutputStreamType}; +use std::borrow::Cow::{self, Borrowed, Owned}; +use std::sync::mpsc::{channel, Receiver}; +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +const COLORED_PROMPT: &'static str = "\x1b[36mgrin-wallet>\x1b[0m "; +const PROMPT: &'static str = "grin-wallet> "; +//const HISTORY_PATH: &str = ".history"; + +// static for keeping track of current stdin buffer contents +lazy_static! { + static ref STDIN_CONTENTS: Mutex = { Mutex::new(String::from("")) }; +} + +#[macro_export] +macro_rules! cli_message_inline { + ($fmt_string:expr, $( $arg:expr ),+) => { + { + use std::io::Write; + let contents = STDIN_CONTENTS.lock(); + /* use crate::common::{is_cli, COLORED_PROMPT}; */ + /* if is_cli() { */ + print!("\r"); + print!($fmt_string, $( $arg ),*); + print!(" {}", COLORED_PROMPT); + print!("\x1B[J"); + print!("{}", *contents); + std::io::stdout().flush().unwrap(); + /*} else { + info!($fmt_string, $( $arg ),*); + }*/ + } + }; +} + +#[macro_export] +macro_rules! cli_message { + ($fmt_string:expr, $( $arg:expr ),+) => { + { + use std::io::Write; + /* use crate::common::{is_cli, COLORED_PROMPT}; */ + /* if is_cli() { */ + //print!("\r"); + print!($fmt_string, $( $arg ),*); + println!(); + std::io::stdout().flush().unwrap(); + /*} else { + info!($fmt_string, $( $arg ),*); + }*/ + } + }; +} + +/// function to catch updates +pub fn start_updater_thread(rx: Receiver) -> Result<(), Error> { + let _ = thread::Builder::new() + .name("wallet-updater-status".to_string()) + .spawn(move || loop { + while let Ok(m) = rx.recv() { + match m { + StatusMessage::UpdatingOutputs(s) => cli_message_inline!("{}", s), + StatusMessage::UpdatingTransactions(s) => cli_message_inline!("{}", s), + StatusMessage::FullScanWarn(s) => cli_message_inline!("{}", s), + StatusMessage::Scanning(_, m) => { + //debug!("{}", s); + cli_message_inline!("Scanning - {}% complete - Please Wait", m); + } + StatusMessage::ScanningComplete(s) => cli_message_inline!("{}", s), + StatusMessage::UpdateWarning(s) => cli_message_inline!("{}", s), + } + } + }); + Ok(()) +} + +pub fn command_loop( + wallet_inst: Arc>>>, + keychain_mask: Option, + wallet_config: &WalletConfig, + tor_config: &TorConfig, + global_wallet_args: &GlobalArgs, + test_mode: bool, +) -> Result<(), Error> +where + DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>, + L: WalletLCProvider<'static, C, K> + 'static, + C: NodeClient + 'static, + K: keychain::Keychain + 'static, +{ + let editor = Config::builder() + .history_ignore_space(true) + .completion_type(CompletionType::List) + .edit_mode(EditMode::Emacs) + .output_stream(OutputStreamType::Stdout) + .build(); + + let mut reader = Editor::with_config(editor); + reader.set_helper(Some(EditorHelper( + FilenameCompleter::new(), + MatchingBracketHighlighter::new(), + ))); + + /*let history_file = self + .api + .config() + .get_data_path() + .unwrap() + .parent() + .unwrap() + .join(HISTORY_PATH); + if history_file.exists() { + let _ = reader.load_history(&history_file); + }*/ + + let yml = load_yaml!("../bin/grin-wallet.yml"); + let mut app = App::from_yaml(yml).version(crate_version!()); + let mut keychain_mask = keychain_mask; + + // catch updater messages + let (tx, rx) = channel(); + let mut owner_api = Owner::new(wallet_inst, Some(tx)); + start_updater_thread(rx)?; + + // start the automatic updater + owner_api.start_updater((&keychain_mask).as_ref(), Duration::from_secs(30))?; + let mut wallet_opened = false; + loop { + match reader.readline(PROMPT) { + Ok(command) => { + if command.is_empty() { + continue; + } + // TODO tidy up a bit + if command.to_lowercase() == "exit" { + break; + } + /* use crate::common::{is_cli, COLORED_PROMPT}; */ + + // reset buffer + { + let mut contents = STDIN_CONTENTS.lock(); + *contents = String::from(""); + } + + // Just add 'grin-wallet' to each command behind the scenes + // so we don't need to maintain a separate definition file + let augmented_command = format!("grin-wallet {}", command); + let args = + app.get_matches_from_safe_borrow(augmented_command.trim().split_whitespace()); + let done = match args { + Ok(args) => { + // handle opening /closing separately + keychain_mask = match args.subcommand() { + ("open", Some(_)) => { + let mut wallet_lock = owner_api.wallet_inst.lock(); + let lc = wallet_lock.lc_provider().unwrap(); + let mask = match lc.open_wallet( + None, + wallet_args::prompt_password(&global_wallet_args.password), + false, + false, + ) { + Ok(m) => { + wallet_opened = true; + m + } + Err(e) => { + cli_message!("{}", e); + None + } + }; + if let Some(account) = args.value_of("account") { + if wallet_opened { + let wallet_inst = lc.wallet_inst()?; + wallet_inst.set_parent_key_id_by_name(account)?; + } + } + mask + } + ("close", Some(_)) => { + let mut wallet_lock = owner_api.wallet_inst.lock(); + let lc = wallet_lock.lc_provider().unwrap(); + lc.close_wallet(None)?; + None + } + _ => keychain_mask, + }; + match wallet_args::parse_and_execute( + &mut owner_api, + keychain_mask.clone(), + &wallet_config, + &tor_config, + &global_wallet_args, + &args, + test_mode, + true, + ) { + Ok(_) => { + cli_message!("Command '{}' completed", args.subcommand().0); + false + } + Err(err) => { + cli_message!("{}", err); + false + } + } + } + Err(err) => { + cli_message!("{}", err); + false + } + }; + reader.add_history_entry(command); + if done { + println!(); + break; + } + } + Err(err) => { + println!("Unable to read line: {}", err); + break; + } + } + } + Ok(()) + + //let _ = reader.save_history(&history_file); +} + +struct EditorHelper(FilenameCompleter, MatchingBracketHighlighter); + +impl Completer for EditorHelper { + type Candidate = Pair; + + fn complete( + &self, + line: &str, + pos: usize, + ctx: &Context<'_>, + ) -> std::result::Result<(usize, Vec), ReadlineError> { + self.0.complete(line, pos, ctx) + } +} + +impl Hinter for EditorHelper { + fn hint(&self, line: &str, _pos: usize, _ctx: &Context<'_>) -> Option { + let mut contents = STDIN_CONTENTS.lock(); + *contents = line.into(); + None + } +} + +impl Highlighter for EditorHelper { + fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { + self.1.highlight(line, pos) + } + + fn highlight_prompt<'b, 's: 'b, 'p: 'b>( + &'s self, + prompt: &'p str, + default: bool, + ) -> Cow<'b, str> { + if default { + Borrowed(COLORED_PROMPT) + } else { + Borrowed(prompt) + } + } + + fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + Owned("\x1b[1m".to_owned() + hint + "\x1b[m") + } + + fn highlight_char(&self, line: &str, pos: usize) -> bool { + self.1.highlight_char(line, pos) + } +} +impl Validator for EditorHelper {} +impl Helper for EditorHelper {} diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 000000000..d4c2ee406 --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,17 @@ +// Copyright 2020 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. + +mod cli; + +pub use cli::command_loop; diff --git a/src/cmd/wallet_args.rs b/src/cmd/wallet_args.rs index 2a173d193..73825d255 100644 --- a/src/cmd/wallet_args.rs +++ b/src/cmd/wallet_args.rs @@ -13,12 +13,15 @@ // limitations under the License. use crate::api::TLSConfig; +use crate::cli::command_loop; use crate::config::GRIN_WALLET_DIR; use crate::util::file::get_first_line; +use crate::util::secp::key::SecretKey; use crate::util::{Mutex, ZeroingString}; /// Argument parsing and error handling for wallet commands use clap::ArgMatches; use failure::Fail; +use grin_wallet_api::Owner; use grin_wallet_config::{config_file_exists, TorConfig, WalletConfig}; use grin_wallet_controller::command; use grin_wallet_controller::{Error, ErrorKind}; @@ -922,6 +925,7 @@ where match wallet_args.subcommand() { ("init", Some(_)) => open_wallet = false, ("recover", _) => open_wallet = false, + ("cli", _) => open_wallet = false, ("owner_api", _) => { // If wallet exists, open it. Otherwise, that's fine too. let mut wallet_lock = wallet.lock(); @@ -950,34 +954,81 @@ where false => None, }; - let km = (&keychain_mask).as_ref(); - let res = match wallet_args.subcommand() { - ("init", Some(args)) => { - let a = arg_parse!(parse_init_args( - wallet.clone(), + ("cli", Some(_)) => command_loop( + wallet, + keychain_mask, + &wallet_config, + &tor_config, + &global_wallet_args, + test_mode, + ), + _ => { + let mut owner_api = Owner::new(wallet, None); + parse_and_execute( + &mut owner_api, + keychain_mask, &wallet_config, + &tor_config, &global_wallet_args, + &wallet_args, + test_mode, + false, + ) + } + }; + + if let Err(e) = res { + Err(e) + } else { + Ok(wallet_args.subcommand().0.to_owned()) + } +} + +pub fn parse_and_execute( + owner_api: &mut Owner, + keychain_mask: Option, + wallet_config: &WalletConfig, + tor_config: &TorConfig, + global_wallet_args: &command::GlobalArgs, + wallet_args: &ArgMatches, + test_mode: bool, + cli_mode: bool, +) -> Result<(), Error> +where + DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>, + L: WalletLCProvider<'static, C, K> + 'static, + C: NodeClient + 'static, + K: keychain::Keychain + 'static, +{ + let km = (&keychain_mask).as_ref(); + match wallet_args.subcommand() { + ("init", Some(args)) => { + let a = arg_parse!(parse_init_args( + owner_api.wallet_inst.clone(), + wallet_config, + global_wallet_args, &args, test_mode, )); - command::init(wallet, &global_wallet_args, a) + command::init(owner_api, &global_wallet_args, a) } ("recover", Some(_)) => { let a = arg_parse!(parse_recover_args(&global_wallet_args,)); - command::recover(wallet, a) + command::recover(owner_api, a) } ("listen", Some(args)) => { let mut c = wallet_config.clone(); let mut t = tor_config.clone(); let a = arg_parse!(parse_listen_args(&mut c, &mut t, &args)); command::listen( - wallet, + owner_api, Arc::new(Mutex::new(keychain_mask)), &c, &t, &a, &global_wallet_args.clone(), + cli_mode, ) } ("owner_api", Some(args)) => { @@ -985,47 +1036,47 @@ where let mut g = global_wallet_args.clone(); g.tls_conf = None; arg_parse!(parse_owner_api_args(&mut c, &args)); - command::owner_api(wallet, keychain_mask, &c, &tor_config, &g) + command::owner_api(owner_api, keychain_mask, &c, &tor_config, &g) } ("web", Some(_)) => command::owner_api( - wallet, + owner_api, keychain_mask, - &wallet_config, - &tor_config, - &global_wallet_args, + wallet_config, + tor_config, + global_wallet_args, ), ("account", Some(args)) => { let a = arg_parse!(parse_account_args(&args)); - command::account(wallet, km, a) + command::account(owner_api, km, a) } ("send", Some(args)) => { let a = arg_parse!(parse_send_args(&args)); command::send( - wallet, + owner_api, km, - Some(tor_config), + Some(tor_config.clone()), a, wallet_config.dark_background_color_scheme.unwrap_or(true), ) } ("receive", Some(args)) => { let a = arg_parse!(parse_receive_args(&args)); - command::receive(wallet, km, &global_wallet_args, a) + command::receive(owner_api, km, &global_wallet_args, a) } ("finalize", Some(args)) => { let a = arg_parse!(parse_finalize_args(&args)); - command::finalize(wallet, km, a) + command::finalize(owner_api, km, a) } ("invoice", Some(args)) => { let a = arg_parse!(parse_issue_invoice_args(&args)); - command::issue_invoice_tx(wallet, km, a) + command::issue_invoice_tx(owner_api, km, a) } ("pay", Some(args)) => { let a = arg_parse!(parse_process_invoice_args(&args, !test_mode)); command::process_invoice( - wallet, + owner_api, km, - Some(tor_config), + Some(tor_config.clone()), a, wallet_config.dark_background_color_scheme.unwrap_or(true), ) @@ -1033,15 +1084,15 @@ where ("info", Some(args)) => { let a = arg_parse!(parse_info_args(&args)); command::info( - wallet, + owner_api, km, - &global_wallet_args, + global_wallet_args, a, wallet_config.dark_background_color_scheme.unwrap_or(true), ) } ("outputs", Some(_)) => command::outputs( - wallet, + owner_api, km, &global_wallet_args, wallet_config.dark_background_color_scheme.unwrap_or(true), @@ -1049,7 +1100,7 @@ where ("txs", Some(args)) => { let a = arg_parse!(parse_txs_args(&args)); command::txs( - wallet, + owner_api, km, &global_wallet_args, a, @@ -1058,37 +1109,40 @@ where } ("post", Some(args)) => { let a = arg_parse!(parse_post_args(&args)); - command::post(wallet, km, a) + command::post(owner_api, km, a) } ("repost", Some(args)) => { let a = arg_parse!(parse_repost_args(&args)); - command::repost(wallet, km, a) + command::repost(owner_api, km, a) } ("cancel", Some(args)) => { let a = arg_parse!(parse_cancel_args(&args)); - command::cancel(wallet, km, a) + command::cancel(owner_api, km, a) } ("export_proof", Some(args)) => { let a = arg_parse!(parse_export_proof_args(&args)); - command::proof_export(wallet, km, a) + command::proof_export(owner_api, km, a) } ("verify_proof", Some(args)) => { let a = arg_parse!(parse_verify_proof_args(&args)); - command::proof_verify(wallet, km, a) + command::proof_verify(owner_api, km, a) } - ("address", Some(_)) => command::address(wallet, &global_wallet_args, km), + ("address", Some(_)) => command::address(owner_api, &global_wallet_args, km), ("scan", Some(args)) => { let a = arg_parse!(parse_check_args(&args)); - command::scan(wallet, km, a) + command::scan(owner_api, km, a) + } + ("open", Some(_)) => { + // for CLI mode only, should be handled externally + Ok(()) + } + ("close", Some(_)) => { + // for CLI mode only, should be handled externally + Ok(()) } _ => { let msg = format!("Unknown wallet command, use 'grin-wallet help' for details"); return Err(ErrorKind::ArgumentError(msg).into()); } - }; - if let Err(e) = res { - Err(e) - } else { - Ok(wallet_args.subcommand().0.to_owned()) } } diff --git a/src/lib.rs b/src/lib.rs index ac6bf6747..ff766d4b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,8 +11,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +#[macro_use] +extern crate lazy_static; +#[macro_use] +extern crate clap; + use grin_wallet_config as config; use grin_wallet_util::grin_api as api; use grin_wallet_util::grin_util as util; +mod cli; pub mod cmd; diff --git a/tests/cmd_line_basic.rs b/tests/cmd_line_basic.rs index 0eb5eb989..94c85b65c 100644 --- a/tests/cmd_line_basic.rs +++ b/tests/cmd_line_basic.rs @@ -151,10 +151,15 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller:: let (wallet1, mask1_i) = instantiate_wallet(wallet_config1, 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(()) - })?; + grin_wallet_controller::controller::owner_single_use( + Some(wallet1.clone()), + mask1, + None, + |api, m| { + api.set_active_account(m, "mining")?; + Ok(()) + }, + )?; let mut bh = 10u64; let _ = @@ -234,16 +239,21 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller:: 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); - for t in txs { - assert!(t.kernel_excess.is_some()); - } - Ok(()) - })?; + grin_wallet_controller::controller::owner_single_use( + Some(wallet1.clone()), + mask1, + None, + |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); + for t in txs { + assert!(t.kernel_excess.is_some()); + } + Ok(()) + }, + )?; let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 10, false); bh += 10; @@ -265,13 +275,18 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller:: )?; 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(()) - })?; + grin_wallet_controller::controller::owner_single_use( + Some(wallet2.clone()), + mask2, + None, + |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![ @@ -330,13 +345,18 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller:: )?; 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(()) - })?; + grin_wallet_controller::controller::owner_single_use( + Some(wallet1.clone()), + mask1, + None, + |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![ @@ -371,13 +391,18 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller:: )?; 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(()) - })?; + grin_wallet_controller::controller::owner_single_use( + Some(wallet1.clone()), + mask1, + None, + |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![ @@ -507,14 +532,19 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller:: // 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(()) - })?; + grin_wallet_controller::controller::owner_single_use( + Some(wallet2.clone()), + mask2, + None, + |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)?; diff --git a/tests/owner_v3_lifecycle.rs b/tests/owner_v3_lifecycle.rs index a7ea7ad0f..3651caf72 100644 --- a/tests/owner_v3_lifecycle.rs +++ b/tests/owner_v3_lifecycle.rs @@ -339,23 +339,28 @@ fn owner_v3_lifecycle() -> Result<(), grin_wallet_controller::Error> { let mut slate: Slate = res.unwrap().into(); // give this slate over to wallet 2 manually - grin_wallet_controller::controller::owner_single_use(wallet2.clone(), mask2, |api, m| { - let args = InitTxArgs { - src_acct_name: None, - amount: slate.amount, - minimum_confirmations: 1, - max_outputs: 500, - num_change_outputs: 1, - selection_strategy_is_use_all: false, - ..Default::default() - }; - let res = api.process_invoice_tx(m, &slate, args); - assert!(res.is_ok()); - slate = res.unwrap(); - api.tx_lock_outputs(m, &slate, 0)?; - - Ok(()) - })?; + grin_wallet_controller::controller::owner_single_use( + Some(wallet2.clone()), + mask2, + None, + |api, m| { + let args = InitTxArgs { + src_acct_name: None, + amount: slate.amount, + minimum_confirmations: 1, + max_outputs: 500, + num_change_outputs: 1, + selection_strategy_is_use_all: false, + ..Default::default() + }; + let res = api.process_invoice_tx(m, &slate, args); + assert!(res.is_ok()); + slate = res.unwrap(); + api.tx_lock_outputs(m, &slate, 0)?; + + Ok(()) + }, + )?; //16) Finalize the invoice tx (to foreign api) // (Tests that foreign API on same port also has its stored mask updated)