diff --git a/core/tests/pmmr.rs b/core/tests/pmmr.rs index 11e6b9801..9fa1b936f 100644 --- a/core/tests/pmmr.rs +++ b/core/tests/pmmr.rs @@ -13,10 +13,9 @@ // limitations under the License. //! PMMR tests -#[macro_use] -extern crate grin_core as core; extern crate chrono; extern crate croaring; +extern crate grin_core as core; mod vec_backend; diff --git a/doc/wallet/design/design.md b/doc/wallet/design/design.md index 462e227fa..6f8e2cd48 100644 --- a/doc/wallet/design/design.md +++ b/doc/wallet/design/design.md @@ -24,7 +24,7 @@ the code is organized into the following components (from highest-level to lowes * **libTx** - Library that provides lower-level transaction building, rangeproof and signing functions, highly-reusable by wallet implementors. * **Wallet Traits** - A set of generic traits defined within libWallet and the `keychain` crate . A wallet implementation such as Grin's current default only needs to implement these traits in order to provide a wallet: - * **WalletClient** - Defines communication between the wallet, a running grin node and/or other wallets + * **WalletToNodeClient** - Defines communication between the wallet, a running grin node and/or other wallets * **WalletBackend** - Defines the storage implementation of the wallet * **KeyChain** - Defines key derivation operations @@ -66,22 +66,22 @@ pub fn retrieve_outputs( ) -> Result, Error> where !·T: WalletBackend, -!·C: WalletClient, +!·C: WalletToNodeClient, !·K: Keychain, { ``` With `T` in this instance being a class that implements the `WalletBackend` trait, which is further parameterized with implementations of -`WalletClient` and `Keychain`. +`WalletToNodeClient` and `Keychain`. There is currently only a single implementation of the Keychain trait within the Grin code, in the `keychain` crate exported as `ExtKeyChain`. The `Keychain` trait makes several assumptions about the underlying implementation, particularly that it will adhere to a [BIP-38 style](https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki) 'master key -> child key' model. -There are two implementations of `WalletClient` within the code, the main version being the `HTTPWalletClient` found within `wallet/src/client.rs` and -the seconds a test client that communicates with an in-process instance of a chain. The WalletClient isolates all network calls, so upgrading wallet +There are two implementations of `WalletToNodeClient` within the code, the main version being the `HTTPWalletToNodeClient` found within `wallet/src/client.rs` and +the seconds a test client that communicates with an in-process instance of a chain. The WalletToNodeClient isolates all network calls, so upgrading wallet communication from the current simple http interaction to a more secure protocol (or allowing for many options) should be a simple -matter of dropping in different `WalletClient` implementations. +matter of dropping in different `WalletToNodeClient` implementations. There are also two implementations of `WalletBackend` within the code at the base of the `wallet` crate. `LMDBBackend` found within `wallet/src/lmdb_wallet.rs` is the main implementation, and is now used by all grin wallet commands. The earlier `FileWallet` still exists diff --git a/doc/wallet/design/wallet-arch.puml b/doc/wallet/design/wallet-arch.puml index 1b7895bd0..3bcea29db 100644 --- a/doc/wallet/design/wallet-arch.puml +++ b/doc/wallet/design/wallet-arch.puml @@ -40,7 +40,7 @@ folder "Provided by Grin" as services { package "Traits Implemented by Wallets" as traits { database "WalletBackend" as wallet_backend database "KeyChain" as keychain - component "WalletClient" as wallet_client + component "WalletToNodeClient" as wallet_client } note left of wallet_client diff --git a/servers/tests/framework/mod.rs b/servers/tests/framework/mod.rs index a8c111a1d..687fb2612 100644 --- a/servers/tests/framework/mod.rs +++ b/servers/tests/framework/mod.rs @@ -30,7 +30,7 @@ use std::{fs, thread, time}; use util::Mutex; use framework::keychain::Keychain; -use wallet::{HTTPWalletClient, LMDBBackend, WalletConfig}; +use wallet::{HTTPWalletToNodeClient, HTTPWalletToWalletClient, LMDBBackend, WalletConfig}; /// Just removes all results from previous runs pub fn clean_all_output(test_name_dir: &str) { @@ -265,19 +265,27 @@ impl LocalServerContainer { let _ = fs::create_dir_all(self.wallet_config.clone().data_file_dir); let r = wallet::WalletSeed::init_file(&self.wallet_config); - let client = HTTPWalletClient::new(&self.wallet_config.check_node_api_http_addr, None); + let client_n = + HTTPWalletToNodeClient::new(&self.wallet_config.check_node_api_http_addr, None); + let client_w = HTTPWalletToWalletClient::new(); if let Err(_e) = r { //panic!("Error initializing wallet seed: {}", e); } - let wallet: LMDBBackend = - LMDBBackend::new(self.wallet_config.clone(), "", client).unwrap_or_else(|e| { - panic!( - "Error creating wallet: {:?} Config: {:?}", - e, self.wallet_config - ) - }); + let wallet: LMDBBackend< + HTTPWalletToNodeClient, + HTTPWalletToWalletClient, + keychain::ExtKeychain, + > = + LMDBBackend::new(self.wallet_config.clone(), "", client_n, client_w).unwrap_or_else( + |e| { + panic!( + "Error creating wallet: {:?} Config: {:?}", + e, self.wallet_config + ) + }, + ); wallet::controller::foreign_listener( Arc::new(Mutex::new(wallet)), @@ -308,8 +316,9 @@ impl LocalServerContainer { let keychain: keychain::ExtKeychain = wallet_seed .derive_keychain("") .expect("Failed to derive keychain from seed file and passphrase."); - let client = HTTPWalletClient::new(&config.check_node_api_http_addr, None); - let mut wallet = LMDBBackend::new(config.clone(), "", client) + let client_n = HTTPWalletToNodeClient::new(&config.check_node_api_http_addr, None); + let client_w = HTTPWalletToWalletClient::new(); + let mut wallet = LMDBBackend::new(config.clone(), "", client_n, client_w) .unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config)); wallet.keychain = Some(keychain); let parent_id = keychain::ExtKeychain::derive_key_id(2, 0, 0, 0, 0); @@ -335,12 +344,13 @@ impl LocalServerContainer { .derive_keychain("") .expect("Failed to derive keychain from seed file and passphrase."); - let client = HTTPWalletClient::new(&config.check_node_api_http_addr, None); + let client_n = HTTPWalletToNodeClient::new(&config.check_node_api_http_addr, None); + let client_w = HTTPWalletToWalletClient::new(); let max_outputs = 500; let change_outputs = 1; - let mut wallet = LMDBBackend::new(config.clone(), "", client) + let mut wallet = LMDBBackend::new(config.clone(), "", client_n, client_w) .unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config)); wallet.keychain = Some(keychain); let _ = wallet::controller::owner_single_use(Arc::new(Mutex::new(wallet)), |api| { diff --git a/servers/tests/simulnet.rs b/servers/tests/simulnet.rs index 34c663e07..a87d407c0 100644 --- a/servers/tests/simulnet.rs +++ b/servers/tests/simulnet.rs @@ -40,8 +40,8 @@ use wallet::controller; use wallet::libtx::slate::Slate; use wallet::libwallet::types::{WalletBackend, WalletInst}; use wallet::lmdb_wallet::LMDBBackend; -use wallet::HTTPWalletClient; use wallet::WalletConfig; +use wallet::{HTTPWalletToNodeClient, HTTPWalletToWalletClient}; use framework::{ config, stop_all_servers, LocalServerContainerConfig, LocalServerContainerPool, @@ -880,15 +880,19 @@ fn long_fork_test_case_6(s: &Vec) { pub fn create_wallet( dir: &str, - client: HTTPWalletClient, -) -> Arc>> { + client_n: HTTPWalletToNodeClient, + client_w: HTTPWalletToWalletClient, +) -> Arc>> +{ let mut wallet_config = WalletConfig::default(); wallet_config.data_file_dir = String::from(dir); let _ = wallet::WalletSeed::init_file(&wallet_config); - let mut wallet: LMDBBackend = - LMDBBackend::new(wallet_config.clone(), "", client).unwrap_or_else(|e| { - panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config) - }); + let mut wallet: LMDBBackend< + HTTPWalletToNodeClient, + HTTPWalletToWalletClient, + keychain::ExtKeychain, + > = LMDBBackend::new(wallet_config.clone(), "", client_n, client_w) + .unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config)); wallet.open_with_credentials().unwrap_or_else(|e| { panic!( "Error initializing wallet: {:?} Config: {:?}", @@ -908,16 +912,26 @@ fn replicate_tx_fluff_failure() { // Create Wallet 1 (Mining Input) and start it listening // Wallet 1 post to another node, just for fun - let client1 = HTTPWalletClient::new("http://127.0.0.1:23003", None); - let wallet1 = create_wallet("target/tmp/tx_fluff/wallet1", client1.clone()); + let client1 = HTTPWalletToNodeClient::new("http://127.0.0.1:23003", None); + let client1_w = HTTPWalletToWalletClient::new(); + let wallet1 = create_wallet( + "target/tmp/tx_fluff/wallet1", + client1.clone(), + client1_w.clone(), + ); let wallet1_handle = thread::spawn(move || { controller::foreign_listener(wallet1, "127.0.0.1:33000", None) .unwrap_or_else(|e| panic!("Error creating wallet1 listener: {:?}", e,)); }); // Create Wallet 2 (Recipient) and launch - let client2 = HTTPWalletClient::new("http://127.0.0.1:23001", None); - let wallet2 = create_wallet("target/tmp/tx_fluff/wallet2", client2.clone()); + let client2 = HTTPWalletToNodeClient::new("http://127.0.0.1:23001", None); + let client2_w = HTTPWalletToWalletClient::new(); + let wallet2 = create_wallet( + "target/tmp/tx_fluff/wallet2", + client2.clone(), + client2_w.clone(), + ); let wallet2_handle = thread::spawn(move || { controller::foreign_listener(wallet2, "127.0.0.1:33001", None) .unwrap_or_else(|e| panic!("Error creating wallet2 listener: {:?}", e,)); @@ -957,7 +971,11 @@ fn replicate_tx_fluff_failure() { thread::sleep(time::Duration::from_secs(10)); // get another instance of wallet1 (to update contents and perform a send) - let wallet1 = create_wallet("target/tmp/tx_fluff/wallet1", client1.clone()); + let wallet1 = create_wallet( + "target/tmp/tx_fluff/wallet1", + client1.clone(), + client1_w.clone(), + ); let amount = 30_000_000_000; let mut slate = Slate::blank(1); @@ -980,7 +998,11 @@ fn replicate_tx_fluff_failure() { thread::sleep(time::Duration::from_secs(200)); // get another instance of wallet (to check contents) - let wallet2 = create_wallet("target/tmp/tx_fluff/wallet2", client2.clone()); + let wallet2 = create_wallet( + "target/tmp/tx_fluff/wallet2", + client2.clone(), + client2_w.clone(), + ); wallet::controller::owner_single_use(wallet2, |api| { let res = api.retrieve_summary_info(true).unwrap(); diff --git a/src/bin/cmd/wallet.rs b/src/bin/cmd/wallet.rs index 8d5b026e4..fb375f02e 100644 --- a/src/bin/cmd/wallet.rs +++ b/src/bin/cmd/wallet.rs @@ -29,7 +29,8 @@ use core::{core, global}; use grin_wallet::libwallet::ErrorKind; use grin_wallet::{self, controller, display, libwallet}; use grin_wallet::{ - HTTPWalletClient, LMDBBackend, WalletBackend, WalletConfig, WalletInst, WalletSeed, + HTTPWalletToNodeClient, HTTPWalletToWalletClient, LMDBBackend, WalletBackend, WalletConfig, + WalletInst, WalletSeed, }; use keychain; use servers::start_webwallet_server; @@ -56,10 +57,13 @@ pub fn instantiate_wallet( passphrase: &str, account: &str, node_api_secret: Option, -) -> Arc>> { - let client = HTTPWalletClient::new(&wallet_config.check_node_api_http_addr, node_api_secret); - let mut db_wallet = - LMDBBackend::new(wallet_config.clone(), passphrase, client).unwrap_or_else(|e| { +) -> Arc>> +{ + let client_n = + HTTPWalletToNodeClient::new(&wallet_config.check_node_api_http_addr, node_api_secret); + let client_w = HTTPWalletToWalletClient::new(); + let mut db_wallet = LMDBBackend::new(wallet_config.clone(), passphrase, client_n, client_w) + .unwrap_or_else(|e| { panic!( "Error creating DB wallet: {} Config: {:?}", e, wallet_config @@ -105,15 +109,19 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i if let ("init", Some(_)) = wallet_args.subcommand() { WalletSeed::init_file(&wallet_config).expect("Failed to init wallet seed file."); info!("Wallet seed file created"); - let client = - HTTPWalletClient::new(&wallet_config.check_node_api_http_addr, node_api_secret); - let _: LMDBBackend = - LMDBBackend::new(wallet_config.clone(), "", client).unwrap_or_else(|e| { - panic!( - "Error creating DB for wallet: {} Config: {:?}", - e, wallet_config - ); - }); + let client_n = + HTTPWalletToNodeClient::new(&wallet_config.check_node_api_http_addr, node_api_secret); + let client_w = HTTPWalletToWalletClient::new(); + let _: LMDBBackend< + HTTPWalletToNodeClient, + HTTPWalletToWalletClient, + keychain::ExtKeychain, + > = LMDBBackend::new(wallet_config.clone(), "", client_n, client_w).unwrap_or_else(|e| { + panic!( + "Error creating DB for wallet: {} Config: {:?}", + e, wallet_config + ); + }); info!("Wallet database backend created"); // give logging thread a moment to catch up thread::sleep(Duration::from_millis(200)); diff --git a/wallet/src/client.rs b/wallet/src/client.rs index 78b30a359..1012750ea 100644 --- a/wallet/src/client.rs +++ b/wallet/src/client.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Client functions, implementations of the WalletClient trait +//! Client functions, implementations of the WalletToNodeClient trait //! specific to the FileWallet use failure::ResultExt; @@ -30,22 +30,22 @@ use util; use util::secp::pedersen; #[derive(Clone)] -pub struct HTTPWalletClient { +pub struct HTTPWalletToNodeClient { node_url: String, node_api_secret: Option, } -impl HTTPWalletClient { +impl HTTPWalletToNodeClient { /// Create a new client that will communicate with the given grin node - pub fn new(node_url: &str, node_api_secret: Option) -> HTTPWalletClient { - HTTPWalletClient { + pub fn new(node_url: &str, node_api_secret: Option) -> HTTPWalletToNodeClient { + HTTPWalletToNodeClient { node_url: node_url.to_owned(), node_api_secret: node_api_secret, } } } -impl WalletClient for HTTPWalletClient { +impl WalletToNodeClient for HTTPWalletToNodeClient { fn node_url(&self) -> &str { &self.node_url } @@ -53,50 +53,6 @@ impl WalletClient for HTTPWalletClient { self.node_api_secret.clone() } - /// Call the wallet API to create a coinbase output for the given - /// block_fees. Will retry based on default "retry forever with backoff" - /// behavior. - fn create_coinbase( - &self, - dest: &str, - block_fees: &BlockFees, - ) -> Result { - let url = format!("{}/v1/wallet/foreign/build_coinbase", dest); - match single_create_coinbase(&url, &block_fees) { - Err(e) => { - error!( - "Failed to get coinbase from {}. Run grin wallet listen?", - url - ); - error!("Underlying Error: {}", e.cause().unwrap()); - error!("Backtrace: {}", e.backtrace().unwrap()); - Err(libwallet::ErrorKind::ClientCallback( - "Failed to get coinbase", - ))? - } - Ok(res) => Ok(res), - } - } - - /// Send the slate to a listening wallet instance - fn send_tx_slate(&self, dest: &str, slate: &Slate) -> Result { - if &dest[..4] != "http" { - let err_str = format!( - "dest formatted as {} but send -d expected stdout or http://IP:port", - dest - ); - error!("{}", err_str,); - Err(libwallet::ErrorKind::Uri)? - } - let url = format!("{}/v1/wallet/foreign/receive_tx", dest); - debug!("Posting transaction slate to {}", url); - - let res = api::client::post(url.as_str(), None, slate).context( - libwallet::ErrorKind::ClientCallback("Posting transaction slate"), - )?; - Ok(res) - } - /// Posts a transaction to a grin node fn post_tx(&self, tx: &TxWrapper, fluff: bool) -> Result<(), libwallet::Error> { let url; @@ -219,6 +175,63 @@ impl WalletClient for HTTPWalletClient { } } } + +#[derive(Clone)] +pub struct HTTPWalletToWalletClient {} + +impl HTTPWalletToWalletClient { + /// Create a new client that will communicate other wallets + pub fn new() -> HTTPWalletToWalletClient { + HTTPWalletToWalletClient {} + } +} + +impl WalletToWalletClient for HTTPWalletToWalletClient { + /// Call the wallet API to create a coinbase output for the given + /// block_fees. Will retry based on default "retry forever with backoff" + /// behavior. + fn create_coinbase( + &self, + dest: &str, + block_fees: &BlockFees, + ) -> Result { + let url = format!("{}/v1/wallet/foreign/build_coinbase", dest); + match single_create_coinbase(&url, &block_fees) { + Err(e) => { + error!( + "Failed to get coinbase from {}. Run grin wallet listen?", + url + ); + error!("Underlying Error: {}", e.cause().unwrap()); + error!("Backtrace: {}", e.backtrace().unwrap()); + Err(libwallet::ErrorKind::ClientCallback( + "Failed to get coinbase", + ))? + } + Ok(res) => Ok(res), + } + } + + /// Send the slate to a listening wallet instance + fn send_tx_slate(&self, dest: &str, slate: &Slate) -> Result { + if &dest[..4] != "http" { + let err_str = format!( + "dest formatted as {} but send -d expected stdout or http://IP:port", + dest + ); + error!("{}", err_str,); + Err(libwallet::ErrorKind::Uri)? + } + let url = format!("{}/v1/wallet/foreign/receive_tx", dest); + debug!("Posting transaction slate to {}", url); + + let res = api::client::post(url.as_str(), None, slate).context( + libwallet::ErrorKind::ClientCallback("Posting transaction slate"), + )?; + Ok(res) + } +} + /// Call the wallet API to create a coinbase output for the given block_fees. /// Will retry based on default "retry forever with backoff" behavior. pub fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result { diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 7764676a9..183f211e6 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -53,11 +53,12 @@ pub mod libwallet; pub mod lmdb_wallet; mod types; -pub use client::{create_coinbase, HTTPWalletClient}; +pub use client::{create_coinbase, HTTPWalletToNodeClient, HTTPWalletToWalletClient}; pub use error::{Error, ErrorKind}; pub use libwallet::controller; pub use libwallet::types::{ - BlockFees, CbData, WalletBackend, WalletClient, WalletInfo, WalletInst, + BlockFees, CbData, WalletBackend, WalletInfo, WalletInst, WalletToNodeClient, + WalletToWalletClient, }; pub use lmdb_wallet::{wallet_db_exists, LMDBBackend}; pub use types::{WalletConfig, WalletSeed, SEED_FILE}; diff --git a/wallet/src/libwallet/api.rs b/wallet/src/libwallet/api.rs index 19e95acc0..8ff9ab30f 100644 --- a/wallet/src/libwallet/api.rs +++ b/wallet/src/libwallet/api.rs @@ -33,7 +33,7 @@ use libtx::slate::Slate; use libwallet::internal::{keys, selection, tx, updater}; use libwallet::types::{ AcctPathMapping, BlockFees, CbData, OutputData, TxLogEntry, TxWrapper, WalletBackend, - WalletClient, WalletInfo, + WalletInfo, WalletToNodeClient, WalletToWalletClient, }; use libwallet::{Error, ErrorKind}; use util; @@ -41,10 +41,11 @@ use util::secp::pedersen; /// Wrapper around internal API functions, containing a reference to /// the wallet/keychain that they're acting upon -pub struct APIOwner +pub struct APIOwner where - W: WalletBackend, - C: WalletClient, + W: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { /// Wallet, contains its keychain (TODO: Split these up into 2 traits @@ -52,12 +53,14 @@ where pub wallet: Arc>, phantom: PhantomData, phantom_c: PhantomData, + phantom_l: PhantomData, } -impl APIOwner +impl APIOwner where - W: WalletBackend, - C: WalletClient, + W: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { /// Create new API instance @@ -66,6 +69,7 @@ where wallet: wallet_in, phantom: PhantomData, phantom_c: PhantomData, + phantom_l: PhantomData, } } @@ -172,7 +176,7 @@ where let mut slate_out: Slate; let lock_fn_out; - client = w.client().clone(); + client = w.w2w_client().clone(); let (slate, context, lock_fn) = tx::create_send_tx( &mut *w, amount, @@ -352,7 +356,8 @@ where &parent_key_id, )?; let tx_hex = util::to_hex(ser::ser_vec(&tx_burn).unwrap()); - w.client().post_tx(&TxWrapper { tx_hex: tx_hex }, false)?; + w.w2n_client() + .post_tx(&TxWrapper { tx_hex: tx_hex }, false)?; w.close()?; Ok(()) } @@ -362,7 +367,7 @@ where let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap()); let client = { let mut w = self.wallet.lock(); - w.client().clone() + w.w2n_client().clone() }; let res = client.post_tx(&TxWrapper { tx_hex: tx_hex }, fluff); if let Err(e) = res { @@ -423,7 +428,7 @@ where let mut w = self.wallet.lock(); w.open_with_credentials()?; let parent_key_id = w.parent_key_id(); - client = w.client().clone(); + client = w.w2n_client().clone(); let res = tx::retrieve_tx_hex(&mut *w, &parent_key_id, tx_id)?; w.close()?; res @@ -475,7 +480,7 @@ where let res = { let mut w = self.wallet.lock(); w.open_with_credentials()?; - w.client().get_chain_height() + w.w2n_client().get_chain_height() }; match res { Ok(height) => Ok((height, true)), @@ -502,10 +507,11 @@ where /// Wrapper around external API functions, intended to communicate /// with other parties -pub struct APIForeign +pub struct APIForeign where - W: WalletBackend, - C: WalletClient, + W: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { /// Wallet, contains its keychain (TODO: Split these up into 2 traits @@ -513,12 +519,14 @@ where pub wallet: Arc>, phantom: PhantomData, phantom_c: PhantomData, + phantom_l: PhantomData, } -impl<'a, W: ?Sized, C, K> APIForeign +impl<'a, W: ?Sized, C, L, K> APIForeign where - W: WalletBackend, - C: WalletClient, + W: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { /// Create new API instance @@ -527,6 +535,7 @@ where wallet: wallet_in, phantom: PhantomData, phantom_c: PhantomData, + phantom_l: PhantomData, }) } diff --git a/wallet/src/libwallet/controller.rs b/wallet/src/libwallet/controller.rs index dc9b87504..9305a0d52 100644 --- a/wallet/src/libwallet/controller.rs +++ b/wallet/src/libwallet/controller.rs @@ -25,7 +25,8 @@ use keychain::Keychain; use libtx::slate::Slate; use libwallet::api::{APIForeign, APIOwner}; use libwallet::types::{ - CbData, OutputData, SendTXArgs, TxLogEntry, WalletBackend, WalletClient, WalletInfo, + CbData, OutputData, SendTXArgs, TxLogEntry, WalletBackend, WalletInfo, WalletToNodeClient, + WalletToWalletClient, }; use libwallet::{Error, ErrorKind}; use serde::{Deserialize, Serialize}; @@ -41,11 +42,12 @@ use util::Mutex; /// 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>, f: F) -> Result<(), Error> +pub fn owner_single_use(wallet: Arc>, f: F) -> Result<(), Error> where - T: WalletBackend, - F: FnOnce(&mut APIOwner) -> Result<(), Error>, - C: WalletClient, + T: WalletBackend, + F: FnOnce(&mut APIOwner) -> Result<(), Error>, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { f(&mut APIOwner::new(wallet.clone()))?; @@ -54,11 +56,12 @@ where /// Instantiate wallet Foreign API for a single-use (command line) call /// Return a function containing a loaded API context to call -pub fn foreign_single_use(wallet: Arc>, f: F) -> Result<(), Error> +pub fn foreign_single_use(wallet: Arc>, f: F) -> Result<(), Error> where - T: WalletBackend, - F: FnOnce(&mut APIForeign) -> Result<(), Error>, - C: WalletClient, + T: WalletBackend, + F: FnOnce(&mut APIForeign) -> Result<(), Error>, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { f(&mut APIForeign::new(wallet.clone()))?; @@ -67,16 +70,17 @@ where /// Listener version, providing same API but listening for requests on a /// port and wrapping the calls -pub fn owner_listener( +pub fn owner_listener( wallet: Arc>, addr: &str, api_secret: Option, tls_config: Option, ) -> Result<(), Error> where - T: WalletBackend + Send + Sync + 'static, - OwnerAPIHandler: Handler, - C: WalletClient + 'static, + T: WalletBackend + Send + Sync + 'static, + OwnerAPIHandler: Handler, + C: WalletToNodeClient + 'static, + L: WalletToWalletClient + 'static, K: Keychain + 'static, { let api_handler = OwnerAPIHandler::new(wallet); @@ -108,14 +112,15 @@ where /// Listener version, providing same API but listening for requests on a /// port and wrapping the calls -pub fn foreign_listener( +pub fn foreign_listener( wallet: Arc>, addr: &str, tls_config: Option, ) -> Result<(), Error> where - T: WalletBackend + Send + Sync + 'static, - C: WalletClient + 'static, + T: WalletBackend + Send + Sync + 'static, + C: WalletToNodeClient + 'static, + L: WalletToWalletClient + 'static, K: Keychain + 'static, { let api_handler = ForeignAPIHandler::new(wallet); @@ -142,37 +147,41 @@ where type WalletResponseFuture = Box, Error = Error> + Send>; /// API Handler/Wrapper for owner functions -pub struct OwnerAPIHandler +pub struct OwnerAPIHandler where - T: WalletBackend + Send + Sync + 'static, - C: WalletClient + 'static, + T: WalletBackend + Send + Sync + 'static, + C: WalletToNodeClient + 'static, + L: WalletToWalletClient + 'static, K: Keychain + 'static, { /// Wallet instance pub wallet: Arc>, phantom: PhantomData, phantom_c: PhantomData, + phantom_l: PhantomData, } -impl OwnerAPIHandler +impl OwnerAPIHandler where - T: WalletBackend + Send + Sync + 'static, - C: WalletClient + 'static, + T: WalletBackend + Send + Sync + 'static, + C: WalletToNodeClient + 'static, + L: WalletToWalletClient + 'static, K: Keychain + 'static, { /// Create a new owner API handler for GET methods - pub fn new(wallet: Arc>) -> OwnerAPIHandler { + pub fn new(wallet: Arc>) -> OwnerAPIHandler { OwnerAPIHandler { wallet, phantom: PhantomData, phantom_c: PhantomData, + phantom_l: PhantomData, } } fn retrieve_outputs( &self, req: &Request, - api: APIOwner, + api: APIOwner, ) -> Result<(bool, Vec<(OutputData, pedersen::Commitment)>), Error> { let mut update_from_node = false; let mut id = None; @@ -196,7 +205,7 @@ where fn retrieve_txs( &self, req: &Request, - api: APIOwner, + api: APIOwner, ) -> Result<(bool, Vec), Error> { let mut id = None; let mut update_from_node = false; @@ -217,7 +226,7 @@ where fn dump_stored_tx( &self, req: &Request, - api: APIOwner, + api: APIOwner, ) -> Result { let params = parse_params(req); if let Some(id_string) = params.get("id") { @@ -246,7 +255,7 @@ where fn retrieve_summary_info( &self, req: &Request, - mut api: APIOwner, + mut api: APIOwner, ) -> Result<(bool, WalletInfo), Error> { let update_from_node = param_exists(req, "refresh"); api.retrieve_summary_info(update_from_node) @@ -255,7 +264,7 @@ where fn node_height( &self, _req: &Request, - mut api: APIOwner, + mut api: APIOwner, ) -> Result<(u64, bool), Error> { api.node_height() } @@ -283,7 +292,7 @@ where fn issue_send_tx( &self, req: Request, - mut api: APIOwner, + mut api: APIOwner, ) -> Box + Send> { Box::new(parse_body(req).and_then(move |args: SendTXArgs| { if args.method == "http" { @@ -315,7 +324,7 @@ where fn finalize_tx( &self, req: Request, - mut api: APIOwner, + mut api: APIOwner, ) -> Box + Send> { Box::new( parse_body(req).and_then(move |mut slate| match api.finalize_tx(&mut slate) { @@ -331,7 +340,7 @@ where fn cancel_tx( &self, req: Request, - mut api: APIOwner, + mut api: APIOwner, ) -> Box + Send> { let params = parse_params(&req); if let Some(id_string) = params.get("id") { @@ -360,7 +369,7 @@ where fn post_tx( &self, req: Request, - api: APIOwner, + api: APIOwner, ) -> Box + Send> { let params = match req.uri().query() { Some(query_string) => form_urlencoded::parse(query_string.as_bytes()) @@ -386,7 +395,7 @@ where fn issue_burn_tx( &self, _req: Request, - mut api: APIOwner, + mut api: APIOwner, ) -> Box + Send> { // TODO: Args Box::new(match api.issue_burn_tx(60, 10, 1000) { @@ -432,10 +441,11 @@ where } } -impl Handler for OwnerAPIHandler +impl Handler for OwnerAPIHandler where - T: WalletBackend + Send + Sync + 'static, - C: WalletClient + 'static, + T: WalletBackend + Send + Sync + 'static, + C: WalletToNodeClient + 'static, + L: WalletToWalletClient + 'static, K: Keychain + 'static, { fn get(&self, req: Request) -> ResponseFuture { @@ -466,37 +476,41 @@ where /// API Handler/Wrapper for foreign functions -pub struct ForeignAPIHandler +pub struct ForeignAPIHandler where - T: WalletBackend + Send + Sync + 'static, - C: WalletClient + 'static, + T: WalletBackend + Send + Sync + 'static, + C: WalletToNodeClient + 'static, + L: WalletToWalletClient + 'static, K: Keychain + 'static, { /// Wallet instance pub wallet: Arc>, phantom: PhantomData, phantom_c: PhantomData, + phantom_l: PhantomData, } -impl ForeignAPIHandler +impl ForeignAPIHandler where - T: WalletBackend + Send + Sync + 'static, - C: WalletClient + 'static, + T: WalletBackend + Send + Sync + 'static, + C: WalletToNodeClient + 'static, + L: WalletToWalletClient + 'static, K: Keychain + 'static, { /// create a new api handler - pub fn new(wallet: Arc>) -> ForeignAPIHandler { + pub fn new(wallet: Arc>) -> ForeignAPIHandler { ForeignAPIHandler { wallet, phantom: PhantomData, phantom_c: PhantomData, + phantom_l: PhantomData, } } fn build_coinbase( &self, req: Request, - mut api: APIForeign, + mut api: APIForeign, ) -> Box + Send> { Box::new(parse_body(req).and_then(move |block_fees| api.build_coinbase(&block_fees))) } @@ -504,7 +518,7 @@ where fn receive_tx( &self, req: Request, - mut api: APIForeign, + mut api: APIForeign, ) -> Box + Send> { Box::new( parse_body(req).and_then(move |mut slate| match api.receive_tx(&mut slate) { @@ -539,10 +553,11 @@ where } } } -impl Handler for ForeignAPIHandler +impl Handler for ForeignAPIHandler where - T: WalletBackend + Send + Sync + 'static, - C: WalletClient + Send + Sync + 'static, + T: WalletBackend + Send + Sync + 'static, + C: WalletToNodeClient + Send + Sync + 'static, + L: WalletToWalletClient + Send + Sync + 'static, K: Keychain + 'static, { fn post(&self, req: Request) -> ResponseFuture { diff --git a/wallet/src/libwallet/internal/keys.rs b/wallet/src/libwallet/internal/keys.rs index 2220ba0eb..2ed964e65 100644 --- a/wallet/src/libwallet/internal/keys.rs +++ b/wallet/src/libwallet/internal/keys.rs @@ -15,13 +15,14 @@ //! Wallet key management functions use keychain::{ChildNumber, ExtKeychain, Identifier, Keychain}; use libwallet::error::{Error, ErrorKind}; -use libwallet::types::{AcctPathMapping, WalletBackend, WalletClient}; +use libwallet::types::{AcctPathMapping, WalletBackend, WalletToNodeClient, WalletToWalletClient}; /// Get next available key in the wallet for a given parent -pub fn next_available_key(wallet: &mut T) -> Result +pub fn next_available_key(wallet: &mut T) -> Result where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let child = wallet.next_child()?; @@ -29,13 +30,14 @@ where } /// Retrieve an existing key from a wallet -pub fn retrieve_existing_key( +pub fn retrieve_existing_key( wallet: &T, key_id: Identifier, ) -> Result<(Identifier, u32), Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let existing = wallet.get(&key_id)?; @@ -45,20 +47,22 @@ where } /// Returns a list of account to BIP32 path mappings -pub fn accounts(wallet: &mut T) -> Result, Error> +pub fn accounts(wallet: &mut T) -> Result, Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { Ok(wallet.acct_path_iter().collect()) } /// Adds an new parent account path with a given label -pub fn new_acct_path(wallet: &mut T, label: &str) -> Result +pub fn new_acct_path(wallet: &mut T, label: &str) -> Result where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let label = label.to_owned(); @@ -96,14 +100,15 @@ where } /// Adds/sets a particular account path with a given label -pub fn set_acct_path( +pub fn set_acct_path( wallet: &mut T, label: &str, path: &Identifier, ) -> Result<(), Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let label = label.to_owned(); diff --git a/wallet/src/libwallet/internal/restore.rs b/wallet/src/libwallet/internal/restore.rs index bcb2c6015..490b01341 100644 --- a/wallet/src/libwallet/internal/restore.rs +++ b/wallet/src/libwallet/internal/restore.rs @@ -42,13 +42,14 @@ struct OutputResult { pub blinding: SecretKey, } -fn identify_utxo_outputs( +fn identify_utxo_outputs( wallet: &mut T, outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)>, ) -> Result, Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let mut wallet_outputs: Vec = Vec::new(); @@ -98,10 +99,11 @@ where } /// Restore a wallet -pub fn restore(wallet: &mut T) -> Result<(), Error> +pub fn restore(wallet: &mut T) -> Result<(), Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { // Don't proceed if wallet_data has anything in it @@ -118,7 +120,7 @@ where let mut result_vec: Vec = vec![]; loop { let (highest_index, last_retrieved_index, outputs) = wallet - .client() + .w2n_client() .get_outputs_by_pmmr_index(start_index, batch_size)?; info!( "Retrieved {} outputs, up to index {}. (Highest index: {})", diff --git a/wallet/src/libwallet/internal/selection.rs b/wallet/src/libwallet/internal/selection.rs index c118fc50a..bffc4d095 100644 --- a/wallet/src/libwallet/internal/selection.rs +++ b/wallet/src/libwallet/internal/selection.rs @@ -25,7 +25,7 @@ use libwallet::types::*; /// and saves the private wallet identifiers of our selected outputs /// into our transaction context -pub fn build_send_tx_slate( +pub fn build_send_tx_slate( wallet: &mut T, num_participants: usize, amount: u64, @@ -46,8 +46,9 @@ pub fn build_send_tx_slate( Error, > where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let (elems, inputs, change_amounts_derivations, amount, fee) = select_send_tx( @@ -144,7 +145,7 @@ where /// returning the key of the fresh output and a closure /// that actually performs the addition of the output to the /// wallet -pub fn build_recipient_output_with_slate( +pub fn build_recipient_output_with_slate( wallet: &mut T, slate: &mut Slate, parent_key_id: Identifier, @@ -158,8 +159,9 @@ pub fn build_recipient_output_with_slate( Error, > where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { // Create a potential output for this transaction @@ -217,7 +219,7 @@ where /// Builds a transaction to send to someone from the HD seed associated with the /// wallet and the amount to send. Handles reading through the wallet data file, /// selecting outputs to spend and building the change. -pub fn select_send_tx( +pub fn select_send_tx( wallet: &mut T, amount: u64, current_height: u64, @@ -238,8 +240,9 @@ pub fn select_send_tx( Error, > where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { // select some spendable coins from the wallet @@ -326,7 +329,7 @@ where } /// Selects inputs and change for a transaction -pub fn inputs_and_change( +pub fn inputs_and_change( coins: &Vec, wallet: &mut T, amount: u64, @@ -334,8 +337,9 @@ pub fn inputs_and_change( num_change_outputs: usize, ) -> Result<(Vec>>, Vec<(u64, Identifier)>), Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let mut parts = vec![]; @@ -397,7 +401,7 @@ where /// we should pass something other than a bool in. /// TODO: Possibly move this into another trait to be owned by a wallet? -pub fn select_coins( +pub fn select_coins( wallet: &mut T, amount: u64, current_height: u64, @@ -408,8 +412,9 @@ pub fn select_coins( ) -> (usize, Vec) // max_outputs_available, Outputs where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { // first find all eligible outputs based on number of confirmations diff --git a/wallet/src/libwallet/internal/tx.rs b/wallet/src/libwallet/internal/tx.rs index 23e419a45..b1b8bd7bb 100644 --- a/wallet/src/libwallet/internal/tx.rs +++ b/wallet/src/libwallet/internal/tx.rs @@ -23,20 +23,23 @@ use keychain::{Identifier, Keychain}; use libtx::slate::Slate; use libtx::{build, tx_fee}; use libwallet::internal::{selection, updater}; -use libwallet::types::{Context, TxLogEntryType, WalletBackend, WalletClient}; +use libwallet::types::{ + Context, TxLogEntryType, WalletBackend, WalletToNodeClient, WalletToWalletClient, +}; use libwallet::{Error, ErrorKind}; /// Receive a transaction, modifying the slate accordingly (which can then be /// sent back to sender for posting) -pub fn receive_tx( +pub fn receive_tx( wallet: &mut T, slate: &mut Slate, parent_key_id: &Identifier, is_self: bool, ) -> Result<(), Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { // create an output using the amount in the slate @@ -66,7 +69,7 @@ where /// Issue a new transaction to the provided sender by spending some of our /// wallet -pub fn create_send_tx( +pub fn create_send_tx( wallet: &mut T, amount: u64, minimum_confirmations: u64, @@ -84,12 +87,13 @@ pub fn create_send_tx( Error, > where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { // Get lock height - let current_height = wallet.client().get_chain_height()?; + let current_height = wallet.w2n_client().get_chain_height()?; // ensure outputs we're selecting are up to date updater::refresh_outputs(wallet, parent_key_id)?; @@ -130,14 +134,15 @@ where } /// Complete a transaction as the sender -pub fn complete_tx( +pub fn complete_tx( wallet: &mut T, slate: &mut Slate, context: &Context, ) -> Result<(), Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 0)?; @@ -150,14 +155,15 @@ where } /// Rollback outputs associated with a transaction in the wallet -pub fn cancel_tx( +pub fn cancel_tx( wallet: &mut T, parent_key_id: &Identifier, tx_id: u32, ) -> Result<(), Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let tx_vec = updater::retrieve_txs(wallet, Some(tx_id), &parent_key_id)?; @@ -180,14 +186,15 @@ where /// Retrieve the associated stored finalised hex Transaction for a given transaction Id /// as well as whether it's been confirmed -pub fn retrieve_tx_hex( +pub fn retrieve_tx_hex( wallet: &mut T, parent_key_id: &Identifier, tx_id: u32, ) -> Result<(bool, Option), Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let tx_vec = updater::retrieve_txs(wallet, Some(tx_id), parent_key_id)?; @@ -199,7 +206,7 @@ where } /// Issue a burn tx -pub fn issue_burn_tx( +pub fn issue_burn_tx( wallet: &mut T, amount: u64, minimum_confirmations: u64, @@ -207,8 +214,9 @@ pub fn issue_burn_tx( parent_key_id: &Identifier, ) -> Result where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { // TODO @@ -216,7 +224,7 @@ where // &Identifier::zero()); let keychain = wallet.keychain().clone(); - let current_height = wallet.client().get_chain_height()?; + let current_height = wallet.w2n_client().get_chain_height()?; let _ = updater::refresh_outputs(wallet, parent_key_id); diff --git a/wallet/src/libwallet/internal/updater.rs b/wallet/src/libwallet/internal/updater.rs index 3e2baa52e..cc6f8abc1 100644 --- a/wallet/src/libwallet/internal/updater.rs +++ b/wallet/src/libwallet/internal/updater.rs @@ -28,21 +28,22 @@ use libwallet::error::{Error, ErrorKind}; use libwallet::internal::keys; use libwallet::types::{ BlockFees, CbData, OutputData, OutputStatus, TxLogEntry, TxLogEntryType, WalletBackend, - WalletClient, WalletInfo, + WalletInfo, WalletToNodeClient, WalletToWalletClient, }; use util; use util::secp::pedersen; /// Retrieve all of the outputs (doesn't attempt to update from node) -pub fn retrieve_outputs( +pub fn retrieve_outputs( wallet: &mut T, show_spent: bool, tx_id: Option, parent_key_id: &Identifier, ) -> Result, Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { // just read the wallet here, no need for a write lock @@ -77,14 +78,15 @@ where } /// Retrieve all of the transaction entries, or a particular entry -pub fn retrieve_txs( +pub fn retrieve_txs( wallet: &mut T, tx_id: Option, parent_key_id: &Identifier, ) -> Result, Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { // just read the wallet here, no need for a write lock @@ -106,29 +108,31 @@ where } /// Refreshes the outputs in a wallet with the latest information /// from a node -pub fn refresh_outputs( +pub fn refresh_outputs( wallet: &mut T, parent_key_id: &Identifier, ) -> Result<(), Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { - let height = wallet.client().get_chain_height()?; + let height = wallet.w2n_client().get_chain_height()?; refresh_output_state(wallet, height, parent_key_id)?; Ok(()) } /// build a local map of wallet outputs keyed by commit /// and a list of outputs we want to query the node for -pub fn map_wallet_outputs( +pub fn map_wallet_outputs( wallet: &mut T, parent_key_id: &Identifier, ) -> Result, Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let mut wallet_outputs: HashMap = HashMap::new(); @@ -144,15 +148,16 @@ where } /// Cancel transaction and associated outputs -pub fn cancel_tx_and_outputs( +pub fn cancel_tx_and_outputs( wallet: &mut T, tx: TxLogEntry, outputs: Vec, parent_key_id: &Identifier, ) -> Result<(), libwallet::Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let mut batch = wallet.batch()?; @@ -180,7 +185,7 @@ where } /// Apply refreshed API output data to the wallet -pub fn apply_api_outputs( +pub fn apply_api_outputs( wallet: &mut T, wallet_outputs: &HashMap, api_outputs: &HashMap, @@ -188,8 +193,9 @@ pub fn apply_api_outputs( parent_key_id: &Identifier, ) -> Result<(), libwallet::Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { // now for each commit, find the output in the wallet and the corresponding @@ -260,14 +266,15 @@ where /// Builds a single api query to retrieve the latest output data from the node. /// So we can refresh the local wallet outputs. -fn refresh_output_state( +fn refresh_output_state( wallet: &mut T, height: u64, parent_key_id: &Identifier, ) -> Result<(), Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { debug!("Refreshing wallet outputs"); @@ -278,16 +285,19 @@ where let wallet_output_keys = wallet_outputs.keys().map(|commit| commit.clone()).collect(); - let api_outputs = wallet.client().get_outputs_from_node(wallet_output_keys)?; + let api_outputs = wallet + .w2n_client() + .get_outputs_from_node(wallet_output_keys)?; apply_api_outputs(wallet, &wallet_outputs, &api_outputs, height, parent_key_id)?; clean_old_unconfirmed(wallet, height)?; Ok(()) } -fn clean_old_unconfirmed(wallet: &mut T, height: u64) -> Result<(), Error> +fn clean_old_unconfirmed(wallet: &mut T, height: u64) -> Result<(), Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { if height < 500 { @@ -309,13 +319,14 @@ where /// Retrieve summary info about the wallet /// caller should refresh first if desired -pub fn retrieve_info( +pub fn retrieve_info( wallet: &mut T, parent_key_id: &Identifier, ) -> Result where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let current_height = wallet.last_confirmed_height()?; @@ -353,13 +364,14 @@ where } /// Build a coinbase output and insert into wallet -pub fn build_coinbase( +pub fn build_coinbase( wallet: &mut T, block_fees: &BlockFees, ) -> Result where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let (out, kern, block_fees) = receive_coinbase(wallet, block_fees).context(ErrorKind::Node)?; @@ -382,13 +394,14 @@ where //TODO: Split up the output creation and the wallet insertion /// Build a coinbase output and the corresponding kernel -pub fn receive_coinbase( +pub fn receive_coinbase( wallet: &mut T, block_fees: &BlockFees, ) -> Result<(Output, TxKernel, BlockFees), Error> where - T: WalletBackend, - C: WalletClient, + T: WalletBackend, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { let height = block_fees.height; diff --git a/wallet/src/libwallet/types.rs b/wallet/src/libwallet/types.rs index e00026656..17c5f99c2 100644 --- a/wallet/src/libwallet/types.rs +++ b/wallet/src/libwallet/types.rs @@ -38,16 +38,18 @@ use util::secp::key::{PublicKey, SecretKey}; use util::secp::{self, pedersen, Secp256k1}; /// Combined trait to allow dynamic wallet dispatch -pub trait WalletInst: WalletBackend + Send + Sync + 'static +pub trait WalletInst: WalletBackend + Send + Sync + 'static where - C: WalletClient, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { } -impl WalletInst for T +impl WalletInst for T where - T: WalletBackend + Send + Sync + 'static, - C: WalletClient, + T: WalletBackend + Send + Sync + 'static, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, {} @@ -55,9 +57,10 @@ where /// Wallets should implement this backend for their storage. All functions /// here expect that the wallet instance has instantiated itself or stored /// whatever credentials it needs -pub trait WalletBackend +pub trait WalletBackend where - C: WalletClient, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { /// Initialize with whatever stored credentials we have @@ -69,8 +72,11 @@ where /// Return the keychain being used fn keychain(&mut self) -> &mut K; - /// Return the client being used - fn client(&mut self) -> &mut C; + /// Return the client being used to communicate with the node + fn w2n_client(&mut self) -> &mut C; + + /// Return the client being used to communicate with other wallets + fn w2w_client(&mut self) -> &mut L; /// Set parent key id by stored account name fn set_parent_key_id_by_name(&mut self, label: &str) -> Result<(), Error>; @@ -181,21 +187,15 @@ where fn commit(&self) -> Result<(), Error>; } -/// Encapsulate all communication functions. No functions within libwallet +/// Encapsulate all wallet-node communication functions. No functions within libwallet /// should care about communication details -pub trait WalletClient: Sync + Send + Clone { +pub trait WalletToNodeClient: Sync + Send + Clone { /// Return the URL of the check node fn node_url(&self) -> &str; + /// Return the node api secret fn node_api_secret(&self) -> Option; - /// Call the wallet API to create a coinbase transaction - fn create_coinbase(&self, dest: &str, block_fees: &BlockFees) -> Result; - - /// Send a transaction slate to another listening wallet and return result - /// TODO: Probably need a slate wrapper type - fn send_tx_slate(&self, addr: &str, slate: &Slate) -> Result; - /// Posts a transaction to a grin node fn post_tx(&self, tx: &TxWrapper, fluff: bool) -> Result<(), Error>; @@ -228,6 +228,16 @@ pub trait WalletClient: Sync + Send + Clone { >; } +/// Encapsulate wallet to wallet communication functions +pub trait WalletToWalletClient: Sync + Send + Clone { + /// Call the wallet API to create a coinbase transaction + fn create_coinbase(&self, dest: &str, block_fees: &BlockFees) -> Result; + + /// Send a transaction slate to another listening wallet and return result + /// TODO: Probably need a slate wrapper type + fn send_tx_slate(&self, addr: &str, slate: &Slate) -> Result; +} + /// Information about an output that's being tracked by the wallet. Must be /// enough to reconstruct the commitment associated with the ouput when the /// root private key is known. diff --git a/wallet/src/lmdb_wallet.rs b/wallet/src/lmdb_wallet.rs index ea1836174..751f3b1cb 100644 --- a/wallet/src/lmdb_wallet.rs +++ b/wallet/src/lmdb_wallet.rs @@ -51,7 +51,7 @@ pub fn wallet_db_exists(config: WalletConfig) -> bool { db_path.exists() } -pub struct LMDBBackend { +pub struct LMDBBackend { db: store::Store, config: WalletConfig, /// passphrase: TODO better ways of dealing with this other than storing @@ -60,12 +60,19 @@ pub struct LMDBBackend { pub keychain: Option, /// Parent path to use by default for output operations parent_key_id: Identifier, - /// client - client: C, + /// wallet to node client + w2n_client: C, + /// w2w client + w2w_client: L, } -impl LMDBBackend { - pub fn new(config: WalletConfig, passphrase: &str, client: C) -> Result { +impl LMDBBackend { + pub fn new( + config: WalletConfig, + passphrase: &str, + n_client: C, + w_client: L, + ) -> Result { let db_path = path::Path::new(&config.data_file_dir).join(DB_DIR); fs::create_dir_all(&db_path).expect("Couldn't create wallet backend directory!"); @@ -75,7 +82,7 @@ impl LMDBBackend { // Make sure default wallet derivation path always exists let default_account = AcctPathMapping { label: "default".to_owned(), - path: LMDBBackend::::default_path(), + path: LMDBBackend::::default_path(), }; let acct_key = to_key( ACCOUNT_PATH_MAPPING_PREFIX, @@ -93,8 +100,9 @@ impl LMDBBackend { config: config.clone(), passphrase: String::from(passphrase), keychain: None, - parent_key_id: LMDBBackend::::default_path(), - client: client, + parent_key_id: LMDBBackend::::default_path(), + w2n_client: n_client, + w2w_client: w_client, }; Ok(res) } @@ -114,9 +122,10 @@ impl LMDBBackend { } } -impl WalletBackend for LMDBBackend +impl WalletBackend for LMDBBackend where - C: WalletClient, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { /// Initialise with whatever stored credentials we have @@ -141,9 +150,14 @@ where self.keychain.as_mut().unwrap() } - /// Return the client being used - fn client(&mut self) -> &mut C { - &mut self.client + /// Return the node client being used + fn w2n_client(&mut self) -> &mut C { + &mut self.w2n_client + } + + /// Return the wallet to wallet client being used + fn w2w_client(&mut self) -> &mut L { + &mut self.w2w_client } /// Set parent path by account name @@ -278,21 +292,21 @@ where /// An atomic batch in which all changes can be committed all at once or /// discarded on error. -pub struct Batch<'a, C: 'a, K: 'a> +pub struct Batch<'a, C: 'a, L: 'a, K: 'a> where - C: WalletClient, + C: WalletToNodeClient, K: Keychain, { - _store: &'a LMDBBackend, + _store: &'a LMDBBackend, db: RefCell>>, /// Keychain keychain: Option, } #[allow(missing_docs)] -impl<'a, C, K> WalletOutputBatch for Batch<'a, C, K> +impl<'a, C, L, K> WalletOutputBatch for Batch<'a, C, L, K> where - C: WalletClient, + C: WalletToNodeClient, K: Keychain, { fn keychain(&mut self) -> &mut K { diff --git a/wallet/tests/accounts.rs b/wallet/tests/accounts.rs index e6eb47197..4b2f7b982 100644 --- a/wallet/tests/accounts.rs +++ b/wallet/tests/accounts.rs @@ -51,17 +51,26 @@ fn setup(test_dir: &str) { fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> { setup(test_dir); // Create a new proxy to simulate server and wallet responses - let mut wallet_proxy: WalletProxy = WalletProxy::new(test_dir); + let mut wallet_proxy: WalletProxy = + WalletProxy::new(test_dir); let chain = wallet_proxy.chain.clone(); // Create a new wallet test client, and set its queues to communicate with the // proxy let client = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone()); - let wallet1 = common::create_wallet(&format!("{}/wallet1", test_dir), client.clone()); + let wallet1 = common::create_wallet( + &format!("{}/wallet1", test_dir), + client.clone(), + client.clone(), + ); wallet_proxy.add_wallet("wallet1", client.get_send_instance(), wallet1.clone()); // define recipient wallet, add to proxy - let wallet2 = common::create_wallet(&format!("{}/wallet2", test_dir), client.clone()); + let wallet2 = common::create_wallet( + &format!("{}/wallet2", test_dir), + client.clone(), + client.clone(), + ); let client = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone()); wallet_proxy.add_wallet("wallet2", client.get_send_instance(), wallet2.clone()); diff --git a/wallet/tests/common/mod.rs b/wallet/tests/common/mod.rs index f41a5167c..a0b09dfba 100644 --- a/wallet/tests/common/mod.rs +++ b/wallet/tests/common/mod.rs @@ -29,7 +29,9 @@ use chain::Chain; use core::core::{OutputFeatures, OutputIdentifier, Transaction}; use core::{consensus, global, pow, ser}; use wallet::libwallet; -use wallet::libwallet::types::{BlockFees, CbData, WalletClient, WalletInst}; +use wallet::libwallet::types::{ + BlockFees, CbData, WalletInst, WalletToNodeClient, WalletToWalletClient, +}; use wallet::lmdb_wallet::LMDBBackend; use wallet::{WalletBackend, WalletConfig}; @@ -113,13 +115,14 @@ pub fn add_block_with_reward(chain: &Chain, txs: Vec<&Transaction>, reward: CbDa /// adds a reward output to a wallet, includes that reward in a block, mines /// the block and adds it to the chain, with option transactions included. /// Helpful for building up precise wallet balances for testing. -pub fn award_block_to_wallet( +pub fn award_block_to_wallet( chain: &Chain, txs: Vec<&Transaction>, - wallet: Arc>>, + wallet: Arc>>, ) -> Result<(), libwallet::Error> where - C: WalletClient, + C: WalletToNodeClient, + L: WalletToWalletClient, K: keychain::Keychain, { // build block fees @@ -140,13 +143,14 @@ where } /// Award a blocks to a wallet directly -pub fn award_blocks_to_wallet( +pub fn award_blocks_to_wallet( chain: &Chain, - wallet: Arc>>, + wallet: Arc>>, number: usize, ) -> Result<(), libwallet::Error> where - C: WalletClient, + C: WalletToNodeClient, + L: WalletToWalletClient, K: keychain::Keychain, { for _ in 0..number { @@ -156,15 +160,20 @@ where } /// dispatch a db wallet -pub fn create_wallet(dir: &str, client: C) -> Arc>> +pub fn create_wallet( + dir: &str, + n_client: C, + w_client: L, +) -> Arc>> where - C: WalletClient + 'static, + C: WalletToNodeClient + 'static, + L: WalletToWalletClient + 'static, K: keychain::Keychain + 'static, { let mut wallet_config = WalletConfig::default(); wallet_config.data_file_dir = String::from(dir); let _ = wallet::WalletSeed::init_file(&wallet_config); - let mut wallet = LMDBBackend::new(wallet_config.clone(), "", client) + let mut wallet = LMDBBackend::new(wallet_config.clone(), "", n_client, w_client) .unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config)); wallet.open_with_credentials().unwrap_or_else(|e| { panic!( diff --git a/wallet/tests/common/testclient.rs b/wallet/tests/common/testclient.rs index 82ee32f8a..56a847727 100644 --- a/wallet/tests/common/testclient.rs +++ b/wallet/tests/common/testclient.rs @@ -63,9 +63,10 @@ pub struct WalletProxyMessage { /// communicates with a chain instance or other wallet /// listener APIs via message queues -pub struct WalletProxy +pub struct WalletProxy where - C: WalletClient, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { /// directory to create the chain in @@ -77,7 +78,7 @@ where String, ( Sender, - Arc>>, + Arc>>, ), >, /// simulate json send to another client @@ -91,11 +92,14 @@ where phantom_c: PhantomData, /// Phantom phantom_k: PhantomData, + /// Phantom + phantom_l: PhantomData, } -impl WalletProxy +impl WalletProxy where - C: WalletClient, + C: WalletToNodeClient, + L: WalletToWalletClient, K: Keychain, { /// Create a new client that will communicate with the given grin node @@ -124,6 +128,7 @@ where running: Arc::new(AtomicBool::new(false)), phantom_c: PhantomData, phantom_k: PhantomData, + phantom_l: PhantomData, }; retval } @@ -133,7 +138,7 @@ where &mut self, addr: &str, tx: Sender, - wallet: Arc>>, + wallet: Arc>>, ) { self.wallets.insert(addr.to_owned(), (tx, wallet)); } @@ -311,14 +316,7 @@ impl LocalWalletClient { } } -impl WalletClient for LocalWalletClient { - fn node_url(&self) -> &str { - "node" - } - fn node_api_secret(&self) -> Option { - None - } - +impl WalletToWalletClient for LocalWalletClient { /// Call the wallet API to create a coinbase output for the given /// block_fees. Will retry based on default "retry forever with backoff" /// behavior. @@ -352,6 +350,15 @@ impl WalletClient for LocalWalletClient { ))?, ) } +} + +impl WalletToNodeClient for LocalWalletClient { + fn node_url(&self) -> &str { + "node" + } + fn node_api_secret(&self) -> Option { + None + } /// Posts a transaction to a grin node /// In this case it will create a new block with award rewarded to diff --git a/wallet/tests/restore.rs b/wallet/tests/restore.rs index eb786bae4..3a441c33a 100644 --- a/wallet/tests/restore.rs +++ b/wallet/tests/restore.rs @@ -56,10 +56,11 @@ fn restore_wallet(base_dir: &str, wallet_dir: &str) -> Result<(), libwallet::Err let dest_seed = format!("{}/wallet.seed", dest_dir); fs::copy(source_seed, dest_seed)?; - let mut wallet_proxy: WalletProxy = WalletProxy::new(base_dir); + let mut wallet_proxy: WalletProxy = + WalletProxy::new(base_dir); let client = LocalWalletClient::new(wallet_dir, wallet_proxy.tx.clone()); - let wallet = common::create_wallet(&dest_dir, client.clone()); + let wallet = common::create_wallet(&dest_dir, client.clone(), client.clone()); wallet_proxy.add_wallet(wallet_dir, client.get_send_instance(), wallet.clone()); @@ -89,10 +90,11 @@ fn compare_wallet_restore( let source_dir = format!("{}/{}", base_dir, wallet_dir); let dest_dir = format!("{}/{}", base_dir, restore_name); - let mut wallet_proxy: WalletProxy = WalletProxy::new(base_dir); + let mut wallet_proxy: WalletProxy = + WalletProxy::new(base_dir); let client = LocalWalletClient::new(wallet_dir, wallet_proxy.tx.clone()); - let wallet_source = common::create_wallet(&source_dir, client.clone()); + let wallet_source = common::create_wallet(&source_dir, client.clone(), client.clone()); wallet_proxy.add_wallet( &wallet_dir, client.get_send_instance(), @@ -100,7 +102,7 @@ fn compare_wallet_restore( ); let client = LocalWalletClient::new(&restore_name, wallet_proxy.tx.clone()); - let wallet_dest = common::create_wallet(&dest_dir, client.clone()); + let wallet_dest = common::create_wallet(&dest_dir, client.clone(), client.clone()); wallet_proxy.add_wallet( &restore_name, client.get_send_instance(), @@ -182,18 +184,27 @@ fn compare_wallet_restore( fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> { setup(test_dir); // Create a new proxy to simulate server and wallet responses - let mut wallet_proxy: WalletProxy = WalletProxy::new(test_dir); + let mut wallet_proxy: WalletProxy = + WalletProxy::new(test_dir); let chain = wallet_proxy.chain.clone(); // Create a new wallet test client, and set its queues to communicate with the // proxy let client = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone()); - let wallet1 = common::create_wallet(&format!("{}/wallet1", test_dir), client.clone()); + let wallet1 = common::create_wallet( + &format!("{}/wallet1", test_dir), + client.clone(), + client.clone(), + ); wallet_proxy.add_wallet("wallet1", client.get_send_instance(), wallet1.clone()); // define recipient wallet, add to proxy let client = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone()); - let wallet2 = common::create_wallet(&format!("{}/wallet2", test_dir), client.clone()); + let wallet2 = common::create_wallet( + &format!("{}/wallet2", test_dir), + client.clone(), + client.clone(), + ); wallet_proxy.add_wallet("wallet2", client.get_send_instance(), wallet2.clone()); // wallet 2 will use another account @@ -211,7 +222,11 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> { // Another wallet let client = LocalWalletClient::new("wallet3", wallet_proxy.tx.clone()); - let wallet3 = common::create_wallet(&format!("{}/wallet3", test_dir), client.clone()); + let wallet3 = common::create_wallet( + &format!("{}/wallet3", test_dir), + client.clone(), + client.clone(), + ); wallet_proxy.add_wallet("wallet3", client.get_send_instance(), wallet3.clone()); // Set the wallet proxy listener running diff --git a/wallet/tests/self_send.rs b/wallet/tests/self_send.rs index b2ece0d4d..4e1cca790 100644 --- a/wallet/tests/self_send.rs +++ b/wallet/tests/self_send.rs @@ -51,17 +51,26 @@ fn setup(test_dir: &str) { fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> { setup(test_dir); // Create a new proxy to simulate server and wallet responses - let mut wallet_proxy: WalletProxy = WalletProxy::new(test_dir); + let mut wallet_proxy: WalletProxy = + WalletProxy::new(test_dir); let chain = wallet_proxy.chain.clone(); // Create a new wallet test client, and set its queues to communicate with the // proxy let client = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone()); - let wallet1 = common::create_wallet(&format!("{}/wallet1", test_dir), client.clone()); + let wallet1 = common::create_wallet( + &format!("{}/wallet1", test_dir), + client.clone(), + client.clone(), + ); wallet_proxy.add_wallet("wallet1", client.get_send_instance(), wallet1.clone()); // define recipient wallet, add to proxy - let wallet2 = common::create_wallet(&format!("{}/wallet2", test_dir), client.clone()); + let wallet2 = common::create_wallet( + &format!("{}/wallet2", test_dir), + client.clone(), + client.clone(), + ); let client = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone()); wallet_proxy.add_wallet("wallet2", client.get_send_instance(), wallet2.clone()); diff --git a/wallet/tests/transaction.rs b/wallet/tests/transaction.rs index 4a70f3b0c..3b2968c4f 100644 --- a/wallet/tests/transaction.rs +++ b/wallet/tests/transaction.rs @@ -49,23 +49,32 @@ fn setup(test_dir: &str) { global::set_mining_mode(ChainTypes::AutomatedTesting); } -/// Exercises the Transaction API fully with a test WalletClient operating +/// Exercises the Transaction API fully with a test WalletToNodeClient operating /// directly on a chain instance /// Callable with any type of wallet fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> { setup(test_dir); // Create a new proxy to simulate server and wallet responses - let mut wallet_proxy: WalletProxy = WalletProxy::new(test_dir); + let mut wallet_proxy: WalletProxy = + WalletProxy::new(test_dir); let chain = wallet_proxy.chain.clone(); // Create a new wallet test client, and set its queues to communicate with the // proxy let client = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone()); - let wallet1 = common::create_wallet(&format!("{}/wallet1", test_dir), client.clone()); + let wallet1 = common::create_wallet( + &format!("{}/wallet1", test_dir), + client.clone(), + client.clone(), + ); wallet_proxy.add_wallet("wallet1", client.get_send_instance(), wallet1.clone()); // define recipient wallet, add to proxy - let wallet2 = common::create_wallet(&format!("{}/wallet2", test_dir), client.clone()); + let wallet2 = common::create_wallet( + &format!("{}/wallet2", test_dir), + client.clone(), + client.clone(), + ); let client = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone()); wallet_proxy.add_wallet("wallet2", client.get_send_instance(), wallet2.clone()); @@ -296,18 +305,27 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> { fn tx_rollback(test_dir: &str) -> Result<(), libwallet::Error> { setup(test_dir); // Create a new proxy to simulate server and wallet responses - let mut wallet_proxy: WalletProxy = WalletProxy::new(test_dir); + let mut wallet_proxy: WalletProxy = + WalletProxy::new(test_dir); let chain = wallet_proxy.chain.clone(); // Create a new wallet test client, and set its queues to communicate with the // proxy let client = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone()); - let wallet1 = common::create_wallet(&format!("{}/wallet1", test_dir), client.clone()); + let wallet1 = common::create_wallet( + &format!("{}/wallet1", test_dir), + client.clone(), + client.clone(), + ); wallet_proxy.add_wallet("wallet1", client.get_send_instance(), wallet1.clone()); // define recipient wallet, add to proxy let client = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone()); - let wallet2 = common::create_wallet(&format!("{}/wallet2", test_dir), client.clone()); + let wallet2 = common::create_wallet( + &format!("{}/wallet2", test_dir), + client.clone(), + client.clone(), + ); wallet_proxy.add_wallet("wallet2", client.get_send_instance(), wallet2.clone()); // Set the wallet proxy listener running