From d5a6992be9f7a953e3c21600bee0d47d0dc08c73 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Mon, 9 Jul 2018 18:01:19 +0100 Subject: [PATCH] Wallet implementation dynamic dispatch (#1235) * add ability to instantiate wallets with different backend implementations * test fix --- servers/src/common/types.rs | 8 +- servers/tests/framework/mod.rs | 4 +- src/bin/grin.rs | 97 +++++++++++++++------- src/bin/tui/menu.rs | 12 ++- src/bin/tui/mining.rs | 12 +-- src/bin/tui/peers.rs | 2 +- src/bin/tui/status.rs | 2 +- src/bin/tui/table.rs | 2 +- src/bin/tui/types.rs | 2 +- src/bin/tui/ui.rs | 6 +- src/bin/tui/version.rs | 2 +- wallet/src/display.rs | 2 +- wallet/src/error.rs | 14 +++- wallet/src/file_wallet.rs | 8 +- wallet/src/lib.rs | 3 +- wallet/src/libtx/build.rs | 11 +-- wallet/src/libtx/slate.rs | 8 +- wallet/src/libwallet/api.rs | 48 ++++++----- wallet/src/libwallet/controller.rs | 85 ++++++++++--------- wallet/src/libwallet/error.rs | 7 +- wallet/src/libwallet/internal/keys.rs | 4 +- wallet/src/libwallet/internal/restore.rs | 4 +- wallet/src/libwallet/internal/selection.rs | 15 ++-- wallet/src/libwallet/internal/tx.rs | 8 +- wallet/src/libwallet/internal/updater.rs | 26 +++--- wallet/src/libwallet/types.rs | 13 +++ wallet/src/lmdb_wallet.rs | 16 +++- wallet/src/types.rs | 2 +- 28 files changed, 258 insertions(+), 165 deletions(-) diff --git a/servers/src/common/types.rs b/servers/src/common/types.rs index 751cd8743..24235f5ee 100644 --- a/servers/src/common/types.rs +++ b/servers/src/common/types.rs @@ -186,6 +186,9 @@ pub struct ServerConfig { /// Whether to run the web wallet owner listener pub run_wallet_owner_api: Option, + /// Whether to use the DB wallet backend implementation + pub use_db_wallet: Option, + /// Whether to run the test miner (internal, cuckoo 16) pub run_test_miner: Option, @@ -212,6 +215,7 @@ impl Default for ServerConfig { run_tui: None, run_wallet_listener: Some(false), run_wallet_owner_api: Some(false), + use_db_wallet: Some(false), run_test_miner: Some(false), test_miner_wallet_url: None, } @@ -323,9 +327,7 @@ impl SyncState { debug!( LOGGER, - "sync_state: sync_status: {:?} -> {:?}", - *status, - new_status, + "sync_state: sync_status: {:?} -> {:?}", *status, new_status, ); *status = new_status; diff --git a/servers/tests/framework/mod.rs b/servers/tests/framework/mod.rs index 61274ed1f..d85d393cb 100644 --- a/servers/tests/framework/mod.rs +++ b/servers/tests/framework/mod.rs @@ -274,7 +274,7 @@ impl LocalServerContainer { ) }); - wallet::controller::foreign_listener(wallet, &self.wallet_config.api_listen_addr()) + wallet::controller::foreign_listener(Box::new(wallet), &self.wallet_config.api_listen_addr()) .unwrap_or_else(|e| { panic!( "Error creating wallet listener: {:?} Config: {:?}", @@ -330,7 +330,7 @@ impl LocalServerContainer { .unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config)); wallet.keychain = Some(keychain); let _ = - wallet::controller::owner_single_use(&mut wallet, |api| { + wallet::controller::owner_single_use(Box::new(wallet), |api| { let result = api.issue_send_tx( amount, minimum_confirmations, diff --git a/src/bin/grin.rs b/src/bin/grin.rs index b8ea13790..e69c9f90d 100644 --- a/src/bin/grin.rs +++ b/src/bin/grin.rs @@ -40,8 +40,8 @@ pub mod tui; use std::env::current_dir; use std::process::exit; -use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; use std::thread; use std::time::Duration; @@ -51,10 +51,9 @@ use daemonize::Daemonize; use config::GlobalConfig; use core::core::amount_to_hr_string; use core::global; -use keychain::ExtKeychain; use tui::ui; use util::{init_logger, LoggingConfig, LOGGER}; -use wallet::{libwallet, FileWallet}; +use wallet::{libwallet, wallet_db_exists, FileWallet, LMDBBackend, WalletConfig, WalletInst}; // include build information pub mod built_info { @@ -296,8 +295,11 @@ fn main() { .about("basic wallet contents summary")) .subcommand(SubCommand::with_name("init") - .about("Initialize a new wallet seed file.")) - + .about("Initialize a new wallet seed file.") + .arg(Arg::with_name("db") + .help("Use database backend. (Default: false)") + .short("d") + .long("db"))) .subcommand(SubCommand::with_name("restore") .about("Attempt to restore wallet contents from the chain using seed and password. \ NOTE: Backup wallet.* and run `wallet listen` before running restore."))) @@ -437,19 +439,14 @@ fn server_command(server_args: Option<&ArgMatches>, mut global_config: GlobalCon } if let Some(true) = server_config.run_wallet_listener { + let use_db = server_config.use_db_wallet == Some(true); let mut wallet_config = global_config.members.as_ref().unwrap().wallet.clone(); - if let Err(_) = wallet::WalletSeed::from_file(&wallet_config) { - wallet::WalletSeed::init_file(&wallet_config) - .expect("Failed to create wallet seed file."); - }; + init_wallet_seed(wallet_config.clone()); let _ = thread::Builder::new() .name("wallet_listener".to_string()) .spawn(move || { - let wallet: FileWallet = - FileWallet::new(wallet_config.clone(), "").unwrap_or_else(|e| { - panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config) - }); + let wallet = instantiate_wallet(wallet_config.clone(), "", use_db); wallet::controller::foreign_listener(wallet, &wallet_config.api_listen_addr()) .unwrap_or_else(|e| { panic!( @@ -460,19 +457,14 @@ fn server_command(server_args: Option<&ArgMatches>, mut global_config: GlobalCon }); } if let Some(true) = server_config.run_wallet_owner_api { + let use_db = server_config.use_db_wallet == Some(true); let mut wallet_config = global_config.members.unwrap().wallet; - if let Err(_) = wallet::WalletSeed::from_file(&wallet_config) { - wallet::WalletSeed::init_file(&wallet_config) - .expect("Failed to create wallet seed file."); - }; + init_wallet_seed(wallet_config.clone()); let _ = thread::Builder::new() .name("wallet_owner_listener".to_string()) .spawn(move || { - let wallet: FileWallet = FileWallet::new(wallet_config.clone(), "") - .unwrap_or_else(|e| { - panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config) - }); + let wallet = instantiate_wallet(wallet_config.clone(), "", use_db); wallet::controller::owner_listener(wallet, "127.0.0.1:13420").unwrap_or_else(|e| { panic!( "Error creating wallet api listener: {:?} Config: {:?}", @@ -551,6 +543,34 @@ fn client_command(client_args: &ArgMatches, global_config: GlobalConfig) { } } +fn init_wallet_seed(wallet_config: WalletConfig) { + if let Err(_) = wallet::WalletSeed::from_file(&wallet_config) { + wallet::WalletSeed::init_file(&wallet_config).expect("Failed to create wallet seed file."); + }; +} + +fn instantiate_wallet( + wallet_config: WalletConfig, + passphrase: &str, + use_db: bool, +) -> Box> { + if use_db { + let db_wallet = LMDBBackend::new(wallet_config.clone(), "").unwrap_or_else(|e| { + panic!( + "Error creating DB wallet: {} Config: {:?}", + e, wallet_config + ); + }); + info!(LOGGER, "Using LMDB Backend for wallet"); + Box::new(db_wallet) + } else { + let file_wallet = FileWallet::new(wallet_config.clone(), passphrase) + .unwrap_or_else(|e| panic!("Error creating wallet: {} Config: {:?}", e, wallet_config)); + info!(LOGGER, "Using File Backend for wallet"); + Box::new(file_wallet) + } +} + fn wallet_command(wallet_args: &ArgMatches, global_config: GlobalConfig) { // just get defaults from the global config let mut wallet_config = global_config.members.unwrap().wallet; @@ -574,8 +594,26 @@ fn wallet_command(wallet_args: &ArgMatches, global_config: GlobalConfig) { // Derive the keychain based on seed from seed file and specified passphrase. // Generate the initial wallet seed if we are running "wallet init". - if let ("init", Some(_)) = wallet_args.subcommand() { + if let ("init", Some(init_args)) = wallet_args.subcommand() { + let mut use_db = false; + if init_args.is_present("db") { + info!(LOGGER, "Use db"); + use_db = true; + } wallet::WalletSeed::init_file(&wallet_config).expect("Failed to init wallet seed file."); + info!(LOGGER, "Wallet seed file created"); + if use_db { + let _: LMDBBackend = LMDBBackend::new(wallet_config.clone(), "") + .unwrap_or_else(|e| { + panic!( + "Error creating DB wallet: {} Config: {:?}", + e, wallet_config + ); + }); + info!(LOGGER, "Wallet database backend created"); + } + // give logging thread a moment to catch up + thread::sleep(Duration::from_millis(200)); // we are done here with creating the wallet, so just return return; } @@ -584,12 +622,12 @@ fn wallet_command(wallet_args: &ArgMatches, global_config: GlobalConfig) { .value_of("pass") .expect("Failed to read passphrase."); + // use database if one exists, otherwise use file + let use_db = wallet_db_exists(wallet_config.clone()); + // Handle listener startup commands { - let wallet: FileWallet = - FileWallet::new(wallet_config.clone(), passphrase).unwrap_or_else(|e| { - panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config) - }); + let wallet = instantiate_wallet(wallet_config.clone(), passphrase, use_db); match wallet_args.subcommand() { ("listen", Some(listen_args)) => { if let Some(port) = listen_args.value_of("port") { @@ -617,11 +655,8 @@ fn wallet_command(wallet_args: &ArgMatches, global_config: GlobalConfig) { // Handle single-use (command line) owner commands { - let mut wallet: FileWallet = - FileWallet::new(wallet_config.clone(), passphrase).unwrap_or_else(|e| { - panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config) - }); - let _res = wallet::controller::owner_single_use(&mut wallet, |api| { + let wallet = instantiate_wallet(wallet_config.clone(), passphrase, use_db); + let _res = wallet::controller::owner_single_use(wallet, |api| { match wallet_args.subcommand() { ("send", Some(send_args)) => { let amount = send_args diff --git a/src/bin/tui/menu.rs b/src/bin/tui/menu.rs index 67cb9a332..0da1f96ec 100644 --- a/src/bin/tui/menu.rs +++ b/src/bin/tui/menu.rs @@ -14,16 +14,20 @@ //! Main Menu definition -use cursive::Cursive; use cursive::align::HAlign; use cursive::direction::Orientation; use cursive::event::{EventResult, Key}; use cursive::view::Identifiable; use cursive::view::View; -use cursive::views::{BoxView, LinearLayout, OnEventView, SelectView, StackView, TextView, ViewRef}; +use cursive::views::{ + BoxView, LinearLayout, OnEventView, SelectView, StackView, TextView, ViewRef, +}; +use cursive::Cursive; -use tui::constants::{MAIN_MENU, ROOT_STACK, SUBMENU_MINING_BUTTON, VIEW_BASIC_STATUS, VIEW_MINING, - VIEW_PEER_SYNC, VIEW_VERSION}; +use tui::constants::{ + MAIN_MENU, ROOT_STACK, SUBMENU_MINING_BUTTON, VIEW_BASIC_STATUS, VIEW_MINING, VIEW_PEER_SYNC, + VIEW_VERSION, +}; pub fn create() -> Box { let mut main_menu = SelectView::new().h_align(HAlign::Left).with_id(MAIN_MENU); diff --git a/src/bin/tui/mining.rs b/src/bin/tui/mining.rs index cee834f9c..25621743a 100644 --- a/src/bin/tui/mining.rs +++ b/src/bin/tui/mining.rs @@ -16,18 +16,20 @@ use std::cmp::Ordering; -use cursive::Cursive; use cursive::direction::Orientation; use cursive::event::Key; use cursive::traits::{Boxable, Identifiable}; use cursive::view::View; -use cursive::views::{BoxView, Button, Dialog, LinearLayout, OnEventView, Panel, StackView, - TextView}; +use cursive::views::{ + BoxView, Button, Dialog, LinearLayout, OnEventView, Panel, StackView, TextView, +}; +use cursive::Cursive; use std::time; use tui::chrono::prelude::{DateTime, NaiveDateTime, Utc}; -use tui::constants::{MAIN_MENU, SUBMENU_MINING_BUTTON, TABLE_MINING_DIFF_STATUS, - TABLE_MINING_STATUS, VIEW_MINING}; +use tui::constants::{ + MAIN_MENU, SUBMENU_MINING_BUTTON, TABLE_MINING_DIFF_STATUS, TABLE_MINING_STATUS, VIEW_MINING, +}; use tui::types::TUIStatusListener; use servers::{DiffBlock, ServerStats, WorkerStats}; diff --git a/src/bin/tui/peers.rs b/src/bin/tui/peers.rs index 8062f5bfe..40a2f11d1 100644 --- a/src/bin/tui/peers.rs +++ b/src/bin/tui/peers.rs @@ -18,11 +18,11 @@ use std::cmp::Ordering; use servers::{PeerStats, ServerStats}; -use cursive::Cursive; use cursive::direction::Orientation; use cursive::traits::{Boxable, Identifiable}; use cursive::view::View; use cursive::views::{BoxView, Dialog, LinearLayout, TextView}; +use cursive::Cursive; use tui::constants::{TABLE_PEER_STATUS, VIEW_PEER_SYNC}; use tui::table::{TableView, TableViewItem}; diff --git a/src/bin/tui/status.rs b/src/bin/tui/status.rs index 6d02afbe2..18a83bb78 100644 --- a/src/bin/tui/status.rs +++ b/src/bin/tui/status.rs @@ -14,11 +14,11 @@ //! Basic status view definition -use cursive::Cursive; use cursive::direction::Orientation; use cursive::traits::Identifiable; use cursive::view::View; use cursive::views::{BoxView, LinearLayout, TextView}; +use cursive::Cursive; use tui::constants::VIEW_BASIC_STATUS; use tui::types::TUIStatusListener; diff --git a/src/bin/tui/table.rs b/src/bin/tui/table.rs index a158841cf..76e9d6f1e 100644 --- a/src/bin/tui/table.rs +++ b/src/bin/tui/table.rs @@ -54,7 +54,6 @@ use std::hash::Hash; use std::rc::Rc; // External Dependencies ------------------------------------------------------ -use cursive::With; use cursive::align::HAlign; use cursive::direction::Direction; use cursive::event::{Callback, Event, EventResult, Key}; @@ -62,6 +61,7 @@ use cursive::theme::ColorStyle; use cursive::theme::PaletteColor::{Highlight, HighlightInactive, Primary}; use cursive::vec::Vec2; use cursive::view::{ScrollBase, View}; +use cursive::With; use cursive::{Cursive, Printer}; /// A trait for displaying and sorting items inside a diff --git a/src/bin/tui/types.rs b/src/bin/tui/types.rs index f899b4dd6..24ec4001a 100644 --- a/src/bin/tui/types.rs +++ b/src/bin/tui/types.rs @@ -14,8 +14,8 @@ //! Types specific to the UI module -use cursive::Cursive; use cursive::view::View; +use cursive::Cursive; use servers::ServerStats; /// Main message struct to communicate between the UI and diff --git a/src/bin/tui/ui.rs b/src/bin/tui/ui.rs index e73b5315d..e53fc781b 100644 --- a/src/bin/tui/ui.rs +++ b/src/bin/tui/ui.rs @@ -19,15 +19,17 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{mpsc, Arc}; use time; -use cursive::Cursive; use cursive::direction::Orientation; use cursive::theme::BaseColor::{Black, Blue, Cyan, White}; use cursive::theme::Color::Dark; -use cursive::theme::PaletteColor::{Background, Highlight, HighlightInactive, Primary, Shadow, View}; +use cursive::theme::PaletteColor::{ + Background, Highlight, HighlightInactive, Primary, Shadow, View, +}; use cursive::theme::{BaseColor, BorderStyle, Color, Theme}; use cursive::traits::Identifiable; use cursive::utils::markup::StyledString; use cursive::views::{LinearLayout, Panel, StackView, TextView, ViewBox}; +use cursive::Cursive; use servers::Server; diff --git a/src/bin/tui/version.rs b/src/bin/tui/version.rs index ca277a427..e8f63d4f3 100644 --- a/src/bin/tui/version.rs +++ b/src/bin/tui/version.rs @@ -14,11 +14,11 @@ //! Version and build info -use cursive::Cursive; use cursive::direction::Orientation; use cursive::traits::Identifiable; use cursive::view::View; use cursive::views::{BoxView, LinearLayout, TextView}; +use cursive::Cursive; use tui::constants::VIEW_VERSION; use tui::types::TUIStatusListener; diff --git a/wallet/src/display.rs b/wallet/src/display.rs index 08060fd5f..f740a8219 100644 --- a/wallet/src/display.rs +++ b/wallet/src/display.rs @@ -13,8 +13,8 @@ // limitations under the License. use core::core::{self, amount_to_hr_string}; -use libwallet::Error; use libwallet::types::{OutputData, WalletInfo}; +use libwallet::Error; use prettytable; use std::io::prelude::Write; use term; diff --git a/wallet/src/error.rs b/wallet/src/error.rs index e79d80464..e4d244dd8 100644 --- a/wallet/src/error.rs +++ b/wallet/src/error.rs @@ -104,7 +104,19 @@ impl Fail for Error { impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Display::fmt(&self.inner, f) + let cause = match self.cause() { + Some(c) => format!("{}", c), + None => String::from("Unknown"), + }; + let backtrace = match self.backtrace() { + Some(b) => format!("{}", b), + None => String::from("Unknown"), + }; + let output = format!( + "{} \n Cause: {} \n Backtrace: {}", + self.inner, cause, backtrace + ); + Display::fmt(&output, f) } } diff --git a/wallet/src/file_wallet.rs b/wallet/src/file_wallet.rs index 02cba82b6..e87e3c426 100644 --- a/wallet/src/file_wallet.rs +++ b/wallet/src/file_wallet.rs @@ -19,14 +19,14 @@ use std::path::{Path, MAIN_SEPARATOR}; use serde_json; use tokio_core::reactor; -use tokio_retry::Retry; use tokio_retry::strategy::FibonacciBackoff; +use tokio_retry::Retry; use failure::ResultExt; use keychain::{self, Identifier, Keychain}; -use util::LOGGER; use util::secp::pedersen; +use util::LOGGER; use error::{Error, ErrorKind}; @@ -35,8 +35,8 @@ use libtx::slate::Slate; use libwallet; use libwallet::types::{ - BlockFees, CbData, OutputData, TxWrapper, WalletBackend, - WalletClient, WalletDetails, WalletOutputBatch, + BlockFees, CbData, OutputData, TxWrapper, WalletBackend, WalletClient, WalletDetails, + WalletOutputBatch, }; use types::{WalletConfig, WalletSeed}; diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index c44b9004d..50fd81778 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -60,5 +60,6 @@ pub use client::create_coinbase; pub use error::{Error, ErrorKind}; pub use file_wallet::FileWallet; pub use libwallet::controller; -pub use libwallet::types::{BlockFees, CbData, WalletInfo}; +pub use libwallet::types::{BlockFees, CbData, WalletInfo, WalletInst}; +pub use lmdb_wallet::{wallet_db_exists, LMDBBackend}; pub use types::{WalletConfig, WalletSeed}; diff --git a/wallet/src/libtx/build.rs b/wallet/src/libtx/build.rs index 01f424039..9828136aa 100644 --- a/wallet/src/libtx/build.rs +++ b/wallet/src/libtx/build.rs @@ -47,11 +47,7 @@ pub type Append = for<'a> Fn(&'a mut Context, (Transaction, TxKernel, Blin /// Adds an input with the provided value and blinding key to the transaction /// being built. -fn build_input( - value: u64, - features: OutputFeatures, - key_id: Identifier, -) -> Box> +fn build_input(value: u64, features: OutputFeatures, key_id: Identifier) -> Box> where K: Keychain, { @@ -78,10 +74,7 @@ where } /// Adds a coinbase input spending a coinbase output. -pub fn coinbase_input( - value: u64, - key_id: Identifier, -) -> Box> +pub fn coinbase_input(value: u64, key_id: Identifier) -> Box> where K: Keychain, { diff --git a/wallet/src/libtx/slate.rs b/wallet/src/libtx/slate.rs index 5ffd62d93..10280345f 100644 --- a/wallet/src/libtx/slate.rs +++ b/wallet/src/libtx/slate.rs @@ -23,8 +23,8 @@ use keychain::{BlindSum, BlindingFactor, Keychain}; use libtx::error::{Error, ErrorKind}; use libtx::{aggsig, build, tx_fee}; -use util::secp::Signature; use util::secp::key::{PublicKey, SecretKey}; +use util::secp::Signature; use util::{secp, LOGGER}; /// Public data for each participant in the slate @@ -261,11 +261,7 @@ impl Slate { // double check the fee amount included in the partial tx // we don't necessarily want to just trust the sender // we could just overwrite the fee here (but we won't) due to the sig - let fee = tx_fee( - self.tx.inputs.len(), - self.tx.outputs.len(), - None, - ); + let fee = tx_fee(self.tx.inputs.len(), self.tx.outputs.len(), None); if fee > self.tx.fee() { return Err(ErrorKind::Fee( format!("Fee Dispute Error: {}, {}", self.tx.fee(), fee,).to_string(), diff --git a/wallet/src/libwallet/api.rs b/wallet/src/libwallet/api.rs index 1386816e4..fec15a850 100644 --- a/wallet/src/libwallet/api.rs +++ b/wallet/src/libwallet/api.rs @@ -22,32 +22,33 @@ use std::marker::PhantomData; use core::ser; use keychain::Keychain; use libtx::slate::Slate; -use libwallet::Error; use libwallet::internal::{tx, updater}; -use libwallet::types::{BlockFees, CbData, OutputData, TxWrapper, WalletBackend, WalletClient, - WalletInfo}; +use libwallet::types::{ + BlockFees, CbData, OutputData, TxWrapper, WalletBackend, WalletClient, WalletInfo, +}; +use libwallet::Error; use util::{self, LOGGER}; /// Wrapper around internal API functions, containing a reference to /// the wallet/keychain that they're acting upon -pub struct APIOwner<'a, W, K> +pub struct APIOwner<'a, W: ?Sized, K> where W: 'a + WalletBackend + WalletClient, K: Keychain, { /// Wallet, contains its keychain (TODO: Split these up into 2 traits /// perhaps) - pub wallet: &'a mut W, + pub wallet: &'a mut Box, phantom: PhantomData, } -impl<'a, W, K> APIOwner<'a, W, K> +impl<'a, W: ?Sized, K> APIOwner<'a, W, K> where W: 'a + WalletBackend + WalletClient, K: Keychain, { /// Create new API instance - pub fn new(wallet_in: &'a mut W) -> APIOwner<'a, W, K> { + pub fn new(wallet_in: &'a mut Box) -> APIOwner<'a, W, K> { APIOwner { wallet: wallet_in, phantom: PhantomData, @@ -67,7 +68,7 @@ where } Ok(( validated, - updater::retrieve_outputs(self.wallet, include_spent)?, + updater::retrieve_outputs(&mut **self.wallet, include_spent)?, )) } @@ -80,7 +81,7 @@ where if refresh_from_node { validated = self.update_outputs(); } - let wallet_info = updater::retrieve_info(self.wallet)?; + let wallet_info = updater::retrieve_info(&mut **self.wallet)?; Ok((validated, wallet_info)) } @@ -95,7 +96,7 @@ where fluff: bool, ) -> Result<(), Error> { let (slate, context, lock_fn) = tx::create_send_tx( - self.wallet, + &mut **self.wallet, amount, minimum_confirmations, max_outputs, @@ -114,7 +115,7 @@ where } }; - tx::complete_tx(self.wallet, &mut slate, &context)?; + tx::complete_tx(&mut **self.wallet, &mut slate, &context)?; // All good here, so let's post it let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap()); @@ -132,7 +133,12 @@ where minimum_confirmations: u64, max_outputs: usize, ) -> Result<(), Error> { - let tx_burn = tx::issue_burn_tx(self.wallet, amount, minimum_confirmations, max_outputs)?; + let tx_burn = tx::issue_burn_tx( + &mut **self.wallet, + amount, + minimum_confirmations, + max_outputs, + )?; let tx_hex = util::to_hex(ser::ser_vec(&tx_burn).unwrap()); self.wallet.post_tx(&TxWrapper { tx_hex: tx_hex }, false)?; Ok(()) @@ -160,7 +166,7 @@ where /// Attempt to update outputs in wallet, return whether it was successful fn update_outputs(&mut self) -> bool { - match updater::refresh_outputs(self.wallet) { + match updater::refresh_outputs(&mut **self.wallet) { Ok(_) => true, Err(_) => false, } @@ -169,24 +175,24 @@ where /// Wrapper around external API functions, intended to communicate /// with other parties -pub struct APIForeign<'a, W, K> +pub struct APIForeign<'a, W: ?Sized, K> where - W: 'a + WalletBackend + WalletClient, + W: WalletBackend + WalletClient + 'a, K: Keychain, { /// Wallet, contains its keychain (TODO: Split these up into 2 traits /// perhaps) - pub wallet: &'a mut W, + pub wallet: &'a mut Box, phantom: PhantomData, } -impl<'a, W, K> APIForeign<'a, W, K> +impl<'a, W: ?Sized, K> APIForeign<'a, W, K> where - W: 'a + WalletBackend + WalletClient, + W: WalletBackend + WalletClient, K: Keychain, { /// Create new API instance - pub fn new(wallet_in: &'a mut W) -> APIForeign<'a, W, K> { + pub fn new(wallet_in: &'a mut Box) -> APIForeign { APIForeign { wallet: wallet_in, phantom: PhantomData, @@ -195,11 +201,11 @@ where /// Build a new (potential) coinbase transaction in the wallet pub fn build_coinbase(&mut self, block_fees: &BlockFees) -> Result { - updater::build_coinbase(self.wallet, block_fees) + updater::build_coinbase(&mut **self.wallet, block_fees) } /// Receive a transaction from a sender pub fn receive_tx(&mut self, slate: &mut Slate) -> Result<(), Error> { - tx::receive_tx(self.wallet, slate) + tx::receive_tx(&mut **self.wallet, slate) } } diff --git a/wallet/src/libwallet/controller.rs b/wallet/src/libwallet/controller.rs index 74f0a8a83..f71020729 100644 --- a/wallet/src/libwallet/controller.rs +++ b/wallet/src/libwallet/controller.rs @@ -31,29 +31,31 @@ use failure::Fail; use keychain::Keychain; use libtx::slate::Slate; use libwallet::api::{APIForeign, APIOwner}; -use libwallet::types::{BlockFees, CbData, OutputData, SendTXArgs, WalletBackend, WalletClient, - WalletInfo}; +use libwallet::types::{ + BlockFees, CbData, OutputData, SendTXArgs, WalletBackend, WalletClient, WalletInfo, WalletInst, +}; use libwallet::{Error, ErrorKind}; use util::LOGGER; /// 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: &mut T, f: F) -> Result<(), Error> +pub fn owner_single_use(wallet: Box, f: F) -> Result<(), Error> where T: WalletBackend + WalletClient, F: FnOnce(&mut APIOwner) -> Result<(), Error>, K: Keychain, { - wallet.open_with_credentials()?; - f(&mut APIOwner::new(wallet))?; - wallet.close()?; + let mut w = wallet; + w.open_with_credentials()?; + f(&mut APIOwner::new(&mut w))?; + w.close()?; Ok(()) } /// Instantiate wallet Foreign API for a single-use (command line) call /// Return a function containing a loaded API context to call -pub fn foreign_single_use(wallet: &mut T, f: F) -> Result<(), Error> +pub fn foreign_single_use(wallet: &mut Box, f: F) -> Result<(), Error> where T: WalletBackend + WalletClient, F: FnOnce(&mut APIForeign) -> Result<(), Error>, @@ -67,9 +69,9 @@ where /// Listener version, providing same API but listening for requests on a /// port and wrapping the calls -pub fn owner_listener(wallet: T, addr: &str) -> Result<(), Error> +pub fn owner_listener(wallet: Box, addr: &str) -> Result<(), Error> where - T: WalletBackend + WalletClient, + T: WalletInst, OwnerAPIGetHandler: Handler, OwnerAPIPostHandler: Handler, K: Keychain, @@ -99,9 +101,9 @@ where /// Listener version, providing same API but listening for requests on a /// port and wrapping the calls -pub fn foreign_listener(wallet: T, addr: &str) -> Result<(), Error> +pub fn foreign_listener(wallet: Box, addr: &str) -> Result<(), Error> where - T: WalletBackend + WalletClient, + T: WalletInst, ForeignAPIHandler: Handler, K: Keychain, { @@ -124,23 +126,23 @@ where } /// API Handler/Wrapper for owner functions -pub struct OwnerAPIGetHandler +pub struct OwnerAPIGetHandler where - T: WalletBackend, + T: WalletBackend + WalletClient, K: Keychain, { /// Wallet instance - pub wallet: Arc>, + pub wallet: Arc>>, phantom: PhantomData, } -impl OwnerAPIGetHandler +impl OwnerAPIGetHandler where T: WalletBackend + WalletClient, K: Keychain, { /// Create a new owner API handler for GET methods - pub fn new(wallet: Arc>) -> OwnerAPIGetHandler { + pub fn new(wallet: Arc>>) -> OwnerAPIGetHandler { OwnerAPIGetHandler { wallet, phantom: PhantomData, @@ -201,7 +203,7 @@ where } } -impl Handler for OwnerAPIGetHandler +impl Handler for OwnerAPIGetHandler where T: WalletBackend + WalletClient + Send + Sync + 'static, K: Keychain + 'static, @@ -215,7 +217,8 @@ where error!(LOGGER, "Error opening wallet: {:?}", e); IronError::new(Fail::compat(e), status::BadRequest) })?; - let mut api = APIOwner::new(&mut *wallet); + let mut w = wallet; + let mut api = APIOwner::new(&mut w); let mut resp_json = self.handle_request(req, &mut api); if !resp_json.is_err() { resp_json @@ -232,23 +235,23 @@ where } /// Handles all owner API POST requests -pub struct OwnerAPIPostHandler +pub struct OwnerAPIPostHandler where T: WalletBackend, K: Keychain, { /// Wallet instance - pub wallet: Arc>, + pub wallet: Arc>>, phantom: PhantomData, } -impl OwnerAPIPostHandler +impl OwnerAPIPostHandler where T: WalletBackend + WalletClient, K: Keychain, { /// New POST handler - pub fn new(wallet: Arc>) -> OwnerAPIPostHandler { + pub fn new(wallet: Arc>>) -> OwnerAPIPostHandler { OwnerAPIPostHandler { wallet, phantom: PhantomData, @@ -320,7 +323,7 @@ where } } -impl Handler for OwnerAPIPostHandler +impl Handler for OwnerAPIPostHandler where T: WalletBackend + WalletClient + Send + Sync + 'static, K: Keychain + 'static, @@ -329,12 +332,15 @@ where // every request should open with stored credentials, // do its thing and then de-init whatever secrets have been // stored + { + let mut wallet = self.wallet.lock().unwrap(); + wallet.open_with_credentials().map_err(|e| { + error!(LOGGER, "Error opening wallet: {:?}", e); + IronError::new(Fail::compat(e), status::BadRequest) + })?; + } let mut wallet = self.wallet.lock().unwrap(); - wallet.open_with_credentials().map_err(|e| { - error!(LOGGER, "Error opening wallet: {:?}", e); - IronError::new(Fail::compat(e), status::BadRequest) - })?; - let mut api = APIOwner::new(&mut *wallet); + let mut api = APIOwner::new(&mut wallet); let resp = match self.handle_request(req, &mut api) { Ok(r) => self.create_ok_response(&r), Err(e) => { @@ -367,23 +373,23 @@ impl Handler for OwnerAPIOptionsHandler where { } /// API Handler/Wrapper for foreign functions -pub struct ForeignAPIHandler +pub struct ForeignAPIHandler where T: WalletBackend + WalletClient, K: Keychain, { /// Wallet instance - pub wallet: Arc>, + pub wallet: Arc>>, phantom: PhantomData, } -impl ForeignAPIHandler +impl ForeignAPIHandler where T: WalletBackend + WalletClient, K: Keychain, { /// create a new api handler - pub fn new(wallet: Arc>) -> ForeignAPIHandler { + pub fn new(wallet: Arc>>) -> ForeignAPIHandler { ForeignAPIHandler { wallet, phantom: PhantomData, @@ -442,7 +448,7 @@ where } } } -impl Handler for ForeignAPIHandler +impl Handler for ForeignAPIHandler where T: WalletBackend + WalletClient + Send + Sync + 'static, K: Keychain + 'static, @@ -451,12 +457,15 @@ where // every request should open with stored credentials, // do its thing and then de-init whatever secrets have been // stored + { + let mut wallet = self.wallet.lock().unwrap(); + wallet.open_with_credentials().map_err(|e| { + error!(LOGGER, "Error opening wallet: {:?}", e); + IronError::new(Fail::compat(e), status::BadRequest) + })?; + } let mut wallet = self.wallet.lock().unwrap(); - wallet.open_with_credentials().map_err(|e| { - error!(LOGGER, "Error opening wallet: {:?}", e); - IronError::new(Fail::compat(e), status::BadRequest) - })?; - let mut api = APIForeign::new(&mut *wallet); + let mut api = APIForeign::new(&mut wallet); let resp_json = self.handle_request(req, &mut api); api.wallet .close() diff --git a/wallet/src/libwallet/error.rs b/wallet/src/libwallet/error.rs index 0f7dd771e..12c4a31b3 100644 --- a/wallet/src/libwallet/error.rs +++ b/wallet/src/libwallet/error.rs @@ -48,8 +48,11 @@ pub enum ErrorKind { }, /// Fee Exceeds amount - #[fail(display = "Fee exceeds amount: sender amount {}, recipient fee {}", sender_amount, - recipient_fee)] + #[fail( + display = "Fee exceeds amount: sender amount {}, recipient fee {}", + sender_amount, + recipient_fee + )] FeeExceedsAmount { /// sender amount sender_amount: u64, diff --git a/wallet/src/libwallet/internal/keys.rs b/wallet/src/libwallet/internal/keys.rs index 64ce1b9ed..e7529d320 100644 --- a/wallet/src/libwallet/internal/keys.rs +++ b/wallet/src/libwallet/internal/keys.rs @@ -18,7 +18,7 @@ use libwallet::error::Error; use libwallet::types::WalletBackend; /// Get next available key in the wallet -pub fn next_available_key(wallet: &mut T) -> Result<(Identifier, u32), Error> +pub fn next_available_key(wallet: &mut T) -> Result<(Identifier, u32), Error> where T: WalletBackend, K: Keychain, @@ -30,7 +30,7 @@ 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> diff --git a/wallet/src/libwallet/internal/restore.rs b/wallet/src/libwallet/internal/restore.rs index ec4ff75a8..a3354ed61 100644 --- a/wallet/src/libwallet/internal/restore.rs +++ b/wallet/src/libwallet/internal/restore.rs @@ -16,9 +16,9 @@ use core::global; use keychain::{Identifier, Keychain}; use libtx::proof; -use libwallet::Error; use libwallet::types::*; -use util::secp::{pedersen, key::SecretKey}; +use libwallet::Error; +use util::secp::{key::SecretKey, pedersen}; use util::LOGGER; /// Utility struct for return values from below diff --git a/wallet/src/libwallet/internal/selection.rs b/wallet/src/libwallet/internal/selection.rs index 3ccd8e35d..f0bd4405e 100644 --- a/wallet/src/libwallet/internal/selection.rs +++ b/wallet/src/libwallet/internal/selection.rs @@ -15,7 +15,7 @@ //! Selection of inputs for building transactions use keychain::{Identifier, Keychain}; -use libtx::{build, tx_fee, slate::Slate}; +use libtx::{build, slate::Slate, tx_fee}; use libwallet::error::{Error, ErrorKind}; use libwallet::internal::{keys, sigcontext}; use libwallet::types::*; @@ -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, @@ -122,7 +122,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, ) -> Result< @@ -182,7 +182,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, @@ -287,7 +287,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, @@ -313,10 +313,7 @@ where for coin in coins { let key_id = wallet.keychain().derive_key_id(coin.n_child)?; if coin.is_coinbase { - parts.push(build::coinbase_input( - coin.value, - key_id, - )); + parts.push(build::coinbase_input(coin.value, key_id)); } else { parts.push(build::input(coin.value, key_id)); } diff --git a/wallet/src/libwallet/internal/tx.rs b/wallet/src/libwallet/internal/tx.rs index cfca90fbc..71635c0d9 100644 --- a/wallet/src/libwallet/internal/tx.rs +++ b/wallet/src/libwallet/internal/tx.rs @@ -25,7 +25,7 @@ use util::LOGGER; /// Receive a transaction, modifying the slate accordingly (which can then be /// sent back to sender for posting) -pub fn receive_tx(wallet: &mut T, slate: &mut Slate) -> Result<(), Error> +pub fn receive_tx(wallet: &mut T, slate: &mut Slate) -> Result<(), Error> where T: WalletBackend, K: Keychain, @@ -53,7 +53,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, @@ -110,7 +110,7 @@ where } /// Complete a transaction as the sender -pub fn complete_tx( +pub fn complete_tx( wallet: &mut T, slate: &mut Slate, context: &sigcontext::Context, @@ -129,7 +129,7 @@ where } /// Issue a burn tx -pub fn issue_burn_tx( +pub fn issue_burn_tx( wallet: &mut T, amount: u64, minimum_confirmations: u64, diff --git a/wallet/src/libwallet/internal/updater.rs b/wallet/src/libwallet/internal/updater.rs index ae2f6a84e..a4878f347 100644 --- a/wallet/src/libwallet/internal/updater.rs +++ b/wallet/src/libwallet/internal/updater.rs @@ -26,13 +26,17 @@ use libtx::reward; use libwallet; use libwallet::error::{Error, ErrorKind}; use libwallet::internal::keys; -use libwallet::types::{BlockFees, CbData, OutputData, OutputStatus, WalletBackend, WalletClient, - WalletInfo}; +use libwallet::types::{ + BlockFees, CbData, OutputData, OutputStatus, WalletBackend, WalletClient, WalletInfo, +}; use util::secp::pedersen; use util::{self, LOGGER}; /// Retrieve all of the outputs (doesn't attempt to update from node) -pub fn retrieve_outputs(wallet: &mut T, show_spent: bool) -> Result, Error> +pub fn retrieve_outputs( + wallet: &mut T, + show_spent: bool, +) -> Result, Error> where T: WalletBackend, K: Keychain, @@ -57,7 +61,7 @@ where /// Refreshes the outputs in a wallet with the latest information /// from a node -pub fn refresh_outputs(wallet: &mut T) -> Result<(), Error> +pub fn refresh_outputs(wallet: &mut T) -> Result<(), Error> where T: WalletBackend + WalletClient, K: Keychain, @@ -69,7 +73,7 @@ where /// 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, ) -> Result, Error> where @@ -90,7 +94,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, @@ -125,7 +129,7 @@ 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(wallet: &mut T, height: u64) -> Result<(), Error> +fn refresh_output_state(wallet: &mut T, height: u64) -> Result<(), Error> where T: WalletBackend + WalletClient, K: Keychain, @@ -144,7 +148,7 @@ where 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, K: Keychain, @@ -168,7 +172,7 @@ where /// Retrieve summary info about the wallet /// caller should refresh first if desired -pub fn retrieve_info(wallet: &mut T) -> Result +pub fn retrieve_info(wallet: &mut T) -> Result where T: WalletBackend + WalletClient, K: Keychain, @@ -209,7 +213,7 @@ where } /// Build a coinbase output and insert into wallet -pub fn build_coinbase(wallet: &mut T, block_fees: &BlockFees) -> Result +pub fn build_coinbase(wallet: &mut T, block_fees: &BlockFees) -> Result where T: WalletBackend, K: Keychain, @@ -234,7 +238,7 @@ 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> diff --git a/wallet/src/libwallet/types.rs b/wallet/src/libwallet/types.rs index 2197bfe19..a406decdf 100644 --- a/wallet/src/libwallet/types.rs +++ b/wallet/src/libwallet/types.rs @@ -33,6 +33,19 @@ use libwallet::error::{Error, ErrorKind}; use util::secp::pedersen; +/// Combined trait to allow dynamic wallet dispatch +pub trait WalletInst: WalletBackend + WalletClient + Send + Sync + 'static +where + K: Keychain, +{ +} +impl WalletInst for T +where + T: WalletBackend + WalletClient + Send + Sync + 'static, + K: Keychain, +{ +} + /// TODO: /// Wallets should implement this backend for their storage. All functions /// here expect that the wallet instance has instantiated itself or stored diff --git a/wallet/src/lmdb_wallet.rs b/wallet/src/lmdb_wallet.rs index 09c1b2991..d051d785c 100644 --- a/wallet/src/lmdb_wallet.rs +++ b/wallet/src/lmdb_wallet.rs @@ -29,7 +29,7 @@ use libwallet::{internal, Error, ErrorKind}; use types::{WalletConfig, WalletSeed}; use util::secp::pedersen; -pub const DB_DIR: &'static str = "wallet"; +pub const DB_DIR: &'static str = "wallet_data"; const OUTPUT_PREFIX: u8 = 'o' as u8; const DERIV_PREFIX: u8 = 'd' as u8; @@ -40,6 +40,13 @@ impl From for Error { } } +/// test to see if database files exist in the current directory. If so, +/// use a DB backend for all operations +pub fn wallet_db_exists(config: WalletConfig) -> bool { + let db_path = path::Path::new(&config.data_file_dir).join(DB_DIR); + db_path.exists() +} + pub struct LMDBBackend { db: store::Store, config: WalletConfig, @@ -64,6 +71,13 @@ impl LMDBBackend { keychain: None, }) } + + /// Just test to see if database files exist in the current directory. If + /// so, use a DB backend for all operations + pub fn exists(config: WalletConfig) -> bool { + let db_path = path::Path::new(&config.data_file_dir).join(DB_DIR); + db_path.exists() + } } impl WalletBackend for LMDBBackend diff --git a/wallet/src/types.rs b/wallet/src/types.rs index 8c819ae8d..1d5bdfee7 100644 --- a/wallet/src/types.rs +++ b/wallet/src/types.rs @@ -15,8 +15,8 @@ use std::cmp::min; use std::fs::{self, File}; use std::io::{Read, Write}; -use std::path::MAIN_SEPARATOR; use std::path::Path; +use std::path::MAIN_SEPARATOR; use blake2; use rand::{thread_rng, Rng};