diff --git a/.travis.yml b/.travis.yml index 4e8d8fab..23b20d92 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,9 @@ matrix: - os: linux env: CI_JOB="test" CI_JOB_ARGS="config libwallet api" - os: linux - env: CI_JOB="test" CI_JOB_ARGS="refwallet ." + env: CI_JOB="test" CI_JOB_ARGS="impls" + - os: linux + env: CI_JOB="test" CI_JOB_ARGS="controller ." - os: linux env: CI_JOB="release" CI_JOB_ARGS= - os: osx diff --git a/Cargo.lock b/Cargo.lock index deba408a..8e299310 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -764,8 +764,9 @@ dependencies = [ "grin_util 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", "grin_wallet_api 1.1.0", "grin_wallet_config 1.1.0", + "grin_wallet_controller 1.1.0", + "grin_wallet_impls 1.1.0", "grin_wallet_libwallet 1.1.0", - "grin_wallet_refwallet 1.1.0", "linefeed 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "prettytable-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -786,8 +787,8 @@ dependencies = [ "grin_store 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", "grin_util 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", "grin_wallet_config 1.1.0", + "grin_wallet_impls 1.1.0", "grin_wallet_libwallet 1.1.0", - "grin_wallet_refwallet 1.1.0", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -808,6 +809,68 @@ dependencies = [ "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "grin_wallet_controller" +version = "1.1.0" +dependencies = [ + "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "grin_api 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", + "grin_chain 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", + "grin_core 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", + "grin_keychain 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", + "grin_util 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", + "grin_wallet_api 1.1.0", + "grin_wallet_config 1.1.0", + "grin_wallet_impls 1.1.0", + "grin_wallet_libwallet 1.1.0", + "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "prettytable-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-retry 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "grin_wallet_impls" +version = "1.1.0" +dependencies = [ + "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "grin_api 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", + "grin_chain 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", + "grin_core 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", + "grin_keychain 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", + "grin_store 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", + "grin_util 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", + "grin_wallet_config 1.1.0", + "grin_wallet_libwallet 1.1.0", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-retry 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "grin_wallet_libwallet" version = "1.1.0" @@ -829,40 +892,6 @@ dependencies = [ "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "grin_wallet_refwallet" -version = "1.1.0" -dependencies = [ - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "grin_api 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", - "grin_chain 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", - "grin_core 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", - "grin_keychain 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", - "grin_store 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", - "grin_util 1.1.0 (git+https://github.com/mimblewimble/grin?branch=milestone/1.1.0)", - "grin_wallet_api 1.1.0", - "grin_wallet_config 1.1.0", - "grin_wallet_libwallet 1.1.0", - "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "prettytable-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-retry 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "h2" version = "0.1.16" diff --git a/Cargo.toml b/Cargo.toml index faf3d1d9..adc3106c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ name = "grin-wallet" path = "src/bin/grin-wallet.rs" [workspace] -members = ["api", "config", "libwallet", "refwallet"] +members = ["api", "config", "controller", "impls", "libwallet"] exclude = ["integration"] [dependencies] @@ -30,8 +30,9 @@ log = "0.4" linefeed = "0.5" grin_wallet_api = { path = "./api", version = "1.1.0" } +grin_wallet_impls = { path = "./impls", version = "1.1.0" } grin_wallet_libwallet = { path = "./libwallet", version = "1.1.0" } -grin_wallet_refwallet = { path = "./refwallet", version = "1.1.0" } +grin_wallet_controller = { path = "./controller", version = "1.1.0" } grin_wallet_config = { path = "./config", version = "1.1.0" } grin_core = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } diff --git a/api/Cargo.toml b/api/Cargo.toml index 1af7b85b..59addac8 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -28,6 +28,6 @@ grin_api = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1 grin_store = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } [dev-dependencies] -grin_wallet_refwallet = { path = "../refwallet", version = "1.1.0" } +grin_wallet_impls = { path = "../impls", version = "1.1.0" } serde_json = "1" tempfile = "3.0.7" diff --git a/api/src/foreign.rs b/api/src/foreign.rs index 4c08ba60..d2aefc6c 100644 --- a/api/src/foreign.rs +++ b/api/src/foreign.rs @@ -27,17 +27,14 @@ //! seed). use crate::keychain::Keychain; -use crate::libwallet::internal::{tx, updater}; +use crate::libwallet::api_impl::foreign; use crate::libwallet::slate::Slate; -use crate::libwallet::types::{BlockFees, CbData, NodeClient, TxLogEntryType, WalletBackend}; -use crate::libwallet::{Error, ErrorKind}; -use crate::util::secp::{ContextFlag, Secp256k1}; +use crate::libwallet::types::{BlockFees, CbData, NodeClient, WalletBackend}; +use crate::libwallet::Error; use crate::util::Mutex; use std::marker::PhantomData; use std::sync::Arc; -const USER_MESSAGE_MAX_LEN: usize = 256; - /// Wrapper around external API functions, intended to communicate /// with other parties pub struct Foreign @@ -72,16 +69,14 @@ where pub fn build_coinbase(&self, block_fees: &BlockFees) -> Result { let mut w = self.wallet.lock(); w.open_with_credentials()?; - let res = updater::build_coinbase(&mut *w, block_fees); + let res = foreign::build_coinbase(&mut *w, block_fees); w.close()?; res } /// Verifies all messages in the slate match their public keys pub fn verify_slate_messages(&self, slate: &Slate) -> Result<(), Error> { - let secp = Secp256k1::with_caps(ContextFlag::VerifyOnly); - slate.verify_messages(&secp)?; - Ok(()) + foreign::verify_slate_messages(slate) } /// Receive a transaction from a sender @@ -93,35 +88,8 @@ where ) -> Result<(), Error> { let mut w = self.wallet.lock(); w.open_with_credentials()?; - let parent_key_id = match dest_acct_name { - Some(d) => { - let pm = w.get_acct_path(d.to_owned())?; - match pm { - Some(p) => p.path, - None => w.parent_key_id(), - } - } - None => w.parent_key_id(), - }; - // Don't do this multiple times - let tx = updater::retrieve_txs(&mut *w, None, Some(slate.id), Some(&parent_key_id), false)?; - for t in &tx { - if t.tx_type == TxLogEntryType::TxReceived { - return Err(ErrorKind::TransactionAlreadyReceived(slate.id.to_string()).into()); - } - } - - let message = match message { - Some(mut m) => { - m.truncate(USER_MESSAGE_MAX_LEN); - Some(m) - } - None => None, - }; - - tx::add_output_to_slate(&mut *w, slate, &parent_key_id, 1, message)?; - tx::update_message(&mut *w, slate)?; + let res = foreign::receive_tx(&mut *w, slate, dest_acct_name, message); w.close()?; - Ok(()) + res } } diff --git a/api/src/foreign_rpc.rs b/api/src/foreign_rpc.rs index 3cf52300..2966b5e7 100644 --- a/api/src/foreign_rpc.rs +++ b/api/src/foreign_rpc.rs @@ -209,7 +209,8 @@ macro_rules! doctest_helper_json_rpc_foreign_assert_response { use grin_util::Mutex; use grin_wallet_api::{Foreign, ForeignRpc}; use grin_wallet_config::WalletConfig; - use grin_wallet_refwallet::{HTTPNodeClient, LMDBBackend, WalletBackend}; + use grin_wallet_impls::{HTTPNodeClient, LMDBBackend}; + use grin_wallet_libwallet::types::WalletBackend; use serde_json; use std::sync::Arc; use tempfile::tempdir; diff --git a/api/src/owner.rs b/api/src/owner.rs index ddc30cce..a96f8d1f 100644 --- a/api/src/owner.rs +++ b/api/src/owner.rs @@ -31,20 +31,15 @@ use std::marker::PhantomData; use std::sync::Arc; use uuid::Uuid; -use crate::core::core::hash::Hashed; use crate::core::core::Transaction; -use crate::core::ser; use crate::keychain::{Identifier, Keychain}; -use crate::libwallet::internal::{keys, selection, tx, updater}; +use crate::libwallet::api_impl::owner; use crate::libwallet::slate::Slate; use crate::libwallet::types::{ - AcctPathMapping, NodeClient, OutputData, TxLogEntry, TxWrapper, WalletBackend, WalletInfo, + AcctPathMapping, NodeClient, OutputData, TxLogEntry, WalletBackend, WalletInfo, }; -use crate::libwallet::{Error, ErrorKind}; -use crate::util; -use crate::util::secp::{pedersen, ContextFlag, Secp256k1}; - -const USER_MESSAGE_MAX_LEN: usize = 256; +use crate::libwallet::Error; +use crate::util::secp::pedersen; /// Functions intended for use by the owner (e.g. master seed holder) of the wallet. pub struct Owner @@ -169,7 +164,7 @@ where pub fn accounts(&self) -> Result, Error> { let mut w = self.wallet.lock(); - keys::accounts(&mut *w) + owner::accounts(&mut *w) } /// Creates a new 'account', which is a mapping of a user-specified @@ -225,7 +220,7 @@ where pub fn create_account_path(&self, label: &str) -> Result { let mut w = self.wallet.lock(); - keys::new_acct_path(&mut *w, label) + owner::create_account_path(&mut *w, label) } /// Sets the wallet's currently active account. This sets the @@ -280,8 +275,7 @@ where pub fn set_active_account(&self, label: &str) -> Result<(), Error> { let mut w = self.wallet.lock(); - w.set_parent_key_id_by_name(label)?; - Ok(()) + owner::set_active_account(&mut *w, label) } /// Returns a list of outputs from the active account in the wallet. @@ -346,18 +340,7 @@ where ) -> Result<(bool, Vec<(OutputData, pedersen::Commitment)>), Error> { let mut w = self.wallet.lock(); w.open_with_credentials()?; - let parent_key_id = w.parent_key_id(); - - let mut validated = false; - if refresh_from_node { - validated = self.update_outputs(&mut w, false); - } - - let res = Ok(( - validated, - updater::retrieve_outputs(&mut *w, include_spent, tx_id, Some(&parent_key_id))?, - )); - + let res = owner::retrieve_outputs(&mut *w, include_spent, refresh_from_node, tx_id); w.close()?; res } @@ -424,18 +407,7 @@ where ) -> Result<(bool, Vec), Error> { let mut w = self.wallet.lock(); w.open_with_credentials()?; - let parent_key_id = w.parent_key_id(); - - let mut validated = false; - if refresh_from_node { - validated = self.update_outputs(&mut w, false); - } - - let res = Ok(( - validated, - updater::retrieve_txs(&mut *w, tx_id, tx_slate_id, Some(&parent_key_id), false)?, - )); - + let res = owner::retrieve_txs(&mut *w, refresh_from_node, tx_id, tx_slate_id); w.close()?; res } @@ -496,16 +468,7 @@ where ) -> Result<(bool, WalletInfo), Error> { let mut w = self.wallet.lock(); w.open_with_credentials()?; - let parent_key_id = w.parent_key_id(); - - let mut validated = false; - if refresh_from_node { - validated = self.update_outputs(&mut w, false); - } - - let wallet_info = updater::retrieve_info(&mut *w, &parent_key_id, minimum_confirmations)?; - let res = Ok((validated, wallet_info)); - + let res = owner::retrieve_summary_info(&mut *w, refresh_from_node, minimum_confirmations); w.close()?; res } @@ -627,53 +590,19 @@ where ) -> Result { let mut w = self.wallet.lock(); w.open_with_credentials()?; - let parent_key_id = match src_acct_name { - Some(d) => { - let pm = w.get_acct_path(d.to_owned())?; - match pm { - Some(p) => p.path, - None => w.parent_key_id(), - } - } - None => w.parent_key_id(), - }; - - let message = match message { - Some(mut m) => { - m.truncate(USER_MESSAGE_MAX_LEN); - Some(m) - } - None => None, - }; - - let mut slate = tx::new_tx_slate(&mut *w, amount, 2)?; - - let context = tx::add_inputs_to_slate( + let res = owner::initiate_tx( &mut *w, - &mut slate, + src_acct_name, + amount, minimum_confirmations, max_outputs, num_change_outputs, selection_strategy_is_use_all, - &parent_key_id, - 0, message, - )?; - - // Save the aggsig context in our DB for when we - // recieve the transaction back - { - let mut batch = w.batch()?; - batch.save_private_context(slate.id.as_bytes(), &context)?; - batch.commit()?; - } - + target_slate_version, + ); w.close()?; - // set target slate version - if let Some(v) = target_slate_version { - slate.version_info.orig_version = v; - } - Ok(slate) + res } /// Estimates the amount to be locked and fee for the transaction without creating one @@ -723,25 +652,17 @@ where > { let mut w = self.wallet.lock(); w.open_with_credentials()?; - let parent_key_id = match src_acct_name { - Some(d) => { - let pm = w.get_acct_path(d.to_owned())?; - match pm { - Some(p) => p.path, - None => w.parent_key_id(), - } - } - None => w.parent_key_id(), - }; - tx::estimate_send_tx( + let res = owner::estimate_initiate_tx( &mut *w, + src_acct_name, amount, minimum_confirmations, max_outputs, num_change_outputs, selection_strategy_is_use_all, - &parent_key_id, - ) + ); + w.close()?; + res } /// Lock outputs associated with a given slate/transaction @@ -749,11 +670,9 @@ where pub fn tx_lock_outputs(&self, slate: &Slate) -> Result<(), Error> { let mut w = self.wallet.lock(); w.open_with_credentials()?; - let context = w.get_private_context(slate.id.as_bytes())?; - w.open_with_credentials()?; - selection::lock_tx_context(&mut *w, slate, &context)?; + let res = owner::tx_lock_outputs(&mut *w, slate); w.close()?; - Ok(()) + res } /// Sender finalization of the transaction. Takes the file returned by the @@ -763,17 +682,9 @@ where pub fn finalize_tx(&self, slate: &mut Slate) -> Result<(), Error> { let mut w = self.wallet.lock(); w.open_with_credentials()?; - let context = w.get_private_context(slate.id.as_bytes())?; - tx::complete_tx(&mut *w, slate, 0, &context)?; - tx::update_stored_tx(&mut *w, slate)?; - tx::update_message(&mut *w, slate)?; - { - let mut batch = w.batch()?; - batch.delete_private_context(slate.id.as_bytes())?; - batch.commit()?; - } + let res = owner::finalize_tx(&mut *w, slate); w.close()?; - Ok(()) + res } /// Roll back a transaction and all associated outputs with a given @@ -784,96 +695,55 @@ where pub fn cancel_tx(&self, tx_id: Option, tx_slate_id: Option) -> Result<(), Error> { let mut w = self.wallet.lock(); w.open_with_credentials()?; - let parent_key_id = w.parent_key_id(); - if !self.update_outputs(&mut w, false) { - return Err(ErrorKind::TransactionCancellationError( - "Can't contact running Grin node. Not Cancelling.", - ))?; - } - tx::cancel_tx(&mut *w, &parent_key_id, tx_id, tx_slate_id)?; + let res = owner::cancel_tx(&mut *w, tx_id, tx_slate_id); w.close()?; - Ok(()) + res } /// Retrieves a stored transaction from a TxLogEntry pub fn get_stored_tx(&self, entry: &TxLogEntry) -> Result, Error> { let w = self.wallet.lock(); - w.get_stored_tx(entry) + owner::get_stored_tx(&*w, entry) } /// Posts a transaction to the chain pub fn post_tx(&self, tx: &Transaction, fluff: bool) -> Result<(), Error> { - let tx_hex = util::to_hex(ser::ser_vec(tx).unwrap()); let client = { let mut w = self.wallet.lock(); w.w2n_client().clone() }; - let res = client.post_tx(&TxWrapper { tx_hex: tx_hex }, fluff); - if let Err(e) = res { - error!("api: post_tx: failed with error: {}", e); - Err(e) - } else { - debug!( - "api: post_tx: successfully posted tx: {}, fluff? {}", - tx.hash(), - fluff - ); - Ok(()) - } + owner::post_tx(&client, tx, fluff) } /// Verifies all messages in the slate match their public keys pub fn verify_slate_messages(&self, slate: &Slate) -> Result<(), Error> { - let secp = Secp256k1::with_caps(ContextFlag::VerifyOnly); - slate.verify_messages(&secp)?; - Ok(()) + owner::verify_slate_messages(slate) } /// Attempt to restore contents of wallet pub fn restore(&self) -> Result<(), Error> { let mut w = self.wallet.lock(); w.open_with_credentials()?; - w.restore()?; + let res = owner::restore(&mut *w); w.close()?; - Ok(()) + res } /// Attempt to check and fix the contents of the wallet pub fn check_repair(&self, delete_unconfirmed: bool) -> Result<(), Error> { let mut w = self.wallet.lock(); w.open_with_credentials()?; - self.update_outputs(&mut w, true); - w.check_repair(delete_unconfirmed)?; + let res = owner::check_repair(&mut *w, delete_unconfirmed); w.close()?; - Ok(()) + res } /// Retrieve current height from node pub fn node_height(&self) -> Result<(u64, bool), Error> { - let res = { - let mut w = self.wallet.lock(); - w.open_with_credentials()?; - w.w2n_client().get_chain_height() - }; - match res { - Ok(height) => Ok((height, true)), - Err(_) => { - let outputs = self.retrieve_outputs(true, false, None)?; - let height = match outputs.1.iter().map(|(out, _)| out.height).max() { - Some(height) => height, - None => 0, - }; - Ok((height, false)) - } - } - } - - /// Attempt to update outputs in wallet, return whether it was successful - fn update_outputs(&self, w: &mut W, update_all: bool) -> bool { - let parent_key_id = w.parent_key_id(); - match updater::refresh_outputs(&mut *w, &parent_key_id, update_all) { - Ok(_) => true, - Err(_) => false, - } + let mut w = self.wallet.lock(); + w.open_with_credentials()?; + let res = owner::node_height(&mut *w); + w.close()?; + res } } diff --git a/api/src/owner_rpc.rs b/api/src/owner_rpc.rs index b7757e24..a7fb5fb4 100644 --- a/api/src/owner_rpc.rs +++ b/api/src/owner_rpc.rs @@ -737,7 +737,8 @@ macro_rules! doctest_helper_json_rpc_owner_assert_response { use grin_util::Mutex; use grin_wallet_api::{Owner, OwnerRpc}; use grin_wallet_config::WalletConfig; - use grin_wallet_refwallet::{HTTPNodeClient, LMDBBackend, WalletBackend}; + use grin_wallet_impls::{HTTPNodeClient, LMDBBackend}; + use grin_wallet_libwallet::types::WalletBackend; use serde_json; use std::sync::Arc; use tempfile::tempdir; diff --git a/refwallet/Cargo.toml b/controller/Cargo.toml similarity index 84% rename from refwallet/Cargo.toml rename to controller/Cargo.toml index 5d6bc7fb..5f5d2109 100644 --- a/refwallet/Cargo.toml +++ b/controller/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "grin_wallet_refwallet" +name = "grin_wallet_controller" version = "1.1.0" authors = ["Grin Developers "] -description = "Simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format." +description = "Controllers for grin wallet instantiation" license = "Apache-2.0" repository = "https://github.com/mimblewimble/grin" keywords = [ "crypto", "grin", "mimblewimble" ] @@ -12,7 +12,6 @@ exclude = ["**/*.grin", "**/*.grin2"] edition = "2018" [dependencies] -blake2-rfc = "0.2" failure = "0.1" failure_derive = "0.1" futures = "0.1" @@ -33,6 +32,7 @@ url = "1.7.0" chrono = { version = "0.4.4", features = ["serde"] } grin_wallet_api = { path = "../api", version = "1.1.0" } +grin_wallet_impls = { path = "../impls", version = "1.1.0" } grin_wallet_libwallet = { path = "../libwallet", version = "1.1.0" } grin_wallet_config = { path = "../config", version = "1.1.0" } @@ -41,4 +41,3 @@ grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "milest grin_chain = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } grin_util = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } grin_api = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } -grin_store = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } diff --git a/refwallet/src/command.rs b/controller/src/command.rs similarity index 90% rename from refwallet/src/command.rs rename to controller/src/command.rs index c899cad9..3010a3fc 100644 --- a/refwallet/src/command.rs +++ b/controller/src/command.rs @@ -28,12 +28,15 @@ use crate::api::TLSConfig; use crate::core::core; use crate::keychain; +use crate::config::WalletConfig; use crate::error::{Error, ErrorKind}; -use crate::{controller, display, HTTPNodeClient, WalletConfig, WalletInst, WalletSeed}; -use crate::{ - FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter, LMDBBackend, - NodeClient, NullWalletCommAdapter, +use crate::impls::{ + instantiate_wallet, FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter, + LMDBBackend, NullWalletCommAdapter, }; +use crate::impls::{HTTPNodeClient, WalletSeed}; +use crate::libwallet::types::{NodeClient, WalletInst}; +use crate::{controller, display}; /// Arguments common to all wallet commands #[derive(Clone)] @@ -90,7 +93,7 @@ pub fn recover(config: &WalletConfig, args: RecoverArgs) -> Result<(), Error> { let res = WalletSeed::from_file(config, &args.passphrase); if let Err(e) = res { error!("Error loading wallet seed (check password): {}", e); - return Err(e); + return Err(e.into()); } let _ = res.unwrap().show_recovery_phrase(); } else { @@ -101,7 +104,7 @@ pub fn recover(config: &WalletConfig, args: RecoverArgs) -> Result<(), Error> { ); if let Err(e) = res { error!("Error recovering seed - {}", e); - return Err(e); + return Err(e.into()); } } Ok(()) @@ -119,19 +122,44 @@ pub fn listen(config: &WalletConfig, args: &ListenArgs, g_args: &GlobalArgs) -> params.insert("certificate".to_owned(), t.certificate.clone()); params.insert("private_key".to_owned(), t.private_key.clone()); } - let adapter = match args.method.as_str() { - "http" => HTTPWalletCommAdapter::new(), - "keybase" => KeybaseWalletCommAdapter::new(), - _ => NullWalletCommAdapter::new(), + let res = match args.method.as_str() { + "http" => { + // HTTP adapter can't use the listen trait method because of the + // crate structure. May be able to fix when V1 API is deprecated + let node_client = HTTPNodeClient::new( + &config.check_node_api_http_addr, + g_args.node_api_secret.clone(), + ); + let wallet = instantiate_wallet( + config.clone(), + node_client, + &g_args.password.clone().unwrap(), + &g_args.account, + )?; + let listen_addr = params.get("api_listen_addr").unwrap(); + let tls_conf = match params.get("certificate") { + Some(s) => Some(grin_api::TLSConfig::new( + s.to_owned(), + params.get("private_key").unwrap().to_owned(), + )), + None => None, + }; + controller::foreign_listener(wallet.clone(), &listen_addr, tls_conf)?; + Ok(()) + } + "keybase" => { + let adapter = KeybaseWalletCommAdapter::new(); + adapter.listen( + params, + config.clone(), + &g_args.password.clone().unwrap(), + &g_args.account, + g_args.node_api_secret.clone(), + ) + } + _ => Ok(()), }; - let res = adapter.listen( - params, - config.clone(), - &g_args.password.clone().unwrap(), - &g_args.account, - g_args.node_api_secret.clone(), - ); if let Err(e) = res { return Err(ErrorKind::LibWallet(e.kind(), e.cause_string()).into()); } diff --git a/refwallet/src/controller.rs b/controller/src/controller.rs similarity index 99% rename from refwallet/src/controller.rs rename to controller/src/controller.rs index 31a131ce..4de4b7be 100644 --- a/refwallet/src/controller.rs +++ b/controller/src/controller.rs @@ -15,11 +15,11 @@ //! Controller for wallet.. instantiates and handles listeners (or single-run //! invocations) as needed. //! Still experimental -use crate::adapters::{FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter}; use crate::api::{ApiServer, BasicAuthMiddleware, Handler, ResponseFuture, Router, TLSConfig}; use crate::apiwallet::{Foreign, Owner}; use crate::core::core; use crate::core::core::Transaction; +use crate::impls::{FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter}; use crate::keychain::Keychain; use crate::libwallet::slate::Slate; use crate::libwallet::types::{ diff --git a/refwallet/src/display.rs b/controller/src/display.rs similarity index 100% rename from refwallet/src/display.rs rename to controller/src/display.rs diff --git a/refwallet/src/error.rs b/controller/src/error.rs similarity index 95% rename from refwallet/src/error.rs rename to controller/src/error.rs index cb676cc4..5ffb6a31 100644 --- a/refwallet/src/error.rs +++ b/controller/src/error.rs @@ -16,6 +16,7 @@ use crate::api; use crate::core::core::transaction; use crate::core::libtx; +use crate::impls; use crate::keychain; use crate::libwallet; use failure::{Backtrace, Context, Fail}; @@ -35,6 +36,10 @@ pub enum ErrorKind { #[fail(display = "LibTx Error")] LibTX(libtx::ErrorKind), + /// Impls error + #[fail(display = "Impls Error")] + Impls(impls::ErrorKind), + /// LibWallet Error #[fail(display = "LibWallet Error: {}", _1)] LibWallet(libwallet::ErrorKind, String), @@ -208,3 +213,11 @@ impl From for Error { } } } + +impl From for Error { + fn from(error: impls::Error) -> Error { + Error { + inner: Context::new(ErrorKind::Impls(error.kind())), + } + } +} diff --git a/controller/src/lib.rs b/controller/src/lib.rs new file mode 100644 index 00000000..1bcffea8 --- /dev/null +++ b/controller/src/lib.rs @@ -0,0 +1,37 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Library module for the main wallet functionalities provided by Grin. + +#[macro_use] +extern crate prettytable; + +#[macro_use] +extern crate log; +use failure; +use grin_api as api; +extern crate grin_core as core; +use grin_keychain as keychain; +use grin_util as util; +use grin_wallet_api as apiwallet; +use grin_wallet_impls as impls; +use grin_wallet_libwallet as libwallet; +extern crate grin_wallet_config as config; + +pub mod command; +pub mod controller; +pub mod display; +mod error; + +pub use crate::error::{Error, ErrorKind}; diff --git a/refwallet/tests/accounts.rs b/controller/tests/accounts.rs similarity index 98% rename from refwallet/tests/accounts.rs rename to controller/tests/accounts.rs index 049bf928..09b8a6fe 100644 --- a/refwallet/tests/accounts.rs +++ b/controller/tests/accounts.rs @@ -14,7 +14,8 @@ //! tests differing accounts in the same wallet #[macro_use] extern crate log; -extern crate grin_wallet_refwallet as wallet; +extern crate grin_wallet_controller as wallet; +extern crate grin_wallet_impls as impls; use self::core::global; use self::core::global::ChainTypes; @@ -23,10 +24,10 @@ use grin_core as core; use grin_keychain as keychain; use grin_util as util; use grin_wallet_libwallet as libwallet; +use impls::test_framework::{self, LocalWalletClient, WalletProxy}; use std::fs; use std::thread; use std::time::Duration; -use wallet::test_framework::{self, LocalWalletClient, WalletProxy}; fn clean_output_dir(test_dir: &str) { let _ = fs::remove_dir_all(test_dir); diff --git a/refwallet/tests/check.rs b/controller/tests/check.rs similarity index 82% rename from refwallet/tests/check.rs rename to controller/tests/check.rs index 95bbffa4..c879bae8 100644 --- a/refwallet/tests/check.rs +++ b/controller/tests/check.rs @@ -14,7 +14,8 @@ //! tests differing accounts in the same wallet #[macro_use] extern crate log; -extern crate grin_wallet_refwallet as wallet; +extern crate grin_wallet_controller as wallet; +extern crate grin_wallet_impls as impls; use self::core::consensus; use self::core::global; @@ -24,11 +25,32 @@ use grin_core as core; use grin_keychain as keychain; use grin_util as util; use grin_wallet_libwallet as libwallet; +use impls::test_framework::{self, LocalWalletClient, WalletProxy}; +use impls::FileWalletCommAdapter; +use libwallet::types::WalletInst; use std::fs; use std::thread; use std::time::Duration; -use wallet::test_framework::{self, LocalWalletClient, WalletProxy}; -use wallet::FileWalletCommAdapter; + +macro_rules! send_to_dest { + ($a:expr, $b:expr, $c:expr, $d:expr) => { + test_framework::send_to_dest::< + WalletInst, + LocalWalletClient, + ExtKeychain, + >($a, $b, $c, $d) + }; +} + +macro_rules! wallet_info { + ($a:expr) => { + test_framework::wallet_info::< + WalletInst, + LocalWalletClient, + ExtKeychain, + >($a) + }; +} fn clean_output_dir(test_dir: &str) { let _ = fs::remove_dir_all(test_dir); @@ -318,49 +340,37 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> { let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), bh as usize); // send some funds to wallets 1 - wallet::controller::owner_single_use(miner.clone(), |api| { - test_framework::send_to_dest(m_client.clone(), api, "wallet1", base_amount * 1)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet1", base_amount * 2)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet1", base_amount * 3)?; - bh += 3; - Ok(()) - })?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet1", base_amount * 1)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet1", base_amount * 2)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet1", base_amount * 3)?; + bh += 3; // 0) Check repair when all is okay should leave wallet contents alone wallet::controller::owner_single_use(wallet1.clone(), |api| { api.check_repair(true)?; - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet1.clone())?; assert_eq!(info.amount_currently_spendable, base_amount * 6); assert_eq!(info.total, base_amount * 6); Ok(()) })?; // send some funds to wallet 2 - wallet::controller::owner_single_use(miner.clone(), |api| { - test_framework::send_to_dest(m_client.clone(), api, "wallet2", base_amount * 4)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet2", base_amount * 5)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet2", base_amount * 6)?; - bh += 3; - Ok(()) - })?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet2", base_amount * 4)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet2", base_amount * 5)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet2", base_amount * 6)?; + bh += 3; let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm); bh += cm as u64; // confirm balances - wallet::controller::owner_single_use(wallet1.clone(), |api| { - let info = test_framework::wallet_info(api)?; - assert_eq!(info.amount_currently_spendable, base_amount * 6); - assert_eq!(info.total, base_amount * 6); - Ok(()) - })?; + let info = wallet_info!(wallet1.clone())?; + assert_eq!(info.amount_currently_spendable, base_amount * 6); + assert_eq!(info.total, base_amount * 6); - wallet::controller::owner_single_use(wallet2.clone(), |api| { - let info = test_framework::wallet_info(api)?; - assert_eq!(info.amount_currently_spendable, base_amount * 15); - assert_eq!(info.total, base_amount * 15); - Ok(()) - })?; + let info = wallet_info!(wallet2.clone())?; + assert_eq!(info.amount_currently_spendable, base_amount * 15); + assert_eq!(info.total, base_amount * 15); // Now there should be outputs on the chain using the same // seed + BIP32 path. @@ -372,7 +382,7 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> { })?; wallet::controller::owner_single_use(wallet3.clone(), |api| { - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet3.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 6); assert_eq!(info.amount_currently_spendable, base_amount * 21); @@ -387,7 +397,7 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> { })?; wallet::controller::owner_single_use(wallet1.clone(), |api| { - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet1.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 6); assert_eq!(info.amount_currently_spendable, base_amount * 21); @@ -396,18 +406,16 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> { // 3) If I recover from seed and start using the wallet without restoring, // check_repair should restore the older outputs - wallet::controller::owner_single_use(miner.clone(), |api| { - test_framework::send_to_dest(m_client.clone(), api, "wallet4", base_amount * 7)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet4", base_amount * 8)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet4", base_amount * 9)?; - bh += 3; - Ok(()) - })?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet4", base_amount * 7)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet4", base_amount * 8)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet4", base_amount * 9)?; + bh += 3; + let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm); bh += cm as u64; wallet::controller::owner_single_use(wallet4.clone(), |api| { - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet4.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 3); assert_eq!(info.amount_currently_spendable, base_amount * 24); @@ -420,7 +428,7 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> { })?; wallet::controller::owner_single_use(wallet5.clone(), |api| { - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet5.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 9); assert_eq!(info.amount_currently_spendable, base_amount * (45)); @@ -429,18 +437,16 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> { // 4) If I recover from seed and start using the wallet without restoring, // check_repair should restore the older outputs - wallet::controller::owner_single_use(miner.clone(), |api| { - test_framework::send_to_dest(m_client.clone(), api, "wallet6", base_amount * 10)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet6", base_amount * 11)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet6", base_amount * 12)?; - bh += 3; - Ok(()) - })?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet6", base_amount * 10)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet6", base_amount * 11)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet6", base_amount * 12)?; + bh += 3; + let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm as usize); bh += cm as u64; wallet::controller::owner_single_use(wallet6.clone(), |api| { - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet6.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 3); assert_eq!(info.amount_currently_spendable, base_amount * 33); @@ -453,7 +459,7 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> { })?; wallet::controller::owner_single_use(wallet6.clone(), |api| { - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet6.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 12); assert_eq!(info.amount_currently_spendable, base_amount * (78)); @@ -463,13 +469,10 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> { // 5) Start using same seed with a different account, amounts should // be distinct and restore should return funds from other account - wallet::controller::owner_single_use(miner.clone(), |api| { - test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 13)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 14)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 15)?; - bh += 3; - Ok(()) - })?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 13)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 14)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 15)?; + bh += 3; // mix it up a bit wallet::controller::owner_single_use(wallet7.clone(), |api| { @@ -478,25 +481,22 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> { Ok(()) })?; - wallet::controller::owner_single_use(miner.clone(), |api| { - test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 1)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 2)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet7", base_amount * 3)?; - bh += 3; - Ok(()) - })?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 1)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 2)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet7", base_amount * 3)?; + bh += 3; // check balances let _ = test_framework::award_blocks_to_wallet(&chain, miner.clone(), cm); bh += cm as u64; wallet::controller::owner_single_use(wallet7.clone(), |api| { - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet7.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 3); assert_eq!(info.amount_currently_spendable, base_amount * 6); api.set_active_account("default")?; - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet7.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 3); assert_eq!(info.amount_currently_spendable, base_amount * 42); @@ -505,12 +505,12 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> { wallet::controller::owner_single_use(wallet8.clone(), |api| { api.restore()?; - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet8.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 15); assert_eq!(info.amount_currently_spendable, base_amount * 120); api.set_active_account("account_1")?; - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet8.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 3); assert_eq!(info.amount_currently_spendable, base_amount * 6); @@ -526,27 +526,26 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> { api.set_active_account("account_1")?; Ok(()) })?; - wallet::controller::owner_single_use(miner.clone(), |api| { - test_framework::send_to_dest(m_client.clone(), api, "wallet9", base_amount * 4)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet9", base_amount * 5)?; - test_framework::send_to_dest(m_client.clone(), api, "wallet9", base_amount * 6)?; - bh += 3; - Ok(()) - })?; + + send_to_dest!(miner.clone(), m_client.clone(), "wallet9", base_amount * 4)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet9", base_amount * 5)?; + send_to_dest!(miner.clone(), m_client.clone(), "wallet9", base_amount * 6)?; + bh += 3; + let _bh = bh; wallet::controller::owner_single_use(wallet9.clone(), |api| { - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet9.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 3); assert_eq!(info.amount_currently_spendable, base_amount * 15); api.check_repair(true)?; - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet9.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 6); assert_eq!(info.amount_currently_spendable, base_amount * 21); api.set_active_account("default")?; - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet9.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 15); assert_eq!(info.amount_currently_spendable, base_amount * 120); @@ -559,13 +558,13 @@ fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> { wallet::controller::owner_single_use(wallet10.clone(), |api| { api.check_repair(true)?; api.set_active_account("account_1")?; - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet10.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 6); assert_eq!(info.amount_currently_spendable, base_amount * 21); api.set_active_account("default")?; - let info = test_framework::wallet_info(api)?; + let info = wallet_info!(wallet10.clone())?; let outputs = api.retrieve_outputs(true, false, None)?.1; assert_eq!(outputs.len(), 15); assert_eq!(info.amount_currently_spendable, base_amount * 120); diff --git a/refwallet/tests/file.rs b/controller/tests/file.rs similarity index 97% rename from refwallet/tests/file.rs rename to controller/tests/file.rs index 579bf153..f68809d2 100644 --- a/refwallet/tests/file.rs +++ b/controller/tests/file.rs @@ -14,17 +14,18 @@ //! Test a wallet file send/recieve #[macro_use] extern crate log; -extern crate grin_wallet_refwallet as wallet; +extern crate grin_wallet_controller as wallet; +extern crate grin_wallet_impls as impls; use self::core::global; use self::core::global::ChainTypes; use self::keychain::ExtKeychain; -use self::wallet::test_framework::{self, LocalWalletClient, WalletProxy}; -use self::wallet::FileWalletCommAdapter; use grin_core as core; use grin_keychain as keychain; use grin_util as util; use grin_wallet_libwallet as libwallet; +use impls::test_framework::{self, LocalWalletClient, WalletProxy}; +use impls::FileWalletCommAdapter; use std::fs; use std::thread; use std::time::Duration; diff --git a/refwallet/tests/repost.rs b/controller/tests/repost.rs similarity index 97% rename from refwallet/tests/repost.rs rename to controller/tests/repost.rs index 22b6bbf3..af84b770 100644 --- a/refwallet/tests/repost.rs +++ b/controller/tests/repost.rs @@ -14,18 +14,19 @@ //! Test a wallet repost command #[macro_use] extern crate log; +extern crate grin_wallet_controller as wallet; +extern crate grin_wallet_impls as impls; extern crate grin_wallet_libwallet as libwallet; -extern crate grin_wallet_refwallet as wallet; use self::core::global; use self::core::global::ChainTypes; use self::keychain::ExtKeychain; use self::libwallet::slate::Slate; -use self::wallet::test_framework::{self, LocalWalletClient, WalletProxy}; -use self::wallet::FileWalletCommAdapter; use grin_core as core; use grin_keychain as keychain; use grin_util as util; +use impls::test_framework::{self, LocalWalletClient, WalletProxy}; +use impls::FileWalletCommAdapter; use std::fs; use std::thread; use std::time::Duration; diff --git a/refwallet/tests/restore.rs b/controller/tests/restore.rs similarity index 98% rename from refwallet/tests/restore.rs rename to controller/tests/restore.rs index ab2371cd..98f7d1ca 100644 --- a/refwallet/tests/restore.rs +++ b/controller/tests/restore.rs @@ -14,18 +14,19 @@ //! tests for wallet restore #[macro_use] extern crate log; +extern crate grin_wallet_controller as wallet; +extern crate grin_wallet_impls as impls; extern crate grin_wallet_libwallet as libwallet; -extern crate grin_wallet_refwallet as wallet; use self::core::global; use self::core::global::ChainTypes; use self::keychain::{ExtKeychain, Identifier, Keychain}; use self::libwallet::slate::Slate; use self::libwallet::types::AcctPathMapping; -use self::wallet::test_framework::{self, LocalWalletClient, WalletProxy}; use grin_core as core; use grin_keychain as keychain; use grin_util as util; +use impls::test_framework::{self, LocalWalletClient, WalletProxy}; use std::fs; use std::sync::atomic::Ordering; use std::thread; diff --git a/refwallet/tests/self_send.rs b/controller/tests/self_send.rs similarity index 96% rename from refwallet/tests/self_send.rs rename to controller/tests/self_send.rs index 9a1840d9..1755160b 100644 --- a/refwallet/tests/self_send.rs +++ b/controller/tests/self_send.rs @@ -14,16 +14,17 @@ //! Test a wallet sending to self #[macro_use] extern crate log; -extern crate grin_wallet_refwallet as wallet; +extern crate grin_wallet_controller as wallet; +extern crate grin_wallet_impls as impls; use self::core::global; use self::core::global::ChainTypes; use self::keychain::ExtKeychain; -use self::wallet::test_framework::{self, LocalWalletClient, WalletProxy}; use grin_core as core; use grin_keychain as keychain; use grin_util as util; use grin_wallet_libwallet as libwallet; +use impls::test_framework::{self, LocalWalletClient, WalletProxy}; use std::fs; use std::thread; use std::time::Duration; diff --git a/refwallet/tests/transaction.rs b/controller/tests/transaction.rs similarity index 99% rename from refwallet/tests/transaction.rs rename to controller/tests/transaction.rs index 3b2fa561..95b17634 100644 --- a/refwallet/tests/transaction.rs +++ b/controller/tests/transaction.rs @@ -14,18 +14,19 @@ //! tests for transactions building within core::libtx #[macro_use] extern crate log; +extern crate grin_wallet_controller as wallet; +extern crate grin_wallet_impls as impls; extern crate grin_wallet_libwallet as libwallet; -extern crate grin_wallet_refwallet as wallet; use self::core::global; use self::core::global::ChainTypes; use self::keychain::ExtKeychain; use self::libwallet::slate::Slate; use self::libwallet::types::OutputStatus; -use self::wallet::test_framework::{self, LocalWalletClient, WalletProxy}; use grin_core as core; use grin_keychain as keychain; use grin_util as util; +use impls::test_framework::{self, LocalWalletClient, WalletProxy}; use std::fs; use std::thread; use std::time::Duration; diff --git a/impls/Cargo.toml b/impls/Cargo.toml new file mode 100644 index 00000000..d18282d6 --- /dev/null +++ b/impls/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "grin_wallet_impls" +version = "1.1.0" +authors = ["Grin Developers "] +description = "Concrete types derived from libwallet traits" +license = "Apache-2.0" +repository = "https://github.com/mimblewimble/grin" +keywords = [ "crypto", "grin", "mimblewimble" ] +readme = "README.md" +exclude = ["**/*.grin", "**/*.grin2"] +edition = "2018" + +[dependencies] +blake2-rfc = "0.2" +failure = "0.1" +failure_derive = "0.1" +futures = "0.1" +rand = "0.5" +serde = "1" +serde_derive = "1" +serde_json = "1" +log = "0.4" +ring = "0.13" +tokio = "= 0.1.11" +tokio-core = "0.1" +tokio-retry = "0.1" +uuid = { version = "0.6", features = ["serde", "v4"] } +chrono = { version = "0.4.4", features = ["serde"] } + +grin_wallet_libwallet = { path = "../libwallet", version = "1.1.0" } +grin_wallet_config = { path = "../config", version = "1.1.0" } + +grin_core = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } +grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } +grin_chain = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } +grin_util = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } +grin_api = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } +grin_store = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } diff --git a/refwallet/src/adapters/file.rs b/impls/src/adapters/file.rs similarity index 96% rename from refwallet/src/adapters/file.rs rename to impls/src/adapters/file.rs index 3dc62ca2..2e396b29 100644 --- a/refwallet/src/adapters/file.rs +++ b/impls/src/adapters/file.rs @@ -16,9 +16,10 @@ use std::fs::File; use std::io::{Read, Write}; +use crate::config::WalletConfig; use crate::libwallet::slate::Slate; use crate::libwallet::Error; -use crate::{WalletCommAdapter, WalletConfig}; +use crate::WalletCommAdapter; use std::collections::HashMap; #[derive(Clone)] diff --git a/refwallet/src/adapters/http.rs b/impls/src/adapters/http.rs similarity index 71% rename from refwallet/src/adapters/http.rs rename to impls/src/adapters/http.rs index ec6664d4..c7b2034f 100644 --- a/refwallet/src/adapters/http.rs +++ b/impls/src/adapters/http.rs @@ -12,13 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +/// HTTP Wallet 'plugin' implementation use crate::api; -use crate::controller; use crate::libwallet::slate::Slate; use crate::libwallet::{Error, ErrorKind}; -use crate::{instantiate_wallet, HTTPNodeClient, WalletCommAdapter, WalletConfig}; -/// HTTP Wallet 'plugin' implementation -use failure::ResultExt; +use crate::WalletCommAdapter; +use config::WalletConfig; use std::collections::HashMap; #[derive(Clone)] @@ -69,24 +68,12 @@ impl WalletCommAdapter for HTTPWalletCommAdapter { fn listen( &self, - params: HashMap, - config: WalletConfig, - passphrase: &str, - account: &str, - node_api_secret: Option, + _params: HashMap, + _config: WalletConfig, + _passphrase: &str, + _account: &str, + _node_api_secret: Option, ) -> Result<(), Error> { - let node_client = HTTPNodeClient::new(&config.check_node_api_http_addr, node_api_secret); - let wallet = instantiate_wallet(config.clone(), node_client, passphrase, account) - .context(ErrorKind::WalletSeedDecryption)?; - let listen_addr = params.get("api_listen_addr").unwrap(); - let tls_conf = match params.get("certificate") { - Some(s) => Some(api::TLSConfig::new( - s.to_owned(), - params.get("private_key").unwrap().to_owned(), - )), - None => None, - }; - controller::foreign_listener(wallet.clone(), &listen_addr, tls_conf)?; - Ok(()) + unimplemented!(); } } diff --git a/refwallet/src/adapters/keybase.rs b/impls/src/adapters/keybase.rs similarity index 96% rename from refwallet/src/adapters/keybase.rs rename to impls/src/adapters/keybase.rs index 0396ba9b..5cbeb711 100644 --- a/refwallet/src/adapters/keybase.rs +++ b/impls/src/adapters/keybase.rs @@ -14,10 +14,11 @@ // Keybase Wallet Plugin -use crate::controller; +use crate::config::WalletConfig; +use crate::libwallet::api_impl::foreign; use crate::libwallet::slate::Slate; use crate::libwallet::{Error, ErrorKind}; -use crate::{instantiate_wallet, HTTPNodeClient, WalletCommAdapter, WalletConfig}; +use crate::{instantiate_wallet, HTTPNodeClient, WalletCommAdapter}; use failure::ResultExt; use serde::Serialize; use serde_json::{from_str, json, to_string, Value}; @@ -374,14 +375,18 @@ impl WalletCommAdapter for KeybaseWalletCommAdapter { slate.amount as f64 / 1000000000.0, tx_uuid, ); - match controller::foreign_single_use(wallet.clone(), |api| { - if let Err(e) = api.verify_slate_messages(&slate) { - error!("Error validating participant messages: {}", e); - return Err(e); - } - api.receive_tx(&mut slate, None, None)?; - Ok(()) - }) { + if let Err(e) = slate.verify_messages() { + error!("Error validating participant messages: {}", e); + return Err(e); + } + let res = { + let mut w = wallet.lock(); + w.open_with_credentials()?; + let r = foreign::receive_tx(&mut *w, &mut slate, None, None); + w.close()?; + r + }; + match res { // Reply to the same channel with topic SLATE_SIGNED Ok(_) => { let slate = slate diff --git a/refwallet/src/adapters/mod.rs b/impls/src/adapters/mod.rs similarity index 98% rename from refwallet/src/adapters/mod.rs rename to impls/src/adapters/mod.rs index c42b0964..f765cb4b 100644 --- a/refwallet/src/adapters/mod.rs +++ b/impls/src/adapters/mod.rs @@ -22,9 +22,9 @@ pub use self::http::HTTPWalletCommAdapter; pub use self::keybase::KeybaseWalletCommAdapter; pub use self::null::NullWalletCommAdapter; +use crate::config::WalletConfig; use crate::libwallet::slate::Slate; use crate::libwallet::Error; -use crate::WalletConfig; use std::collections::HashMap; /// Encapsulate wallet to wallet communication functions diff --git a/refwallet/src/adapters/null.rs b/impls/src/adapters/null.rs similarity index 95% rename from refwallet/src/adapters/null.rs rename to impls/src/adapters/null.rs index 9ac7500e..8c6e4723 100644 --- a/refwallet/src/adapters/null.rs +++ b/impls/src/adapters/null.rs @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::config::WalletConfig; /// Null Output 'plugin' implementation use crate::libwallet::slate::Slate; use crate::libwallet::Error; -use crate::{WalletCommAdapter, WalletConfig}; +use crate::WalletCommAdapter; use std::collections::HashMap; diff --git a/impls/src/backends/lmdb.rs b/impls/src/backends/lmdb.rs new file mode 100644 index 00000000..d939808e --- /dev/null +++ b/impls/src/backends/lmdb.rs @@ -0,0 +1,563 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::cell::RefCell; +use std::{fs, path}; + +// for writing storedtransaction files +use std::fs::File; +use std::io::{Read, Write}; +use std::path::Path; + +use failure::ResultExt; +use uuid::Uuid; + +use crate::blake2::blake2b::Blake2b; + +use crate::keychain::{ChildNumber, ExtKeychain, Identifier, Keychain}; +use crate::store::{self, option_to_not_found, to_key, to_key_u64}; + +use crate::core::core::Transaction; +use crate::core::{global, ser}; +use crate::libwallet::types::*; +use crate::libwallet::{internal, Error, ErrorKind}; +use crate::util; +use crate::util::secp::constants::SECRET_KEY_SIZE; +use crate::util::ZeroingString; +use crate::WalletSeed; +use config::WalletConfig; + +pub const DB_DIR: &'static str = "db"; +pub const TX_SAVE_DIR: &'static str = "saved_txs"; + +const OUTPUT_PREFIX: u8 = 'o' as u8; +const DERIV_PREFIX: u8 = 'd' as u8; +const CONFIRMED_HEIGHT_PREFIX: u8 = 'c' as u8; +const PRIVATE_TX_CONTEXT_PREFIX: u8 = 'p' as u8; +const TX_LOG_ENTRY_PREFIX: u8 = 't' as u8; +const TX_LOG_ID_PREFIX: u8 = 'i' as u8; +const ACCOUNT_PATH_MAPPING_PREFIX: u8 = 'a' as u8; + +/// 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() +} + +/// Helper to derive XOR keys for storing private transaction keys in the DB +/// (blind_xor_key, nonce_xor_key) +fn private_ctx_xor_keys( + keychain: &K, + slate_id: &[u8], +) -> Result<([u8; SECRET_KEY_SIZE], [u8; SECRET_KEY_SIZE]), Error> +where + K: Keychain, +{ + let root_key = keychain.derive_key(0, &K::root_key_id())?; + + // derive XOR values for storing secret values in DB + // h(root_key|slate_id|"blind") + let mut hasher = Blake2b::new(SECRET_KEY_SIZE); + hasher.update(&root_key.0[..]); + hasher.update(&slate_id[..]); + hasher.update(&"blind".as_bytes()[..]); + let blind_xor_key = hasher.finalize(); + let mut ret_blind = [0; SECRET_KEY_SIZE]; + ret_blind.copy_from_slice(&blind_xor_key.as_bytes()[0..SECRET_KEY_SIZE]); + + // h(root_key|slate_id|"nonce") + let mut hasher = Blake2b::new(SECRET_KEY_SIZE); + hasher.update(&root_key.0[..]); + hasher.update(&slate_id[..]); + hasher.update(&"nonce".as_bytes()[..]); + let nonce_xor_key = hasher.finalize(); + let mut ret_nonce = [0; SECRET_KEY_SIZE]; + ret_nonce.copy_from_slice(&nonce_xor_key.as_bytes()[0..SECRET_KEY_SIZE]); + + Ok((ret_blind, ret_nonce)) +} + +pub struct LMDBBackend { + db: store::Store, + config: WalletConfig, + /// passphrase: TODO better ways of dealing with this other than storing + passphrase: ZeroingString, + /// Keychain + pub keychain: Option, + /// Parent path to use by default for output operations + parent_key_id: Identifier, + /// wallet to node client + w2n_client: C, +} + +impl LMDBBackend { + pub fn new(config: WalletConfig, passphrase: &str, n_client: C) -> 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!"); + + let stored_tx_path = path::Path::new(&config.data_file_dir).join(TX_SAVE_DIR); + fs::create_dir_all(&stored_tx_path) + .expect("Couldn't create wallet backend tx storage directory!"); + + let store = store::Store::new(db_path.to_str().unwrap(), None, Some(DB_DIR), None)?; + + // Make sure default wallet derivation path always exists + // as well as path (so it can be retrieved by batches to know where to store + // completed transactions, for reference + let default_account = AcctPathMapping { + label: "default".to_owned(), + path: LMDBBackend::::default_path(), + }; + let acct_key = to_key( + ACCOUNT_PATH_MAPPING_PREFIX, + &mut default_account.label.as_bytes().to_vec(), + ); + + { + let batch = store.batch()?; + batch.put_ser(&acct_key, &default_account)?; + batch.commit()?; + } + + let res = LMDBBackend { + db: store, + config: config.clone(), + passphrase: ZeroingString::from(passphrase), + keychain: None, + parent_key_id: LMDBBackend::::default_path(), + w2n_client: n_client, + }; + Ok(res) + } + + fn default_path() -> Identifier { + // return the default parent wallet path, corresponding to the default account + // in the BIP32 spec. Parent is account 0 at level 2, child output identifiers + // are all at level 3 + ExtKeychain::derive_key_id(2, 0, 0, 0, 0) + } + + /// 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 +where + C: NodeClient, + K: Keychain, +{ + /// Initialise with whatever stored credentials we have + fn open_with_credentials(&mut self) -> Result<(), Error> { + let wallet_seed = WalletSeed::from_file(&self.config, &self.passphrase) + .context(ErrorKind::CallbackImpl("Error opening wallet"))?; + self.keychain = Some( + wallet_seed + .derive_keychain(global::is_floonet()) + .context(ErrorKind::CallbackImpl("Error deriving keychain"))?, + ); + Ok(()) + } + + /// Close wallet and remove any stored credentials (TBD) + fn close(&mut self) -> Result<(), Error> { + self.keychain = None; + Ok(()) + } + + /// Return the keychain being used + fn keychain(&mut self) -> &mut K { + self.keychain.as_mut().unwrap() + } + + /// Return the node client being used + fn w2n_client(&mut self) -> &mut C { + &mut self.w2n_client + } + + /// return the version of the commit for caching + fn calc_commit_for_cache( + &mut self, + amount: u64, + id: &Identifier, + ) -> Result, Error> { + if self.config.no_commit_cache == Some(true) { + Ok(None) + } else { + Ok(Some(util::to_hex( + self.keychain().commit(amount, &id)?.0.to_vec(), + ))) + } + } + + /// Set parent path by account name + fn set_parent_key_id_by_name(&mut self, label: &str) -> Result<(), Error> { + let label = label.to_owned(); + let res = self.acct_path_iter().find(|l| l.label == label); + if let Some(a) = res { + self.set_parent_key_id(a.path); + Ok(()) + } else { + return Err(ErrorKind::UnknownAccountLabel(label.clone()).into()); + } + } + + /// set parent path + fn set_parent_key_id(&mut self, id: Identifier) { + self.parent_key_id = id; + } + + fn parent_key_id(&mut self) -> Identifier { + self.parent_key_id.clone() + } + + fn get(&self, id: &Identifier, mmr_index: &Option) -> Result { + let key = match mmr_index { + Some(i) => to_key_u64(OUTPUT_PREFIX, &mut id.to_bytes().to_vec(), *i), + None => to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()), + }; + option_to_not_found(self.db.get_ser(&key), &format!("Key Id: {}", id)).map_err(|e| e.into()) + } + + fn iter<'a>(&'a self) -> Box + 'a> { + Box::new(self.db.iter(&[OUTPUT_PREFIX]).unwrap()) + } + + fn get_tx_log_entry(&self, u: &Uuid) -> Result, Error> { + let key = to_key(TX_LOG_ENTRY_PREFIX, &mut u.as_bytes().to_vec()); + self.db.get_ser(&key).map_err(|e| e.into()) + } + + fn tx_log_iter<'a>(&'a self) -> Box + 'a> { + Box::new(self.db.iter(&[TX_LOG_ENTRY_PREFIX]).unwrap()) + } + + fn get_private_context(&mut self, slate_id: &[u8]) -> Result { + let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec()); + let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain(), slate_id)?; + + let mut ctx: Context = option_to_not_found( + self.db.get_ser(&ctx_key), + &format!("Slate id: {:x?}", slate_id.to_vec()), + )?; + + for i in 0..SECRET_KEY_SIZE { + ctx.sec_key.0[i] = ctx.sec_key.0[i] ^ blind_xor_key[i]; + ctx.sec_nonce.0[i] = ctx.sec_nonce.0[i] ^ nonce_xor_key[i]; + } + + Ok(ctx) + } + + fn acct_path_iter<'a>(&'a self) -> Box + 'a> { + Box::new(self.db.iter(&[ACCOUNT_PATH_MAPPING_PREFIX]).unwrap()) + } + + fn get_acct_path(&self, label: String) -> Result, Error> { + let acct_key = to_key(ACCOUNT_PATH_MAPPING_PREFIX, &mut label.as_bytes().to_vec()); + self.db.get_ser(&acct_key).map_err(|e| e.into()) + } + + fn store_tx(&self, uuid: &str, tx: &Transaction) -> Result<(), Error> { + let filename = format!("{}.grintx", uuid); + let path = path::Path::new(&self.config.data_file_dir) + .join(TX_SAVE_DIR) + .join(filename); + let path_buf = Path::new(&path).to_path_buf(); + let mut stored_tx = File::create(path_buf)?; + let tx_hex = util::to_hex(ser::ser_vec(tx).unwrap());; + stored_tx.write_all(&tx_hex.as_bytes())?; + stored_tx.sync_all()?; + Ok(()) + } + + fn get_stored_tx(&self, entry: &TxLogEntry) -> Result, Error> { + let filename = match entry.stored_tx.clone() { + Some(f) => f, + None => return Ok(None), + }; + let path = path::Path::new(&self.config.data_file_dir) + .join(TX_SAVE_DIR) + .join(filename); + let tx_file = Path::new(&path).to_path_buf(); + let mut tx_f = File::open(tx_file)?; + let mut content = String::new(); + tx_f.read_to_string(&mut content)?; + let tx_bin = util::from_hex(content).unwrap(); + Ok(Some( + ser::deserialize::(&mut &tx_bin[..]).unwrap(), + )) + } + + fn batch<'a>(&'a mut self) -> Result + 'a>, Error> { + Ok(Box::new(Batch { + _store: self, + db: RefCell::new(Some(self.db.batch()?)), + keychain: self.keychain.clone(), + })) + } + + fn next_child<'a>(&mut self) -> Result { + let parent_key_id = self.parent_key_id.clone(); + let mut deriv_idx = { + let batch = self.db.batch()?; + let deriv_key = to_key(DERIV_PREFIX, &mut self.parent_key_id.to_bytes().to_vec()); + match batch.get_ser(&deriv_key)? { + Some(idx) => idx, + None => 0, + } + }; + let mut return_path = self.parent_key_id.to_path(); + return_path.depth = return_path.depth + 1; + return_path.path[return_path.depth as usize - 1] = ChildNumber::from(deriv_idx); + deriv_idx = deriv_idx + 1; + let mut batch = self.batch()?; + batch.save_child_index(&parent_key_id, deriv_idx)?; + batch.commit()?; + Ok(Identifier::from_path(&return_path)) + } + + fn last_confirmed_height<'a>(&mut self) -> Result { + let batch = self.db.batch()?; + let height_key = to_key( + CONFIRMED_HEIGHT_PREFIX, + &mut self.parent_key_id.to_bytes().to_vec(), + ); + let last_confirmed_height = match batch.get_ser(&height_key)? { + Some(h) => h, + None => 0, + }; + Ok(last_confirmed_height) + } + + fn restore(&mut self) -> Result<(), Error> { + internal::restore::restore(self).context(ErrorKind::Restore)?; + Ok(()) + } + + fn check_repair(&mut self, delete_unconfirmed: bool) -> Result<(), Error> { + internal::restore::check_repair(self, delete_unconfirmed).context(ErrorKind::Restore)?; + Ok(()) + } +} + +/// An atomic batch in which all changes can be committed all at once or +/// discarded on error. +pub struct Batch<'a, C, K> +where + C: NodeClient, + K: Keychain, +{ + _store: &'a LMDBBackend, + db: RefCell>>, + /// Keychain + keychain: Option, +} + +#[allow(missing_docs)] +impl<'a, C, K> WalletOutputBatch for Batch<'a, C, K> +where + C: NodeClient, + K: Keychain, +{ + fn keychain(&mut self) -> &mut K { + self.keychain.as_mut().unwrap() + } + + fn save(&mut self, out: OutputData) -> Result<(), Error> { + // Save the output data to the db. + { + let key = match out.mmr_index { + Some(i) => to_key_u64(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec(), i), + None => to_key(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec()), + }; + self.db.borrow().as_ref().unwrap().put_ser(&key, &out)?; + } + + Ok(()) + } + + fn get(&self, id: &Identifier, mmr_index: &Option) -> Result { + let key = match mmr_index { + Some(i) => to_key_u64(OUTPUT_PREFIX, &mut id.to_bytes().to_vec(), *i), + None => to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()), + }; + option_to_not_found( + self.db.borrow().as_ref().unwrap().get_ser(&key), + &format!("Key ID: {}", id), + ) + .map_err(|e| e.into()) + } + + fn iter(&self) -> Box> { + Box::new( + self.db + .borrow() + .as_ref() + .unwrap() + .iter(&[OUTPUT_PREFIX]) + .unwrap(), + ) + } + + fn delete(&mut self, id: &Identifier, mmr_index: &Option) -> Result<(), Error> { + // Delete the output data. + { + let key = match mmr_index { + Some(i) => to_key_u64(OUTPUT_PREFIX, &mut id.to_bytes().to_vec(), *i), + None => to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()), + }; + let _ = self.db.borrow().as_ref().unwrap().delete(&key); + } + + Ok(()) + } + + fn next_tx_log_id(&mut self, parent_key_id: &Identifier) -> Result { + let tx_id_key = to_key(TX_LOG_ID_PREFIX, &mut parent_key_id.to_bytes().to_vec()); + let last_tx_log_id = match self.db.borrow().as_ref().unwrap().get_ser(&tx_id_key)? { + Some(t) => t, + None => 0, + }; + self.db + .borrow() + .as_ref() + .unwrap() + .put_ser(&tx_id_key, &(last_tx_log_id + 1))?; + Ok(last_tx_log_id) + } + + fn tx_log_iter(&self) -> Box> { + Box::new( + self.db + .borrow() + .as_ref() + .unwrap() + .iter(&[TX_LOG_ENTRY_PREFIX]) + .unwrap(), + ) + } + + fn save_last_confirmed_height( + &mut self, + parent_key_id: &Identifier, + height: u64, + ) -> Result<(), Error> { + let height_key = to_key( + CONFIRMED_HEIGHT_PREFIX, + &mut parent_key_id.to_bytes().to_vec(), + ); + self.db + .borrow() + .as_ref() + .unwrap() + .put_ser(&height_key, &height)?; + Ok(()) + } + + fn save_child_index(&mut self, parent_id: &Identifier, child_n: u32) -> Result<(), Error> { + let deriv_key = to_key(DERIV_PREFIX, &mut parent_id.to_bytes().to_vec()); + self.db + .borrow() + .as_ref() + .unwrap() + .put_ser(&deriv_key, &child_n)?; + Ok(()) + } + + fn save_tx_log_entry( + &mut self, + tx_in: TxLogEntry, + parent_id: &Identifier, + ) -> Result<(), Error> { + let tx_log_key = to_key_u64( + TX_LOG_ENTRY_PREFIX, + &mut parent_id.to_bytes().to_vec(), + tx_in.id as u64, + ); + self.db + .borrow() + .as_ref() + .unwrap() + .put_ser(&tx_log_key, &tx_in)?; + Ok(()) + } + + fn save_acct_path(&mut self, mapping: AcctPathMapping) -> Result<(), Error> { + let acct_key = to_key( + ACCOUNT_PATH_MAPPING_PREFIX, + &mut mapping.label.as_bytes().to_vec(), + ); + self.db + .borrow() + .as_ref() + .unwrap() + .put_ser(&acct_key, &mapping)?; + Ok(()) + } + + fn acct_path_iter(&self) -> Box> { + Box::new( + self.db + .borrow() + .as_ref() + .unwrap() + .iter(&[ACCOUNT_PATH_MAPPING_PREFIX]) + .unwrap(), + ) + } + + fn lock_output(&mut self, out: &mut OutputData) -> Result<(), Error> { + out.lock(); + self.save(out.clone()) + } + + fn save_private_context(&mut self, slate_id: &[u8], ctx: &Context) -> Result<(), Error> { + let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec()); + let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain(), slate_id)?; + + let mut s_ctx = ctx.clone(); + for i in 0..SECRET_KEY_SIZE { + s_ctx.sec_key.0[i] = s_ctx.sec_key.0[i] ^ blind_xor_key[i]; + s_ctx.sec_nonce.0[i] = s_ctx.sec_nonce.0[i] ^ nonce_xor_key[i]; + } + + self.db + .borrow() + .as_ref() + .unwrap() + .put_ser(&ctx_key, &s_ctx)?; + Ok(()) + } + + fn delete_private_context(&mut self, slate_id: &[u8]) -> Result<(), Error> { + let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec()); + self.db + .borrow() + .as_ref() + .unwrap() + .delete(&ctx_key) + .map_err(|e| e.into()) + } + + fn commit(&self) -> Result<(), Error> { + let db = self.db.replace(None); + db.unwrap().commit()?; + Ok(()) + } +} diff --git a/impls/src/backends/mod.rs b/impls/src/backends/mod.rs new file mode 100644 index 00000000..83b527ae --- /dev/null +++ b/impls/src/backends/mod.rs @@ -0,0 +1,17 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod lmdb; + +pub use self::lmdb::LMDBBackend; diff --git a/impls/src/error.rs b/impls/src/error.rs new file mode 100644 index 00000000..85ee501d --- /dev/null +++ b/impls/src/error.rs @@ -0,0 +1,164 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation specific error types +use crate::core::libtx; +use crate::keychain; +use crate::libwallet; +use failure::{Backtrace, Context, Fail}; +use std::env; +use std::fmt::{self, Display}; + +/// Error definition +#[derive(Debug)] +pub struct Error { + pub inner: Context, +} + +/// Wallet errors, mostly wrappers around underlying crypto or I/O errors. +#[derive(Clone, Eq, PartialEq, Debug, Fail)] +pub enum ErrorKind { + /// LibTX Error + #[fail(display = "LibTx Error")] + LibTX(libtx::ErrorKind), + + /// LibWallet Error + #[fail(display = "LibWallet Error: {}", _1)] + LibWallet(libwallet::ErrorKind, String), + + /// Keychain error + #[fail(display = "Keychain error")] + Keychain(keychain::Error), + + /// Error when formatting json + #[fail(display = "IO error")] + IO, + + /// Error when formatting json + #[fail(display = "Serde JSON error")] + Format, + + /// Wallet seed already exists + #[fail(display = "Wallet seed file exists: {}", _0)] + WalletSeedExists(String), + + /// Wallet seed doesn't exist + #[fail(display = "Wallet seed doesn't exist error")] + WalletSeedDoesntExist, + + /// Enc/Decryption Error + #[fail(display = "Enc/Decryption error (check password?)")] + Encryption, + + /// BIP 39 word list + #[fail(display = "BIP39 Mnemonic (word list) Error")] + Mnemonic, + + /// Command line argument error + #[fail(display = "{}", _0)] + ArgumentError(String), + + /// Other + #[fail(display = "Generic error: {}", _0)] + GenericError(String), +} + +impl Fail for Error { + fn cause(&self) -> Option<&dyn Fail> { + self.inner.cause() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let show_bt = match env::var("RUST_BACKTRACE") { + Ok(r) => { + if r == "1" { + true + } else { + false + } + } + Err(_) => false, + }; + let backtrace = match self.backtrace() { + Some(b) => format!("{}", b), + None => String::from("Unknown"), + }; + let inner_output = format!("{}", self.inner,); + let backtrace_output = format!("\nBacktrace: {}", backtrace); + let mut output = inner_output.clone(); + if show_bt { + output.push_str(&backtrace_output); + } + Display::fmt(&output, f) + } +} + +impl Error { + /// get kind + pub fn kind(&self) -> ErrorKind { + self.inner.get_context().clone() + } + /// get cause + pub fn cause(&self) -> Option<&dyn Fail> { + self.inner.cause() + } + /// get backtrace + pub fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl From for Error { + fn from(kind: ErrorKind) -> Error { + Error { + inner: Context::new(kind), + } + } +} + +impl From> for Error { + fn from(inner: Context) -> Error { + Error { inner: inner } + } +} + +impl From for Error { + fn from(error: keychain::Error) -> Error { + Error { + inner: Context::new(ErrorKind::Keychain(error)), + } + } +} + +impl From for Error { + fn from(error: libwallet::Error) -> Error { + Error { + inner: Context::new(ErrorKind::LibWallet(error.kind(), format!("{}", error))), + } + } +} + +impl From for Error { + fn from(error: libtx::Error) -> Error { + Error { + inner: Context::new(ErrorKind::LibTX(error.kind())), + } + } +} diff --git a/refwallet/src/lib.rs b/impls/src/lib.rs similarity index 76% rename from refwallet/src/lib.rs rename to impls/src/lib.rs index a931b652..d375cecb 100644 --- a/refwallet/src/lib.rs +++ b/impls/src/lib.rs @@ -12,57 +12,48 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Library module for the main wallet functionalities provided by Grin. +//! Concrete implementations of types found in libwallet, organised this +//! way mostly to avoid any circular dependencies of any kind +//! Functions in this crate should not use the wallet api crate directly use blake2_rfc as blake2; -#[macro_use] -extern crate prettytable; - #[macro_use] extern crate serde_derive; #[macro_use] extern crate log; -use failure; -use grin_api as api; extern crate grin_core as core; +use grin_api as api; use grin_keychain as keychain; use grin_store as store; use grin_util as util; -use grin_wallet_api as apiwallet; use grin_wallet_libwallet as libwallet; extern crate grin_wallet_config as config; mod adapters; -pub mod command; -pub mod controller; -pub mod display; mod error; -pub mod lmdb_wallet; +mod lmdb_wallet; mod node_clients; +mod seed; pub mod test_framework; -mod types; pub use crate::adapters::{ FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter, NullWalletCommAdapter, WalletCommAdapter, }; pub use crate::error::{Error, ErrorKind}; -pub use crate::libwallet::slate::Slate; -pub use crate::libwallet::types::{ - BlockFees, CbData, NodeClient, WalletBackend, WalletInfo, WalletInst, -}; pub use crate::lmdb_wallet::{wallet_db_exists, LMDBBackend}; -pub use crate::node_clients::{create_coinbase, HTTPNodeClient}; -pub use crate::types::{EncryptedWalletSeed, WalletSeed, SEED_FILE}; -use config::WalletConfig; +pub use crate::node_clients::HTTPNodeClient; +pub use crate::seed::{EncryptedWalletSeed, WalletSeed, SEED_FILE}; use crate::util::Mutex; use std::sync::Arc; +use libwallet::types::{NodeClient, WalletBackend, WalletInst}; + /// Helper to create an instance of the LMDB wallet pub fn instantiate_wallet( - wallet_config: WalletConfig, + wallet_config: config::WalletConfig, node_client: impl NodeClient + 'static, passphrase: &str, account: &str, diff --git a/refwallet/src/lmdb_wallet.rs b/impls/src/lmdb_wallet.rs similarity index 98% rename from refwallet/src/lmdb_wallet.rs rename to impls/src/lmdb_wallet.rs index 3b1bb56d..4aeb9f66 100644 --- a/refwallet/src/lmdb_wallet.rs +++ b/impls/src/lmdb_wallet.rs @@ -31,11 +31,12 @@ use crate::store::{self, option_to_not_found, to_key, to_key_u64}; use crate::core::core::Transaction; use crate::core::{global, ser}; use crate::libwallet::types::*; -use crate::libwallet::{internal, Error, ErrorKind}; -use crate::types::WalletSeed; +use crate::libwallet::{check_repair, restore}; +use crate::libwallet::{Error, ErrorKind}; use crate::util; use crate::util::secp::constants::SECRET_KEY_SIZE; use crate::util::ZeroingString; +use crate::WalletSeed; use config::WalletConfig; pub const DB_DIR: &'static str = "db"; @@ -346,12 +347,12 @@ where } fn restore(&mut self) -> Result<(), Error> { - internal::restore::restore(self).context(ErrorKind::Restore)?; + restore(self).context(ErrorKind::Restore)?; Ok(()) } fn check_repair(&mut self, delete_unconfirmed: bool) -> Result<(), Error> { - internal::restore::check_repair(self, delete_unconfirmed).context(ErrorKind::Restore)?; + check_repair(self, delete_unconfirmed).context(ErrorKind::Restore)?; Ok(()) } } diff --git a/refwallet/src/node_clients/http.rs b/impls/src/node_clients/http.rs similarity index 99% rename from refwallet/src/node_clients/http.rs rename to impls/src/node_clients/http.rs index e07a9c6e..a9c13ee1 100644 --- a/refwallet/src/node_clients/http.rs +++ b/impls/src/node_clients/http.rs @@ -15,7 +15,6 @@ //! Client functions, implementations of the NodeClient trait //! specific to the FileWallet -use failure::ResultExt; use futures::{stream, Stream}; use crate::libwallet::types::*; @@ -23,7 +22,6 @@ use std::collections::HashMap; use tokio::runtime::Runtime; use crate::api; -use crate::error::{Error, ErrorKind}; use crate::libwallet; use crate::util; use crate::util::secp::pedersen; @@ -192,6 +190,7 @@ impl NodeClient for HTTPNodeClient { } } +/* /// 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 { @@ -216,4 +215,4 @@ fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result Option { let outputs = [ @@ -130,11 +124,14 @@ where height: prev.height + 1, }; // build coinbase (via api) and add block - controller::foreign_single_use(wallet.clone(), |api| { - let coinbase_tx = api.build_coinbase(&block_fees)?; - add_block_with_reward(chain, txs, coinbase_tx.clone()); - Ok(()) - })?; + let coinbase_tx = { + let mut w = wallet.lock(); + w.open_with_credentials()?; + let res = foreign::build_coinbase(&mut *w, &block_fees)?; + w.close()?; + res + }; + add_block_with_reward(chain, txs, coinbase_tx.clone()); Ok(()) } @@ -184,8 +181,8 @@ where /// send an amount to a destination pub fn send_to_dest( + wallet: Arc>>, client: LocalWalletClient, - api: &mut Owner, dest: &str, amount: u64, ) -> Result<(), libwallet::Error> @@ -194,32 +191,45 @@ where C: NodeClient, K: keychain::Keychain, { - let slate_i = api.initiate_tx( - None, // account - amount, // amount - 2, // minimum confirmations - 500, // max outputs - 1, // num change outputs - true, // select all outputs - None, None, - )?; - let mut slate = client.send_tx_slate_direct(dest, &slate_i)?; - api.tx_lock_outputs(&slate)?; - api.finalize_tx(&mut slate)?; - api.post_tx(&slate.tx, false)?; // mines a block + let slate = { + let mut w = wallet.lock(); + w.open_with_credentials()?; + let slate_i = owner::initiate_tx( + &mut *w, None, // account + amount, // amount + 2, // minimum confirmations + 500, // max outputs + 1, // num change outputs + true, // select all outputs + None, None, + )?; + let mut slate = client.send_tx_slate_direct(dest, &slate_i)?; + owner::tx_lock_outputs(&mut *w, &slate)?; + owner::finalize_tx(&mut *w, &mut slate)?; + w.close()?; + slate + }; + let client = { + let mut w = wallet.lock(); + w.w2n_client().clone() + }; + owner::post_tx(&client, &slate.tx, false)?; // mines a block Ok(()) } /// get wallet info totals pub fn wallet_info( - api: &mut Owner, + wallet: Arc>>, ) -> Result where T: WalletBackend, C: NodeClient, K: keychain::Keychain, { - let (wallet_refreshed, wallet_info) = api.retrieve_summary_info(true, 1)?; + let mut w = wallet.lock(); + w.open_with_credentials()?; + let (wallet_refreshed, wallet_info) = owner::retrieve_summary_info(&mut *w, true, 1)?; assert!(wallet_refreshed); + w.close()?; Ok(wallet_info) } diff --git a/refwallet/src/test_framework/testclient.rs b/impls/src/test_framework/testclient.rs similarity index 96% rename from refwallet/src/test_framework/testclient.rs rename to impls/src/test_framework/testclient.rs index 97cb2522..f3576ef7 100644 --- a/refwallet/src/test_framework/testclient.rs +++ b/impls/src/test_framework/testclient.rs @@ -26,9 +26,11 @@ use self::keychain::Keychain; use self::util::secp::pedersen; use self::util::secp::pedersen::Commitment; use self::util::{Mutex, RwLock, StopState}; +use crate::config::WalletConfig; +use crate::libwallet::api_impl::foreign; use crate::libwallet::slate::Slate; use crate::libwallet::types::*; -use crate::{controller, libwallet, WalletCommAdapter, WalletConfig}; +use crate::{libwallet, WalletCommAdapter}; use failure::ResultExt; use grin_api as api; use grin_chain as chain; @@ -205,15 +207,23 @@ where m: WalletProxyMessage, ) -> Result { let dest_wallet = self.wallets.get_mut(&m.dest); - if let None = dest_wallet { - panic!("Unknown wallet destination for send_tx_slate: {:?}", m); + let wallet = match dest_wallet { + None => panic!("Unknown wallet destination for send_tx_slate: {:?}", m), + Some(w) => w, + }; + + let mut slate = serde_json::from_str(&m.body).context( + libwallet::ErrorKind::ClientCallback("Error parsing TxWrapper".to_owned()), + )?; +; + { + let mut w = wallet.1.lock(); + w.open_with_credentials()?; + // receive tx + foreign::receive_tx(&mut *w, &mut slate, None, None)?; + w.close()?; } - let w = dest_wallet.unwrap().1.clone(); - let mut slate = serde_json::from_str(&m.body).unwrap(); - controller::foreign_single_use(w.clone(), |listener_api| { - listener_api.receive_tx(&mut slate, None, None)?; - Ok(()) - })?; + Ok(WalletProxyMessage { sender_id: m.dest, dest: m.sender_id, diff --git a/refwallet/tests/encryption.rs b/impls/tests/encryption.rs similarity index 90% rename from refwallet/tests/encryption.rs rename to impls/tests/encryption.rs index 4f3ccef5..78a1b388 100644 --- a/refwallet/tests/encryption.rs +++ b/impls/tests/encryption.rs @@ -11,10 +11,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! core::libtx specific tests -extern crate grin_wallet_refwallet as wallet; +//! Wallet seed encryption tests +extern crate grin_wallet_impls as impls; -use self::wallet::{EncryptedWalletSeed, WalletSeed}; +use impls::{EncryptedWalletSeed, WalletSeed}; #[test] fn wallet_seed_encrypt() { diff --git a/libwallet/src/api_impl.rs b/libwallet/src/api_impl.rs new file mode 100644 index 00000000..0de2fe49 --- /dev/null +++ b/libwallet/src/api_impl.rs @@ -0,0 +1,25 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! lower-level wallet functions which build upon core::libtx to perform wallet +//! operations + +#![deny(non_upper_case_globals)] +#![deny(non_camel_case_types)] +#![deny(non_snake_case)] +#![deny(unused_mut)] +#![warn(missing_docs)] + +pub mod foreign; +pub mod owner; diff --git a/libwallet/src/api_impl/foreign.rs b/libwallet/src/api_impl/foreign.rs new file mode 100644 index 00000000..1beed5c2 --- /dev/null +++ b/libwallet/src/api_impl/foreign.rs @@ -0,0 +1,81 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Generic implementation of owner API functions + +use crate::internal::{tx, updater}; +use crate::keychain::Keychain; +use crate::slate::Slate; +use crate::types::{BlockFees, CbData, NodeClient, TxLogEntryType, WalletBackend}; +use crate::{Error, ErrorKind}; + +const USER_MESSAGE_MAX_LEN: usize = 256; + +/// Build a coinbase transaction +pub fn build_coinbase(w: &mut T, block_fees: &BlockFees) -> Result +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + updater::build_coinbase(&mut *w, block_fees) +} + +/// verify slate messages +pub fn verify_slate_messages(slate: &Slate) -> Result<(), Error> { + slate.verify_messages() +} + +/// Receive a tx as recipient +pub fn receive_tx( + w: &mut T, + slate: &mut Slate, + dest_acct_name: Option<&str>, + message: Option, +) -> Result<(), Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + let parent_key_id = match dest_acct_name { + Some(d) => { + let pm = w.get_acct_path(d.to_owned())?; + match pm { + Some(p) => p.path, + None => w.parent_key_id(), + } + } + None => w.parent_key_id(), + }; + // Don't do this multiple times + let tx = updater::retrieve_txs(&mut *w, None, Some(slate.id), Some(&parent_key_id), false)?; + for t in &tx { + if t.tx_type == TxLogEntryType::TxReceived { + return Err(ErrorKind::TransactionAlreadyReceived(slate.id.to_string()).into()); + } + } + + let message = match message { + Some(mut m) => { + m.truncate(USER_MESSAGE_MAX_LEN); + Some(m) + } + None => None, + }; + + tx::add_output_to_slate(&mut *w, slate, &parent_key_id, 1, message)?; + tx::update_message(&mut *w, slate)?; + Ok(()) +} diff --git a/libwallet/src/api_impl/owner.rs b/libwallet/src/api_impl/owner.rs new file mode 100644 index 00000000..07973687 --- /dev/null +++ b/libwallet/src/api_impl/owner.rs @@ -0,0 +1,384 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Generic implementation of owner API functions + +use uuid::Uuid; + +use crate::core::core::hash::Hashed; +use crate::core::core::Transaction; +use crate::core::ser; + +use crate::internal::{keys, selection, tx, updater}; +use crate::keychain::{Identifier, Keychain}; +use crate::slate::Slate; +use crate::types::{ + AcctPathMapping, NodeClient, OutputData, TxLogEntry, TxWrapper, WalletBackend, WalletInfo, +}; +use crate::util::secp::pedersen; +use crate::{Error, ErrorKind}; + +const USER_MESSAGE_MAX_LEN: usize = 256; + +/// List of accounts +pub fn accounts(w: &mut T) -> Result, Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + keys::accounts(&mut *w) +} + +/// new account path +pub fn create_account_path(w: &mut T, label: &str) -> Result +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + keys::new_acct_path(&mut *w, label) +} + +/// set active account +pub fn set_active_account(w: &mut T, label: &str) -> Result<(), Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + w.set_parent_key_id_by_name(label) +} + +/// retrieve outputs +pub fn retrieve_outputs( + w: &mut T, + include_spent: bool, + refresh_from_node: bool, + tx_id: Option, +) -> Result<(bool, Vec<(OutputData, pedersen::Commitment)>), Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + let parent_key_id = w.parent_key_id(); + + let mut validated = false; + if refresh_from_node { + validated = update_outputs(w, false); + } + + Ok(( + validated, + updater::retrieve_outputs(&mut *w, include_spent, tx_id, Some(&parent_key_id))?, + )) +} + +/// Retrieve txs +pub fn retrieve_txs( + w: &mut T, + refresh_from_node: bool, + tx_id: Option, + tx_slate_id: Option, +) -> Result<(bool, Vec), Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + let parent_key_id = w.parent_key_id(); + + let mut validated = false; + if refresh_from_node { + validated = update_outputs(w, false); + } + + Ok(( + validated, + updater::retrieve_txs(&mut *w, tx_id, tx_slate_id, Some(&parent_key_id), false)?, + )) +} + +/// Retrieve summary info +pub fn retrieve_summary_info( + w: &mut T, + refresh_from_node: bool, + minimum_confirmations: u64, +) -> Result<(bool, WalletInfo), Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + let parent_key_id = w.parent_key_id(); + + let mut validated = false; + if refresh_from_node { + validated = update_outputs(w, false); + } + + let wallet_info = updater::retrieve_info(&mut *w, &parent_key_id, minimum_confirmations)?; + Ok((validated, wallet_info)) +} + +/// Initiate tx as sender +pub fn initiate_tx( + w: &mut T, + src_acct_name: Option<&str>, + amount: u64, + minimum_confirmations: u64, + max_outputs: usize, + num_change_outputs: usize, + selection_strategy_is_use_all: bool, + message: Option, + target_slate_version: Option, +) -> Result +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + let parent_key_id = match src_acct_name { + Some(d) => { + let pm = w.get_acct_path(d.to_owned())?; + match pm { + Some(p) => p.path, + None => w.parent_key_id(), + } + } + None => w.parent_key_id(), + }; + + let message = match message { + Some(mut m) => { + m.truncate(USER_MESSAGE_MAX_LEN); + Some(m) + } + None => None, + }; + + let mut slate = tx::new_tx_slate(&mut *w, amount, 2)?; + + let context = tx::add_inputs_to_slate( + &mut *w, + &mut slate, + minimum_confirmations, + max_outputs, + num_change_outputs, + selection_strategy_is_use_all, + &parent_key_id, + 0, + message, + )?; + + // Save the aggsig context in our DB for when we + // recieve the transaction back + { + let mut batch = w.batch()?; + batch.save_private_context(slate.id.as_bytes(), &context)?; + batch.commit()?; + } + if let Some(v) = target_slate_version { + slate.version_info.orig_version = v; + } + Ok(slate) +} + +/// Estimate +pub fn estimate_initiate_tx( + w: &mut T, + src_acct_name: Option<&str>, + amount: u64, + minimum_confirmations: u64, + max_outputs: usize, + num_change_outputs: usize, + selection_strategy_is_use_all: bool, +) -> Result< + ( + u64, // total + u64, // fee + ), + Error, +> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + let parent_key_id = match src_acct_name { + Some(d) => { + let pm = w.get_acct_path(d.to_owned())?; + match pm { + Some(p) => p.path, + None => w.parent_key_id(), + } + } + None => w.parent_key_id(), + }; + tx::estimate_send_tx( + &mut *w, + amount, + minimum_confirmations, + max_outputs, + num_change_outputs, + selection_strategy_is_use_all, + &parent_key_id, + ) +} + +/// Lock sender outputs +pub fn tx_lock_outputs(w: &mut T, slate: &Slate) -> Result<(), Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + let context = w.get_private_context(slate.id.as_bytes())?; + selection::lock_tx_context(&mut *w, slate, &context) +} + +/// Finalize slate +pub fn finalize_tx(w: &mut T, slate: &mut Slate) -> Result<(), Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + let context = w.get_private_context(slate.id.as_bytes())?; + tx::complete_tx(&mut *w, slate, 0, &context)?; + tx::update_stored_tx(&mut *w, slate)?; + tx::update_message(&mut *w, slate)?; + { + let mut batch = w.batch()?; + batch.delete_private_context(slate.id.as_bytes())?; + batch.commit()?; + } + Ok(()) +} + +/// cancel tx +pub fn cancel_tx( + w: &mut T, + tx_id: Option, + tx_slate_id: Option, +) -> Result<(), Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + let parent_key_id = w.parent_key_id(); + if !update_outputs(w, false) { + return Err(ErrorKind::TransactionCancellationError( + "Can't contact running Grin node. Not Cancelling.", + ))?; + } + tx::cancel_tx(&mut *w, &parent_key_id, tx_id, tx_slate_id) +} + +/// get stored tx +pub fn get_stored_tx( + w: &T, + entry: &TxLogEntry, +) -> Result, Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + w.get_stored_tx(entry) +} + +/// Posts a transaction to the chain +/// take a client impl instead of wallet so as not to have to lock the wallet +pub fn post_tx(client: &C, tx: &Transaction, fluff: bool) -> Result<(), Error> +where + C: NodeClient, +{ + let tx_hex = util::to_hex(ser::ser_vec(tx).unwrap()); + let res = client.post_tx(&TxWrapper { tx_hex: tx_hex }, fluff); + if let Err(e) = res { + error!("api: post_tx: failed with error: {}", e); + Err(e) + } else { + debug!( + "api: post_tx: successfully posted tx: {}, fluff? {}", + tx.hash(), + fluff + ); + Ok(()) + } +} + +/// verify slate messages +pub fn verify_slate_messages(slate: &Slate) -> Result<(), Error> { + slate.verify_messages() +} + +/// Attempt to restore contents of wallet +pub fn restore(w: &mut T) -> Result<(), Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + w.restore() +} + +/// check repair +pub fn check_repair(w: &mut T, delete_unconfirmed: bool) -> Result<(), Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + update_outputs(w, true); + w.check_repair(delete_unconfirmed) +} + +/// node height +pub fn node_height(w: &mut T) -> Result<(u64, bool), Error> +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + let res = w.w2n_client().get_chain_height(); + match res { + Ok(height) => Ok((height, true)), + Err(_) => { + let outputs = retrieve_outputs(w, true, false, None)?; + let height = match outputs.1.iter().map(|(out, _)| out.height).max() { + Some(height) => height, + None => 0, + }; + Ok((height, false)) + } + } +} + +/// Attempt to update outputs in wallet, return whether it was successful +fn update_outputs(w: &mut T, update_all: bool) -> bool +where + T: WalletBackend, + C: NodeClient, + K: Keychain, +{ + let parent_key_id = w.parent_key_id(); + match updater::refresh_outputs(&mut *w, &parent_key_id, update_all) { + Ok(_) => true, + Err(_) => false, + } +} diff --git a/libwallet/src/internal/tx.rs b/libwallet/src/internal/tx.rs index 3577e758..8af3682d 100644 --- a/libwallet/src/internal/tx.rs +++ b/libwallet/src/internal/tx.rs @@ -234,26 +234,6 @@ where Ok(()) } -/// 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( - wallet: &mut T, - parent_key_id: &Identifier, - tx_id: u32, -) -> Result<(bool, Option), Error> -where - T: WalletBackend, - C: NodeClient, - K: Keychain, -{ - let tx_vec = updater::retrieve_txs(wallet, Some(tx_id), None, Some(parent_key_id), false)?; - if tx_vec.len() != 1 { - return Err(ErrorKind::TransactionDoesntExist(tx_id.to_string()))?; - } - let tx = tx_vec[0].clone(); - Ok((tx.confirmed, tx.stored_tx)) -} - /// Update the stored transaction (this update needs to happen when the TX is finalised) pub fn update_stored_tx(wallet: &mut T, slate: &Slate) -> Result<(), Error> where @@ -299,6 +279,7 @@ where batch.commit()?; Ok(()) } + #[cfg(test)] mod test { use crate::core::libtx::build; diff --git a/libwallet/src/lib.rs b/libwallet/src/lib.rs index 28d33891..27ba3a5f 100644 --- a/libwallet/src/lib.rs +++ b/libwallet/src/lib.rs @@ -24,7 +24,6 @@ #[macro_use] extern crate grin_core as core; - extern crate grin_keychain as keychain; extern crate grin_store as store; extern crate grin_util as util; @@ -39,10 +38,12 @@ extern crate serde_derive; #[macro_use] extern crate log; +pub mod api_impl; mod error; -pub mod internal; +mod internal; pub mod slate; pub mod slate_versions; pub mod types; pub use crate::error::{Error, ErrorKind}; +pub use internal::restore::{check_repair, restore}; diff --git a/libwallet/src/slate.rs b/libwallet/src/slate.rs index 289fc1ee..b3e9320b 100644 --- a/libwallet/src/slate.rs +++ b/libwallet/src/slate.rs @@ -22,16 +22,16 @@ use crate::util::secp; use crate::util::secp::key::{PublicKey, SecretKey}; use crate::util::secp::Signature; use crate::util::RwLock; +use failure::ResultExt; use grin_core::core::amount_to_hr_string; use grin_core::core::committed::Committed; use grin_core::core::transaction::{kernel_features, kernel_sig_msg, Transaction, Weighting}; use grin_core::core::verifier_cache::LruVerifierCache; use grin_core::libtx::{aggsig, build, secp_ser, tx_fee}; use rand::thread_rng; +use serde_json; use std::sync::Arc; use uuid::Uuid; -use failure::ResultExt; -use serde_json; use crate::slate_versions::v0::SlateV0; use crate::slate_versions::v1::SlateV1; @@ -159,15 +159,15 @@ impl Slate { fn parse_slate_version(slate_json: &str) -> Result { // keep attempting to deser, working through known versions until we have // enough to get the version out - let res : Result = serde_json::from_str(slate_json); + let res: Result = serde_json::from_str(slate_json); if let Ok(s) = res { return Ok(s.version_info.version); } - let res : Result = serde_json::from_str(slate_json); + let res: Result = serde_json::from_str(slate_json); if let Ok(s) = res { return Ok(s.version as u16); } - let res : Result = serde_json::from_str(slate_json); + let res: Result = serde_json::from_str(slate_json); if let Ok(_) = res { return Ok(0); } @@ -180,12 +180,14 @@ impl Slate { let v2 = match version { 2 => serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?, 1 => { - let mut v1 : SlateV1 = serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?; + let mut v1: SlateV1 = + serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?; v1.orig_version = 1; SlateV2::from(v1) } 0 => { - let v0 : SlateV0 = serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?; + let v0: SlateV0 = + serde_json::from_str(slate_json).context(ErrorKind::SlateDeser)?; let v1 = SlateV1::from(v0); SlateV2::from(v1) } @@ -205,12 +207,12 @@ impl Slate { match version { 2 => Ok(ser_self.clone()), 1 => { - let v2 : SlateV2 = serde_json::from_str(&ser_self).context(ErrorKind::SlateDeser)?; + let v2: SlateV2 = serde_json::from_str(&ser_self).context(ErrorKind::SlateDeser)?; let v1 = SlateV1::from(v2); Ok(serde_json::to_string(&v1).context(ErrorKind::SlateDeser)?) } 0 => { - let v2 : SlateV2 = serde_json::from_str(&ser_self).context(ErrorKind::SlateDeser)?; + let v2: SlateV2 = serde_json::from_str(&ser_self).context(ErrorKind::SlateDeser)?; let v1 = SlateV1::from(v2); let v0 = SlateV0::from(v1); Ok(serde_json::to_string(&v0).context(ErrorKind::SlateDeser)?) @@ -487,7 +489,8 @@ impl Slate { } /// Verifies any messages in the slate's participant data match their signatures - pub fn verify_messages(&self, secp: &secp::Secp256k1) -> Result<(), Error> { + pub fn verify_messages(&self) -> Result<(), Error> { + let secp = secp::Secp256k1::with_caps(secp::ContextFlag::VerifyOnly); for p in self.participant_data.iter() { if let Some(msg) = &p.message { let hashed = blake2b(secp::constants::MESSAGE_SIZE, &[], &msg.as_bytes()[..]); @@ -503,7 +506,7 @@ impl Slate { Some(s) => s, }; if !aggsig::verify_single( - secp, + &secp, &signature, &m, None, diff --git a/libwallet/src/slate_versions/v0.rs b/libwallet/src/slate_versions/v0.rs index e622ef48..f66b579f 100644 --- a/libwallet/src/slate_versions/v0.rs +++ b/libwallet/src/slate_versions/v0.rs @@ -14,9 +14,7 @@ //! Contains V0 of the slate (grin 1.0.0) //! And methods to downgrade v1 to v0 -use crate::core::core::transaction::{ - KernelFeatures, OutputFeatures, -}; +use crate::core::core::transaction::{KernelFeatures, OutputFeatures}; use crate::keychain::BlindingFactor; use crate::util::secp; use crate::util::secp::key::PublicKey; @@ -24,7 +22,6 @@ use crate::util::secp::pedersen::{Commitment, RangeProof}; use crate::util::secp::Signature; use uuid::Uuid; - #[derive(Serialize, Deserialize, Debug, Clone)] pub struct SlateV0 { /// The number of participants intended to take part in this transaction diff --git a/libwallet/src/slate_versions/v1.rs b/libwallet/src/slate_versions/v1.rs index 206d0e76..2e5144a1 100644 --- a/libwallet/src/slate_versions/v1.rs +++ b/libwallet/src/slate_versions/v1.rs @@ -12,14 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. - //! Contains V1 of the slate (grin 1.0.1, 1.0.2) //! Changes from V0: //! * Addition of a version field to Slate struct -use crate::core::core::transaction::{ - KernelFeatures, OutputFeatures, -}; +use crate::core::core::transaction::{KernelFeatures, OutputFeatures}; use crate::keychain::BlindingFactor; use crate::util::secp; use crate::util::secp::key::PublicKey; @@ -27,7 +24,9 @@ use crate::util::secp::pedersen::{Commitment, RangeProof}; use crate::util::secp::Signature; use uuid::Uuid; -use crate::slate_versions::v0::{SlateV0, TransactionV0, TransactionBodyV0, InputV0, OutputV0,TxKernelV0, ParticipantDataV0}; +use crate::slate_versions::v0::{ + InputV0, OutputV0, ParticipantDataV0, SlateV0, TransactionBodyV0, TransactionV0, TxKernelV0, +}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct SlateV1 { @@ -191,10 +190,7 @@ impl From for TransactionV0 { fn from(tx: TransactionV1) -> TransactionV0 { let TransactionV1 { offset, body } = tx; let body = TransactionBodyV0::from(&body); - TransactionV0 { - offset, - body, - } + TransactionV0 { offset, body } } } @@ -320,10 +316,7 @@ impl From for TransactionV1 { fn from(tx: TransactionV0) -> TransactionV1 { let TransactionV0 { offset, body } = tx; let body = TransactionBodyV1::from(&body); - TransactionV1 { - offset, - body, - } + TransactionV1 { offset, body } } } @@ -386,6 +379,3 @@ impl From<&TxKernelV0> for TxKernelV1 { } } } - - - diff --git a/libwallet/src/slate_versions/v2.rs b/libwallet/src/slate_versions/v2.rs index 2750095b..1425378a 100644 --- a/libwallet/src/slate_versions/v2.rs +++ b/libwallet/src/slate_versions/v2.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. - //! Contains V2 of the slate (grin-wallet 1.1.0) //! Changes from V1: //! * ParticipantData struct fields serialized as hex strings instead of arrays: @@ -36,19 +35,19 @@ //! orig_verion: u16, //! min_compat_version: u16 -use crate::core::core::transaction::{ - KernelFeatures, OutputFeatures, -}; +use crate::core::core::transaction::{KernelFeatures, OutputFeatures}; +use crate::core::libtx::secp_ser; use crate::keychain::BlindingFactor; use crate::util::secp; use crate::util::secp::key::PublicKey; use crate::util::secp::pedersen::{Commitment, RangeProof}; use crate::util::secp::Signature; -use crate::core::libtx::secp_ser; use uuid::Uuid; -use crate::slate_versions::v1::{SlateV1, TransactionV1, TransactionBodyV1, InputV1, OutputV1,TxKernelV1, ParticipantDataV1}; +use crate::slate_versions::v1::{ + InputV1, OutputV1, ParticipantDataV1, SlateV1, TransactionBodyV1, TransactionV1, TxKernelV1, +}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct SlateV2 { @@ -201,7 +200,7 @@ impl From for SlateV1 { let tx = TransactionV1::from(tx); let version = 1; let orig_version = version_info.orig_version as u64; - let participant_data = map_vec!(participant_data, |data| ParticipantDataV1::from(data)); + let participant_data = map_vec!(participant_data, |data| ParticipantDataV1::from(data)); SlateV1 { num_participants, id, @@ -250,10 +249,7 @@ impl From for TransactionV1 { let body = TransactionBodyV1::from(&body); /*let transaction = TransactionV2::new(body.inputs, body.outputs, body.kernels); transaction.with_offset(offset)*/ - TransactionV1 { - offset, - body, - } + TransactionV1 { offset, body } } } @@ -390,10 +386,7 @@ impl From for TransactionV2 { let body = TransactionBodyV2::from(&body); /*let transaction = TransactionV2::new(body.inputs, body.outputs, body.kernels); transaction.with_offset(offset)*/ - TransactionV2 { - offset, - body, - } + TransactionV2 { offset, body } } } @@ -456,5 +449,3 @@ impl From<&TxKernelV1> for TxKernelV2 { } } } - - diff --git a/src/bin/cmd/wallet.rs b/src/bin/cmd/wallet.rs index 14608ed0..19cd3a0e 100644 --- a/src/bin/cmd/wallet.rs +++ b/src/bin/cmd/wallet.rs @@ -16,7 +16,7 @@ use crate::cmd::wallet_args; use crate::config::GlobalWalletConfig; use clap::ArgMatches; use grin_wallet_config::WalletConfig; -use grin_wallet_refwallet::{self, HTTPNodeClient, WalletSeed}; +use grin_wallet_impls::{HTTPNodeClient, WalletSeed, SEED_FILE}; use std::path::PathBuf; use std::thread; use std::time::Duration; @@ -31,7 +31,7 @@ pub fn _init_wallet_seed(wallet_config: WalletConfig, password: &str) { pub fn seed_exists(wallet_config: WalletConfig) -> bool { let mut data_file_dir = PathBuf::new(); data_file_dir.push(wallet_config.data_file_dir); - data_file_dir.push(grin_wallet_refwallet::SEED_FILE); + data_file_dir.push(SEED_FILE); if data_file_dir.exists() { true } else { diff --git a/src/bin/cmd/wallet_args.rs b/src/bin/cmd/wallet_args.rs index 7f6543fc..51dbf516 100644 --- a/src/bin/cmd/wallet_args.rs +++ b/src/bin/cmd/wallet_args.rs @@ -21,8 +21,10 @@ use failure::Fail; use grin_core as core; use grin_keychain as keychain; use grin_wallet_config::WalletConfig; -use grin_wallet_refwallet::{command, instantiate_wallet, NodeClient, WalletInst, WalletSeed}; -use grin_wallet_refwallet::{Error, ErrorKind}; +use grin_wallet_controller::command; +use grin_wallet_controller::{Error, ErrorKind}; +use grin_wallet_impls::{instantiate_wallet, WalletSeed}; +use grin_wallet_libwallet::types::{NodeClient, WalletInst}; use linefeed::terminal::Signal; use linefeed::{Interface, ReadResult}; use rpassword; @@ -152,7 +154,7 @@ pub fn inst_wallet( Err(e) => { let msg = { match e.kind() { - ErrorKind::Encryption => { + grin_wallet_impls::ErrorKind::Encryption => { format!("Error decrypting wallet seed (check provided password)") } _ => format!("Error instantiating wallet: {}", e), diff --git a/src/bin/cmd/wallet_tests.rs b/src/bin/cmd/wallet_tests.rs index 89436ebf..8e7d36ed 100644 --- a/src/bin/cmd/wallet_tests.rs +++ b/src/bin/cmd/wallet_tests.rs @@ -16,9 +16,8 @@ mod wallet_tests { use clap; use grin_util as util; - use grin_wallet_refwallet; - use grin_wallet_refwallet::test_framework::{self, LocalWalletClient, WalletProxy}; + use grin_wallet_impls::test_framework::{self, LocalWalletClient, WalletProxy}; use clap::{App, ArgMatches}; use grin_util::Mutex; @@ -31,7 +30,8 @@ mod wallet_tests { use grin_core::global::ChainTypes; use grin_keychain::ExtKeychain; use grin_wallet_config::{GlobalWalletConfig, WalletConfig}; - use grin_wallet_refwallet::{LMDBBackend, WalletBackend, WalletInst, WalletSeed}; + use grin_wallet_impls::{LMDBBackend, WalletSeed}; + use grin_wallet_libwallet::types::{WalletBackend, WalletInst}; use super::super::wallet_args; @@ -49,7 +49,7 @@ mod wallet_tests { pub fn config_command_wallet( dir_name: &str, wallet_name: &str, - ) -> Result<(), grin_wallet_refwallet::Error> { + ) -> Result<(), grin_wallet_controller::Error> { let mut current_dir; let mut default_config = GlobalWalletConfig::default(); current_dir = env::current_dir().unwrap_or_else(|e| { @@ -61,7 +61,7 @@ mod wallet_tests { let mut config_file_name = current_dir.clone(); config_file_name.push("grin-wallet.toml"); if config_file_name.exists() { - return Err(grin_wallet_refwallet::ErrorKind::ArgumentError( + return Err(grin_wallet_controller::ErrorKind::ArgumentError( "grin-wallet.toml already exists in the target directory. Please remove it first" .to_owned(), ))?; @@ -122,7 +122,7 @@ mod wallet_tests { node_client: LocalWalletClient, passphrase: &str, account: &str, - ) -> Result>>, grin_wallet_refwallet::Error> + ) -> Result>>, grin_wallet_controller::Error> { wallet_config.chain_type = None; // First test decryption, so we can abort early if we have the wrong password @@ -139,7 +139,7 @@ mod wallet_tests { wallet_name: &str, client: &LocalWalletClient, arg_vec: Vec<&str>, - ) -> Result { + ) -> Result { let args = app.clone().get_matches_from(arg_vec); let _ = get_wallet_subcommand(test_dir, wallet_name, args.clone()); let mut config = initial_setup_wallet(test_dir, wallet_name); @@ -149,7 +149,7 @@ mod wallet_tests { } /// command line tests - fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_refwallet::Error> { + fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::Error> { setup(test_dir); // Create a new proxy to simulate server and wallet responses let mut wallet_proxy: WalletProxy = @@ -239,7 +239,7 @@ mod wallet_tests { // Mine a bit into wallet 1 so we have something to send // (TODO: Be able to stop listeners so we can test this better) let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?; - grin_wallet_refwallet::controller::owner_single_use(wallet1.clone(), |api| { + grin_wallet_controller::controller::owner_single_use(wallet1.clone(), |api| { api.set_active_account("mining")?; Ok(()) })?; @@ -312,7 +312,7 @@ mod wallet_tests { let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?; // Check our transaction log, should have 10 entries - grin_wallet_refwallet::controller::owner_single_use(wallet1.clone(), |api| { + grin_wallet_controller::controller::owner_single_use(wallet1.clone(), |api| { api.set_active_account("mining")?; let (refreshed, txs) = api.retrieve_txs(true, None, None)?; assert!(refreshed); @@ -332,7 +332,7 @@ mod wallet_tests { // check results in wallet 2 let wallet2 = instantiate_wallet(config2.clone(), client2.clone(), "password", "default")?; - grin_wallet_refwallet::controller::owner_single_use(wallet2.clone(), |api| { + grin_wallet_controller::controller::owner_single_use(wallet2.clone(), |api| { api.set_active_account("account_1")?; let (_, wallet1_info) = api.retrieve_summary_info(true, 1)?; assert_eq!(wallet1_info.last_confirmed_height, bh); @@ -388,7 +388,7 @@ mod wallet_tests { // Check our transaction log, should have bh entries + one for the self receive let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?; - grin_wallet_refwallet::controller::owner_single_use(wallet1.clone(), |api| { + grin_wallet_controller::controller::owner_single_use(wallet1.clone(), |api| { api.set_active_account("mining")?; let (refreshed, txs) = api.retrieve_txs(true, None, None)?; assert!(refreshed); @@ -422,7 +422,7 @@ mod wallet_tests { // Check our transaction log, should have bh entries + 2 for the self receives let wallet1 = instantiate_wallet(config1.clone(), client1.clone(), "password", "default")?; - grin_wallet_refwallet::controller::owner_single_use(wallet1.clone(), |api| { + grin_wallet_controller::controller::owner_single_use(wallet1.clone(), |api| { api.set_active_account("mining")?; let (refreshed, txs) = api.retrieve_txs(true, None, None)?; assert!(refreshed);