Merge milestone/2.1.0 into master (#199)

* version bump for next potential release

* Merge master into milestone/2.1.0 (#182)

* Derive --version output dynamically from cargo package version (#174)

* add --txid to the `wallet txs` command (#176)

* add --txid to the `wallet txs` command

* add test for `wallet txs` command with `--txid` parameter

* Refactor - Split WalletCommAdapter into multiple traits (#180)

* Derive --version output dynamically from cargo package version (#174)

* add server auth argument to http client

* Revert "add server auth argument to http client"

This reverts commit f52a8d2c7c.

* modify WalletCommAdapter, moving dest argument into fields on implementors,
visiting havok on automated tests, at least one of which is now out of date and failing

* Split WalletCommAdapter into four traits, one for each of its intended behaviors.

* Remove two vestigals
1. args, a stringly typed argument to put_tx
2. NullAdapter, which is no longer used

* Remove unused "params" argument from listen method.

* Re-add previously existing TODO comment

* Fix non-test build

* completely Fix non-test build

* Full Lifecycle API Support (#184)

* refactoring wallet lib traits

* rustfmt

* rustfmt

* add new files

* explicit lifetime specifiers on all wallet traits

* rustfmt

* modify apis to use new walletinst

* rustfmt

* converting controller crate

* rustfmt

* controller crate compiling

* rustfmt

* compilation

* rustfmt

* Remove config from wallet, implement open_wallet, close_wallet in lifecycle provider, remove password + open_with_credentials from WalletBackend + impl

* rustfmt

* full compilation, changing recovery + init to new model

* rustfmt

* wallet initialisation working, init command output and flow identical to v2.0.0 wallet

* rustfmt

* fix listener and owner api startup

* rustfmt

* rustfmt

* move encryption test

* rustfmt

* fix api doctests

* rustfmt

* fix for most tests in controller crate

* rustfmt

* fix for check tests in controller crate

* fix main wallet tests

* rustfmt

* add explicit functions to handle mnemonic recovery, fix CLI recovery workflow

* rustfmt

* update keybase adapter to use new wallet format

* rustfmt

* test fix

* remove debug output
This commit is contained in:
Yeastplume 2019-07-29 13:25:03 +01:00 committed by GitHub
parent 7b6d597d39
commit c40e8a915b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 2405 additions and 1942 deletions

57
Cargo.lock generated
View file

@ -756,19 +756,19 @@ dependencies = [
[[package]]
name = "grin_wallet"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
dependencies = [
"built 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_wallet_api 2.0.1-beta.1",
"grin_wallet_config 2.0.1-beta.1",
"grin_wallet_controller 2.0.1-beta.1",
"grin_wallet_impls 2.0.1-beta.1",
"grin_wallet_libwallet 2.0.1-beta.1",
"grin_wallet_util 2.0.1-beta.1",
"grin_wallet_api 2.1.0-beta.1",
"grin_wallet_config 2.1.0-beta.1",
"grin_wallet_controller 2.1.0-beta.1",
"grin_wallet_impls 2.1.0-beta.1",
"grin_wallet_libwallet 2.1.0-beta.1",
"grin_wallet_util 2.1.0-beta.1",
"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)",
@ -778,16 +778,16 @@ dependencies = [
[[package]]
name = "grin_wallet_api"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
dependencies = [
"chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"easy-jsonrpc 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_wallet_config 2.0.1-beta.1",
"grin_wallet_impls 2.0.1-beta.1",
"grin_wallet_libwallet 2.0.1-beta.1",
"grin_wallet_util 2.0.1-beta.1",
"grin_wallet_config 2.1.0-beta.1",
"grin_wallet_impls 2.1.0-beta.1",
"grin_wallet_libwallet 2.1.0-beta.1",
"grin_wallet_util 2.1.0-beta.1",
"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.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -796,10 +796,10 @@ dependencies = [
[[package]]
name = "grin_wallet_config"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
dependencies = [
"dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_wallet_util 2.0.1-beta.1",
"grin_wallet_util 2.1.0-beta.1",
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.93 (registry+https://github.com/rust-lang/crates.io-index)",
@ -809,18 +809,18 @@ dependencies = [
[[package]]
name = "grin_wallet_controller"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
dependencies = [
"chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"easy-jsonrpc 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_wallet_api 2.0.1-beta.1",
"grin_wallet_config 2.0.1-beta.1",
"grin_wallet_impls 2.0.1-beta.1",
"grin_wallet_libwallet 2.0.1-beta.1",
"grin_wallet_util 2.0.1-beta.1",
"grin_wallet_api 2.1.0-beta.1",
"grin_wallet_config 2.1.0-beta.1",
"grin_wallet_impls 2.1.0-beta.1",
"grin_wallet_libwallet 2.1.0-beta.1",
"grin_wallet_util 2.1.0-beta.1",
"hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -840,16 +840,16 @@ dependencies = [
[[package]]
name = "grin_wallet_impls"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
dependencies = [
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.7 (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.28 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_wallet_config 2.0.1-beta.1",
"grin_wallet_libwallet 2.0.1-beta.1",
"grin_wallet_util 2.0.1-beta.1",
"grin_wallet_config 2.1.0-beta.1",
"grin_wallet_libwallet 2.1.0-beta.1",
"grin_wallet_util 2.1.0-beta.1",
"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)",
@ -859,19 +859,20 @@ dependencies = [
"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.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "grin_wallet_libwallet"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
dependencies = [
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_wallet_config 2.0.1-beta.1",
"grin_wallet_util 2.0.1-beta.1",
"grin_wallet_config 2.1.0-beta.1",
"grin_wallet_util 2.1.0-beta.1",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"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)",
@ -885,7 +886,7 @@ dependencies = [
[[package]]
name = "grin_wallet_util"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
dependencies = [
"dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_api 2.0.1-beta.1 (git+https://github.com/mimblewimble/grin)",

View file

@ -1,6 +1,6 @@
[package]
name = "grin_wallet"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -30,13 +30,13 @@ log = "0.4"
linefeed = "0.5"
semver = "0.9"
grin_wallet_api = { path = "./api", version = "2.0.1-beta.1" }
grin_wallet_impls = { path = "./impls", version = "2.0.1-beta.1" }
grin_wallet_libwallet = { path = "./libwallet", version = "2.0.1-beta.1" }
grin_wallet_controller = { path = "./controller", version = "2.0.1-beta.1" }
grin_wallet_config = { path = "./config", version = "2.0.1-beta.1" }
grin_wallet_api = { path = "./api", version = "2.1.0-beta.1" }
grin_wallet_impls = { path = "./impls", version = "2.1.0-beta.1" }
grin_wallet_libwallet = { path = "./libwallet", version = "2.1.0-beta.1" }
grin_wallet_controller = { path = "./controller", version = "2.1.0-beta.1" }
grin_wallet_config = { path = "./config", version = "2.1.0-beta.1" }
grin_wallet_util = { path = "./util", version = "2.0.1-beta.1" }
grin_wallet_util = { path = "./util", version = "2.1.0-beta.1" }
[build-dependencies]
built = "0.3"

View file

@ -1,6 +1,6 @@
[package]
name = "grin_wallet_api"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Grin Wallet API"
license = "Apache-2.0"
@ -18,10 +18,10 @@ serde_json = "1"
easy-jsonrpc = "0.5.1"
chrono = { version = "0.4.4", features = ["serde"] }
grin_wallet_libwallet = { path = "../libwallet", version = "2.0.1-beta.1" }
grin_wallet_config = { path = "../config", version = "2.0.1-beta.1" }
grin_wallet_impls = { path = "../impls", version = "2.0.1-beta.1" }
grin_wallet_util = { path = "../util", version = "2.0.1-beta.1" }
grin_wallet_libwallet = { path = "../libwallet", version = "2.1.0-beta.1" }
grin_wallet_config = { path = "../config", version = "2.1.0-beta.1" }
grin_wallet_impls = { path = "../impls", version = "2.1.0-beta.1" }
grin_wallet_util = { path = "../util", version = "2.1.0-beta.1" }
[dev-dependencies]
serde_json = "1"

View file

@ -17,10 +17,10 @@
use crate::keychain::Keychain;
use crate::libwallet::api_impl::foreign;
use crate::libwallet::{
BlockFees, CbData, Error, NodeClient, NodeVersionInfo, Slate, VersionInfo, WalletBackend,
BlockFees, CbData, Error, NodeClient, NodeVersionInfo, Slate, VersionInfo, WalletInst,
WalletLCProvider,
};
use crate::util::Mutex;
use std::marker::PhantomData;
use std::sync::Arc;
/// ForeignAPI Middleware Check callback
@ -55,30 +55,25 @@ pub enum ForeignCheckMiddlewareFn {
/// its operation, then 'close' the wallet (unloading references to the keychain and master
/// seed).
pub struct Foreign<W: ?Sized, C, K>
pub struct Foreign<'a, L, C, K>
where
W: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
/// Wallet, contains its keychain (TODO: Split these up into 2 traits
/// perhaps)
pub wallet: Arc<Mutex<W>>,
/// Wallet instance
pub wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
/// Flag to normalize some output during testing. Can mostly be ignored.
pub doctest_mode: bool,
/// phantom
phantom: PhantomData<K>,
/// phantom
phantom_c: PhantomData<C>,
/// foreign check middleware
middleware: Option<ForeignCheckMiddleware>,
}
impl<'a, W: ?Sized, C, K> Foreign<W, C, K>
impl<'a, L, C, K> Foreign<'a, L, C, K>
where
W: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
/// Create a new API instance with the given wallet instance. All subsequent
/// API calls will operate on this instance of the wallet.
@ -110,12 +105,12 @@ where
/// use tempfile::tempdir;
///
/// use std::sync::Arc;
/// use util::Mutex;
/// use util::{Mutex, ZeroingString};
///
/// use api::Foreign;
/// use config::WalletConfig;
/// use impls::{HTTPNodeClient, LMDBBackend};
/// use libwallet::WalletBackend;
/// use impls::{DefaultWalletImpl, DefaultLCProvider, HTTPNodeClient};
/// use libwallet::WalletInst;
///
/// let mut wallet_config = WalletConfig::default();
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
@ -128,24 +123,42 @@ where
///
/// // A NodeClient must first be created to handle communication between
/// // the wallet and the node.
///
/// let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// Arc::new(Mutex::new(
/// LMDBBackend::new(wallet_config.clone(), "", node_client).unwrap()
/// ));
///
/// // impls::DefaultWalletImpl is provided for convenience in instantiating the wallet
/// // It contains the LMDBBackend, DefaultLCProvider (lifecycle) and ExtKeychain used
/// // by the reference wallet implementation.
/// // These traits can be replaced with alternative implementations if desired
///
/// let mut wallet = Box::new(DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap())
/// as Box<WalletInst<'static, DefaultLCProvider<HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>>;
///
/// // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc...
/// let lc = wallet.lc_provider().unwrap();
///
/// // The top level wallet directory should be set manually (in the reference implementation,
/// // this is provided in the WalletConfig)
/// lc.set_wallet_directory(&wallet_config.data_file_dir);
///
/// // Wallet must be opened with the password (TBD)
/// let pw = ZeroingString::from("wallet_password");
/// lc.open_wallet(None, pw);
///
/// // All wallet functions operate on an Arc::Mutex to allow multithreading where needed
/// let mut wallet = Arc::new(Mutex::new(wallet));
///
/// let api_foreign = Foreign::new(wallet.clone(), None);
/// // .. perform wallet operations
///
/// ```
pub fn new(wallet_in: Arc<Mutex<W>>, middleware: Option<ForeignCheckMiddleware>) -> Self {
pub fn new(
wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
middleware: Option<ForeignCheckMiddleware>,
) -> Self {
Foreign {
wallet: wallet_in,
wallet_inst,
doctest_mode: false,
phantom: PhantomData,
phantom_c: PhantomData,
middleware,
}
}
@ -168,7 +181,8 @@ where
pub fn check_version(&self) -> Result<VersionInfo, Error> {
if let Some(m) = self.middleware.as_ref() {
let mut w = self.wallet.lock();
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
m(
ForeignCheckMiddlewareFn::CheckVersion,
w.w2n_client().get_version_info(),
@ -229,7 +243,8 @@ where
/// ```
pub fn build_coinbase(&self, block_fees: &BlockFees) -> Result<CbData, Error> {
let mut w = self.wallet.lock();
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
if let Some(m) = self.middleware.as_ref() {
m(
ForeignCheckMiddlewareFn::BuildCoinbase,
@ -237,10 +252,7 @@ where
None,
)?;
}
w.open_with_credentials()?;
let res = foreign::build_coinbase(&mut *w, block_fees, self.doctest_mode);
w.close()?;
res
foreign::build_coinbase(&mut **w, block_fees, self.doctest_mode)
}
/// Verifies all messages in the slate match their public keys.
@ -283,7 +295,8 @@ where
pub fn verify_slate_messages(&self, slate: &Slate) -> Result<(), Error> {
if let Some(m) = self.middleware.as_ref() {
let mut w = self.wallet.lock();
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
m(
ForeignCheckMiddlewareFn::VerifySlateMessages,
w.w2n_client().get_version_info(),
@ -355,7 +368,8 @@ where
dest_acct_name: Option<&str>,
message: Option<String>,
) -> Result<Slate, Error> {
let mut w = self.wallet.lock();
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
if let Some(m) = self.middleware.as_ref() {
m(
ForeignCheckMiddlewareFn::ReceiveTx,
@ -363,10 +377,7 @@ where
Some(slate),
)?;
}
w.open_with_credentials()?;
let res = foreign::receive_tx(&mut *w, slate, dest_acct_name, message, self.doctest_mode);
w.close()?;
res
foreign::receive_tx(&mut **w, slate, dest_acct_name, message, self.doctest_mode)
}
/// Finalizes an invoice transaction initiated by this wallet's Owner api.
@ -417,7 +428,8 @@ where
/// ```
pub fn finalize_invoice_tx(&self, slate: &Slate) -> Result<Slate, Error> {
let mut w = self.wallet.lock();
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
if let Some(m) = self.middleware.as_ref() {
m(
ForeignCheckMiddlewareFn::FinalizeInvoiceTx,
@ -425,10 +437,7 @@ where
Some(slate),
)?;
}
w.open_with_credentials()?;
let res = foreign::finalize_invoice_tx(&mut *w, slate);
w.close()?;
res
foreign::finalize_invoice_tx(&mut **w, slate)
}
}
@ -447,12 +456,12 @@ macro_rules! doctest_helper_setup_doc_env_foreign {
use tempfile::tempdir;
use std::sync::Arc;
use util::Mutex;
use util::{Mutex, ZeroingString};
use api::{Foreign, Owner};
use config::WalletConfig;
use impls::{HTTPNodeClient, LMDBBackend, WalletSeed};
use libwallet::{BlockFees, IssueInvoiceTxArgs, Slate, WalletBackend};
use impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient};
use libwallet::{BlockFees, IssueInvoiceTxArgs, Slate, WalletInst};
let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
let dir = dir
@ -462,11 +471,23 @@ macro_rules! doctest_helper_setup_doc_env_foreign {
.unwrap();
let mut wallet_config = WalletConfig::default();
wallet_config.data_file_dir = dir.to_owned();
let pw = "";
let pw = ZeroingString::from("");
let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
let mut $wallet: Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> = Arc::new(
Mutex::new(LMDBBackend::new(wallet_config.clone(), pw, node_client).unwrap()),
);
let mut wallet = Box::new(
DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap(),
)
as Box<
WalletInst<
'static,
DefaultLCProvider<HTTPNodeClient, ExtKeychain>,
HTTPNodeClient,
ExtKeychain,
>,
>;
let lc = wallet.lc_provider().unwrap();
lc.set_wallet_directory(&wallet_config.data_file_dir);
lc.open_wallet(None, pw);
let mut $wallet = Arc::new(Mutex::new(wallet));
};
}

View file

@ -17,7 +17,7 @@
use crate::keychain::Keychain;
use crate::libwallet::{
self, BlockFees, CbData, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient,
NodeVersionInfo, Slate, VersionInfo, VersionedSlate, WalletBackend,
NodeVersionInfo, Slate, VersionInfo, VersionedSlate, WalletLCProvider,
};
use crate::{Foreign, ForeignCheckMiddlewareFn};
use easy_jsonrpc;
@ -515,11 +515,11 @@ pub trait ForeignRpc {
fn finalize_invoice_tx(&self, slate: &Slate) -> Result<Slate, ErrorKind>;
}
impl<W: ?Sized, C, K> ForeignRpc for Foreign<W, C, K>
impl<'a, L, C, K> ForeignRpc for Foreign<'a, L, C, K>
where
W: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
fn check_version(&self) -> Result<VersionInfo, ErrorKind> {
Foreign::check_version(self).map_err(|e| e.kind())
@ -577,13 +577,17 @@ pub fn run_doctest_foreign(
) -> Result<Option<serde_json::Value>, String> {
use easy_jsonrpc::Handler;
use grin_wallet_impls::test_framework::{self, LocalWalletClient, WalletProxy};
use grin_wallet_libwallet::api_impl;
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl};
use grin_wallet_libwallet::{api_impl, WalletInst};
use grin_wallet_util::grin_keychain::ExtKeychain;
use crate::core::global;
use crate::core::global::ChainTypes;
use grin_wallet_util::grin_util as util;
use std::sync::Arc;
use util::Mutex;
use std::fs;
use std::thread;
@ -591,29 +595,60 @@ pub fn run_doctest_foreign(
let _ = fs::remove_dir_all(test_dir);
global::set_mining_mode(ChainTypes::AutomatedTesting);
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
let mut wallet_proxy: WalletProxy<
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
> = WalletProxy::new(test_dir);
let chain = wallet_proxy.chain.clone();
let rec_phrase_1 =
let rec_phrase_1 = util::ZeroingString::from(
"fat twenty mean degree forget shell check candy immense awful \
flame next during february bulb bike sun wink theory day kiwi embrace peace lunch";
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let wallet1 = test_framework::create_wallet(
&format!("{}/wallet1", test_dir),
client1.clone(),
Some(rec_phrase_1),
flame next during february bulb bike sun wink theory day kiwi embrace peace lunch",
);
let empty_string = util::ZeroingString::from("");
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let mut wallet1 =
Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client1.clone()).unwrap())
as Box<
WalletInst<
'static,
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>;
let lc = wallet1.lc_provider().unwrap();
lc.set_wallet_directory(&format!("{}/wallet1", test_dir));
lc.create_wallet(None, Some(rec_phrase_1), 32, empty_string.clone())
.unwrap();
lc.open_wallet(None, empty_string.clone()).unwrap();
let wallet1 = Arc::new(Mutex::new(wallet1));
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
let rec_phrase_2 =
let rec_phrase_2 = util::ZeroingString::from(
"hour kingdom ripple lunch razor inquiry coyote clay stamp mean \
sell finish magic kid tiny wage stand panther inside settle feed song hole exile";
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
let wallet2 = test_framework::create_wallet(
&format!("{}/wallet2", test_dir),
client2.clone(),
Some(rec_phrase_2),
sell finish magic kid tiny wage stand panther inside settle feed song hole exile",
);
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
let mut wallet2 =
Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client2.clone()).unwrap())
as Box<
WalletInst<
'static,
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>;
let lc = wallet2.lc_provider().unwrap();
lc.set_wallet_directory(&format!("{}/wallet2", test_dir));
lc.create_wallet(None, Some(rec_phrase_2), 32, empty_string.clone())
.unwrap();
lc.open_wallet(None, empty_string.clone()).unwrap();
let wallet2 = Arc::new(Mutex::new(wallet2));
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
// Set the wallet proxy listener running
@ -627,28 +662,27 @@ pub fn run_doctest_foreign(
for _ in 0..blocks_to_mine {
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 1 as usize, false);
//update local outputs after each block, so transaction IDs stay consistent
let mut w = wallet1.lock();
w.open_with_credentials().unwrap();
let mut w_lock = wallet1.lock();
let w = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
let (wallet_refreshed, _) =
api_impl::owner::retrieve_summary_info(&mut *w, true, 1).unwrap();
api_impl::owner::retrieve_summary_info(&mut **w, true, 1).unwrap();
assert!(wallet_refreshed);
w.close().unwrap();
}
if init_invoice_tx {
let amount = 60_000_000_000;
let mut slate = {
let mut w = wallet2.lock();
w.open_with_credentials().unwrap();
let mut w_lock = wallet2.lock();
let w = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
let args = IssueInvoiceTxArgs {
amount,
..Default::default()
};
api_impl::owner::issue_invoice_tx(&mut *w, args, true).unwrap()
api_impl::owner::issue_invoice_tx(&mut **w, args, true).unwrap()
};
slate = {
let mut w = wallet1.lock();
w.open_with_credentials().unwrap();
let mut w_lock = wallet1.lock();
let w = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
let args = InitTxArgs {
src_acct_name: None,
amount: slate.amount,
@ -658,7 +692,7 @@ pub fn run_doctest_foreign(
selection_strategy_is_use_all: true,
..Default::default()
};
api_impl::owner::process_invoice_tx(&mut *w, &slate, args, true).unwrap()
api_impl::owner::process_invoice_tx(&mut **w, &slate, args, true).unwrap()
};
println!("INIT INVOICE SLATE");
// Spit out slate for input to finalize_invoice_tx
@ -667,8 +701,8 @@ pub fn run_doctest_foreign(
if init_tx {
let amount = 60_000_000_000;
let mut w = wallet1.lock();
w.open_with_credentials().unwrap();
let mut w_lock = wallet1.lock();
let w = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
let args = InitTxArgs {
src_acct_name: None,
amount,
@ -678,15 +712,15 @@ pub fn run_doctest_foreign(
selection_strategy_is_use_all: true,
..Default::default()
};
let slate = api_impl::owner::init_send_tx(&mut *w, args, true).unwrap();
let slate = api_impl::owner::init_send_tx(&mut **w, args, true).unwrap();
println!("INIT SLATE");
// Spit out slate for input to finalize_tx
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
}
let mut api_foreign = match init_invoice_tx {
false => Foreign::new(wallet1.clone(), Some(test_check_middleware)),
true => Foreign::new(wallet2.clone(), Some(test_check_middleware)),
false => Foreign::new(wallet1, Some(test_check_middleware)),
true => Foreign::new(wallet2, Some(test_check_middleware)),
};
api_foreign.doctest_mode = true;
let foreign_api = &api_foreign as &dyn ForeignRpc;

View file

@ -36,8 +36,10 @@ extern crate log;
mod foreign;
mod foreign_rpc;
mod owner;
mod owner_rpc;
pub use crate::foreign::{Foreign, ForeignCheckMiddleware, ForeignCheckMiddlewareFn};
pub use crate::foreign_rpc::ForeignRpc;
pub use crate::owner::Owner;

View file

@ -14,20 +14,20 @@
//! Owner API External Definition
use crate::util::Mutex;
use chrono::prelude::*;
use std::marker::PhantomData;
use std::sync::Arc;
use uuid::Uuid;
use crate::core::core::Transaction;
use crate::impls::{HTTPWalletCommAdapter, KeybaseWalletCommAdapter};
use crate::impls::create_sender;
use crate::keychain::{Identifier, Keychain};
use crate::libwallet::api_impl::owner;
use crate::libwallet::{
AcctPathMapping, Error, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient,
NodeHeightResult, OutputCommitMapping, Slate, TxLogEntry, WalletBackend, WalletInfo,
NodeHeightResult, OutputCommitMapping, Slate, TxLogEntry, WalletInfo, WalletInst,
WalletLCProvider,
};
use crate::util::Mutex;
use std::sync::Arc;
/// Main interface into all wallet API functions.
/// Wallet APIs are split into two seperate blocks of functionality
@ -43,24 +43,21 @@ use crate::libwallet::{
/// its operation, then 'close' the wallet (unloading references to the keychain and master
/// seed).
pub struct Owner<W: ?Sized, C, K>
pub struct Owner<'a, L, C, K>
where
W: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
/// A reference-counted mutex to an implementation of the
/// [`WalletBackend`](../grin_wallet_libwallet/types/trait.WalletBackend.html) trait.
pub wallet: Arc<Mutex<W>>,
/// contain all methods to manage the wallet
pub wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
/// Flag to normalize some output during testing. Can mostly be ignored.
pub doctest_mode: bool,
phantom: PhantomData<K>,
phantom_c: PhantomData<C>,
}
impl<W: ?Sized, C, K> Owner<W, C, K>
impl<'a, L, C, K> Owner<'a, L, C, K>
where
W: WalletBackend<C, K>,
L: WalletLCProvider<'a, C, K>,
C: NodeClient,
K: Keychain,
{
@ -92,12 +89,12 @@ where
/// use tempfile::tempdir;
///
/// use std::sync::Arc;
/// use util::Mutex;
/// use util::{Mutex, ZeroingString};
///
/// use api::Owner;
/// use config::WalletConfig;
/// use impls::{HTTPNodeClient, LMDBBackend};
/// use libwallet::WalletBackend;
/// use impls::{DefaultWalletImpl, DefaultLCProvider, HTTPNodeClient};
/// use libwallet::WalletInst;
///
/// let mut wallet_config = WalletConfig::default();
/// # let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
@ -110,24 +107,39 @@ where
///
/// // A NodeClient must first be created to handle communication between
/// // the wallet and the node.
///
/// let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// Arc::new(Mutex::new(
/// LMDBBackend::new(wallet_config.clone(), "", node_client).unwrap()
/// ));
///
/// // impls::DefaultWalletImpl is provided for convenience in instantiating the wallet
/// // It contains the LMDBBackend, DefaultLCProvider (lifecycle) and ExtKeychain used
/// // by the reference wallet implementation.
/// // These traits can be replaced with alternative implementations if desired
///
/// let mut wallet = Box::new(DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap())
/// as Box<WalletInst<'static, DefaultLCProvider<HTTPNodeClient, ExtKeychain>, HTTPNodeClient, ExtKeychain>>;
///
/// // Wallet LifeCycle Provider provides all functions init wallet and work with seeds, etc...
/// let lc = wallet.lc_provider().unwrap();
///
/// // The top level wallet directory should be set manually (in the reference implementation,
/// // this is provided in the WalletConfig)
/// lc.set_wallet_directory(&wallet_config.data_file_dir);
///
/// // Wallet must be opened with the password (TBD)
/// let pw = ZeroingString::from("wallet_password");
/// lc.open_wallet(None, pw);
///
/// // All wallet functions operate on an Arc::Mutex to allow multithreading where needed
/// let mut wallet = Arc::new(Mutex::new(wallet));
///
/// let api_owner = Owner::new(wallet.clone());
/// // .. perform wallet operations
///
/// ```
pub fn new(wallet_in: Arc<Mutex<W>>) -> Self {
pub fn new(wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>) -> Self {
Owner {
wallet: wallet_in,
wallet_inst,
doctest_mode: false,
phantom: PhantomData,
phantom_c: PhantomData,
}
}
@ -160,8 +172,9 @@ where
/// ```
pub fn accounts(&self) -> Result<Vec<AcctPathMapping>, Error> {
let mut w = self.wallet.lock();
owner::accounts(&mut *w)
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::accounts(&mut **w)
}
/// Creates a new 'account', which is a mapping of a user-specified
@ -202,8 +215,9 @@ where
/// ```
pub fn create_account_path(&self, label: &str) -> Result<Identifier, Error> {
let mut w = self.wallet.lock();
owner::create_account_path(&mut *w, label)
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::create_account_path(&mut **w, label)
}
/// Sets the wallet's currently active account. This sets the
@ -243,8 +257,9 @@ where
/// ```
pub fn set_active_account(&self, label: &str) -> Result<(), Error> {
let mut w = self.wallet.lock();
owner::set_active_account(&mut *w, label)
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::set_active_account(&mut **w, label)
}
/// Returns a list of outputs from the active account in the wallet.
@ -295,11 +310,9 @@ where
refresh_from_node: bool,
tx_id: Option<u32>,
) -> Result<(bool, Vec<OutputCommitMapping>), Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let res = owner::retrieve_outputs(&mut *w, include_spent, refresh_from_node, tx_id);
w.close()?;
res
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::retrieve_outputs(&mut **w, include_spent, refresh_from_node, tx_id)
}
/// Returns a list of [Transaction Log Entries](../grin_wallet_libwallet/types/struct.TxLogEntry.html)
@ -348,9 +361,9 @@ where
tx_id: Option<u32>,
tx_slate_id: Option<Uuid>,
) -> Result<(bool, Vec<TxLogEntry>), Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let mut res = owner::retrieve_txs(&mut *w, refresh_from_node, tx_id, tx_slate_id)?;
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
let mut res = owner::retrieve_txs(&mut **w, refresh_from_node, tx_id, tx_slate_id)?;
if self.doctest_mode {
res.1 = res
.1
@ -362,7 +375,6 @@ where
})
.collect();
}
w.close()?;
Ok(res)
}
@ -406,11 +418,9 @@ where
refresh_from_node: bool,
minimum_confirmations: u64,
) -> Result<(bool, WalletInfo), Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let res = owner::retrieve_summary_info(&mut *w, refresh_from_node, minimum_confirmations);
w.close()?;
res
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::retrieve_summary_info(&mut **w, refresh_from_node, minimum_confirmations)
}
/// Initiates a new transaction as the sender, creating a new
@ -489,30 +499,27 @@ where
pub fn init_send_tx(&self, args: InitTxArgs) -> Result<Slate, Error> {
let send_args = args.send_args.clone();
let mut slate = {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let slate = owner::init_send_tx(&mut *w, args, self.doctest_mode)?;
w.close()?;
slate
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::init_send_tx(&mut **w, args, self.doctest_mode)?
};
// Helper functionality. If send arguments exist, attempt to send
match send_args {
Some(sa) => {
//TODO: in case of keybase, the response might take 60s and leave the service hanging
match sa.method.as_ref() {
"http" => {
slate = HTTPWalletCommAdapter::new().send_tx_sync(&sa.dest, &slate)?
}
"keybase" => {
//TODO: in case of keybase, the response might take 60s and leave the service hanging
slate = KeybaseWalletCommAdapter::new().send_tx_sync(&sa.dest, &slate)?;
}
"http" | "keybase" => {}
_ => {
error!("unsupported payment method: {}", sa.method);
return Err(ErrorKind::ClientCallback(
"unsupported payment method".to_owned(),
))?;
)
.into());
}
}
};
let comm_adapter = create_sender(&sa.method, &sa.dest)
.map_err(|e| ErrorKind::GenericError(format!("{}", e)))?;
slate = comm_adapter.send_tx(&slate)?;
self.tx_lock_outputs(&slate, 0)?;
let slate = match sa.finalize {
true => self.finalize_tx(&slate)?,
@ -562,11 +569,9 @@ where
/// }
/// ```
pub fn issue_invoice_tx(&self, args: IssueInvoiceTxArgs) -> Result<Slate, Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let slate = owner::issue_invoice_tx(&mut *w, args, self.doctest_mode)?;
w.close()?;
Ok(slate)
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::issue_invoice_tx(&mut **w, args, self.doctest_mode)
}
/// Processes an invoice tranaction created by another party, essentially
@ -623,11 +628,9 @@ where
/// ```
pub fn process_invoice_tx(&self, slate: &Slate, args: InitTxArgs) -> Result<Slate, Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let slate = owner::process_invoice_tx(&mut *w, slate, args, self.doctest_mode)?;
w.close()?;
Ok(slate)
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::process_invoice_tx(&mut **w, slate, args, self.doctest_mode)
}
/// Locks the outputs associated with the inputs to the transaction in the given
@ -683,11 +686,9 @@ where
/// ```
pub fn tx_lock_outputs(&self, slate: &Slate, participant_id: usize) -> Result<(), Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let res = owner::tx_lock_outputs(&mut *w, slate, participant_id);
w.close()?;
res
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::tx_lock_outputs(&mut **w, slate, participant_id)
}
/// Finalizes a transaction, after all parties
@ -745,12 +746,9 @@ where
/// ```
pub fn finalize_tx(&self, slate: &Slate) -> Result<Slate, Error> {
let mut w = self.wallet.lock();
let mut slate = slate.clone();
w.open_with_credentials()?;
slate = owner::finalize_tx(&mut *w, &slate)?;
w.close()?;
Ok(slate)
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::finalize_tx(&mut **w, &slate)
}
/// Posts a completed transaction to the listening node for validation and inclusion in a block
@ -804,7 +802,8 @@ where
pub fn post_tx(&self, tx: &Transaction, fluff: bool) -> Result<(), Error> {
let client = {
let mut w = self.wallet.lock();
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
w.w2n_client().clone()
};
owner::post_tx(&client, tx, fluff)
@ -863,11 +862,9 @@ where
/// ```
pub fn cancel_tx(&self, tx_id: Option<u32>, tx_slate_id: Option<Uuid>) -> Result<(), Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let res = owner::cancel_tx(&mut *w, tx_id, tx_slate_id);
w.close()?;
res
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::cancel_tx(&mut **w, tx_id, tx_slate_id)
}
/// Retrieves the stored transaction associated with a TxLogEntry. Can be used even after the
@ -903,8 +900,9 @@ where
// TODO: Should be accepting an id, not an entire entry struct
pub fn get_stored_tx(&self, tx_log_entry: &TxLogEntry) -> Result<Option<Transaction>, Error> {
let w = self.wallet.lock();
owner::get_stored_tx(&*w, tx_log_entry)
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::get_stored_tx(&**w, tx_log_entry)
}
/// Verifies all messages in the slate match their public keys.
@ -993,10 +991,9 @@ where
/// }
/// ```
pub fn restore(&self) -> Result<(), Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let res = owner::restore(&mut *w);
w.close()?;
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
let res = owner::restore(&mut **w);
res
}
@ -1047,11 +1044,9 @@ where
/// ```
pub fn check_repair(&self, delete_unconfirmed: bool) -> Result<(), Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let res = owner::check_repair(&mut *w, delete_unconfirmed);
w.close()?;
res
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::check_repair(&mut **w, delete_unconfirmed)
}
/// Retrieves the last known height known by the wallet. This is determined as follows:
@ -1092,11 +1087,9 @@ where
/// ```
pub fn node_height(&self) -> Result<NodeHeightResult, Error> {
let mut w = self.wallet.lock();
w.open_with_credentials()?;
let res = owner::node_height(&mut *w);
w.close()?;
res
let mut w_lock = self.wallet_inst.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::node_height(&mut **w)
}
}
@ -1115,12 +1108,12 @@ macro_rules! doctest_helper_setup_doc_env {
use tempfile::tempdir;
use std::sync::Arc;
use util::Mutex;
use util::{Mutex, ZeroingString};
use api::Owner;
use api::{Foreign, Owner};
use config::WalletConfig;
use impls::{HTTPNodeClient, LMDBBackend, WalletSeed};
use libwallet::{InitTxArgs, IssueInvoiceTxArgs, Slate, WalletBackend};
use impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient};
use libwallet::{BlockFees, InitTxArgs, IssueInvoiceTxArgs, Slate, WalletInst};
let dir = tempdir().map_err(|e| format!("{:#?}", e)).unwrap();
let dir = dir
@ -1130,11 +1123,23 @@ macro_rules! doctest_helper_setup_doc_env {
.unwrap();
let mut wallet_config = WalletConfig::default();
wallet_config.data_file_dir = dir.to_owned();
let pw = "";
let pw = ZeroingString::from("");
let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
let mut $wallet: Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> = Arc::new(
Mutex::new(LMDBBackend::new(wallet_config.clone(), pw, node_client).unwrap()),
);
let mut wallet = Box::new(
DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap(),
)
as Box<
WalletInst<
'static,
DefaultLCProvider<HTTPNodeClient, ExtKeychain>,
HTTPNodeClient,
ExtKeychain,
>,
>;
let lc = wallet.lc_provider().unwrap();
lc.set_wallet_directory(&wallet_config.data_file_dir);
lc.open_wallet(None, pw);
let mut $wallet = Arc::new(Mutex::new(wallet));
};
}

View file

@ -19,10 +19,12 @@ use crate::core::core::Transaction;
use crate::keychain::{Identifier, Keychain};
use crate::libwallet::{
AcctPathMapping, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient, NodeHeightResult,
OutputCommitMapping, Slate, TxLogEntry, WalletBackend, WalletInfo,
OutputCommitMapping, Slate, TxLogEntry, WalletInfo, WalletLCProvider,
};
use crate::util::Mutex;
use crate::Owner;
use easy_jsonrpc;
use std::sync::Arc;
/// Public definition used to generate Owner jsonrpc api.
/// * When running `grin-wallet owner_api` with defaults, the V2 api is available at
@ -1233,11 +1235,11 @@ pub trait OwnerRpc {
fn node_height(&self) -> Result<NodeHeightResult, ErrorKind>;
}
impl<W: ?Sized, C, K> OwnerRpc for Owner<W, C, K>
impl<'a, L, C, K> OwnerRpc for Owner<'a, L, C, K>
where
W: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
fn accounts(&self) -> Result<Vec<AcctPathMapping>, ErrorKind> {
Owner::accounts(self).map_err(|e| e.kind())
@ -1338,7 +1340,8 @@ pub fn run_doctest_owner(
) -> Result<Option<serde_json::Value>, String> {
use easy_jsonrpc::Handler;
use grin_wallet_impls::test_framework::{self, LocalWalletClient, WalletProxy};
use grin_wallet_libwallet::api_impl;
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl};
use grin_wallet_libwallet::{api_impl, WalletInst};
use grin_wallet_util::grin_keychain::ExtKeychain;
use crate::core::global;
@ -1352,29 +1355,61 @@ pub fn run_doctest_owner(
let _ = fs::remove_dir_all(test_dir);
global::set_mining_mode(ChainTypes::AutomatedTesting);
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
let mut wallet_proxy: WalletProxy<
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
> = WalletProxy::new(test_dir);
let chain = wallet_proxy.chain.clone();
let rec_phrase_1 =
let rec_phrase_1 = util::ZeroingString::from(
"fat twenty mean degree forget shell check candy immense awful \
flame next during february bulb bike sun wink theory day kiwi embrace peace lunch";
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let wallet1 = test_framework::create_wallet(
&format!("{}/wallet1", test_dir),
client1.clone(),
Some(rec_phrase_1),
flame next during february bulb bike sun wink theory day kiwi embrace peace lunch",
);
let empty_string = util::ZeroingString::from("");
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let mut wallet1 =
Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client1.clone()).unwrap())
as Box<
WalletInst<
'static,
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>;
let lc = wallet1.lc_provider().unwrap();
lc.set_wallet_directory(&format!("{}/wallet1", test_dir));
lc.create_wallet(None, Some(rec_phrase_1), 32, empty_string.clone())
.unwrap();
lc.open_wallet(None, empty_string.clone()).unwrap();
let wallet1 = Arc::new(Mutex::new(wallet1));
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
let rec_phrase_2 =
let rec_phrase_2 = util::ZeroingString::from(
"hour kingdom ripple lunch razor inquiry coyote clay stamp mean \
sell finish magic kid tiny wage stand panther inside settle feed song hole exile";
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
let wallet2 = test_framework::create_wallet(
&format!("{}/wallet2", test_dir),
client2.clone(),
Some(rec_phrase_2),
sell finish magic kid tiny wage stand panther inside settle feed song hole exile",
);
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
let mut wallet2 =
Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client2.clone()).unwrap())
as Box<
WalletInst<
'static,
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>;
let lc = wallet2.lc_provider().unwrap();
lc.set_wallet_directory(&format!("{}/wallet2", test_dir));
lc.create_wallet(None, Some(rec_phrase_2), 32, empty_string.clone())
.unwrap();
lc.open_wallet(None, empty_string.clone()).unwrap();
let wallet2 = Arc::new(Mutex::new(wallet2));
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
// Set the wallet proxy listener running
@ -1388,18 +1423,17 @@ pub fn run_doctest_owner(
for _ in 0..blocks_to_mine {
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 1 as usize, false);
//update local outputs after each block, so transaction IDs stay consistent
let mut w = wallet1.lock();
w.open_with_credentials().unwrap();
let mut w_lock = wallet1.lock();
let w = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
let (wallet_refreshed, _) =
api_impl::owner::retrieve_summary_info(&mut *w, true, 1).unwrap();
api_impl::owner::retrieve_summary_info(&mut **w, true, 1).unwrap();
assert!(wallet_refreshed);
w.close().unwrap();
}
if perform_tx {
let amount = 60_000_000_000;
let mut w = wallet1.lock();
w.open_with_credentials().unwrap();
let mut w_lock = wallet1.lock();
let w = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
let args = InitTxArgs {
src_acct_name: None,
amount,
@ -1409,27 +1443,26 @@ pub fn run_doctest_owner(
selection_strategy_is_use_all: true,
..Default::default()
};
let mut slate = api_impl::owner::init_send_tx(&mut *w, args, true).unwrap();
let mut slate = api_impl::owner::init_send_tx(&mut **w, args, true).unwrap();
println!("INITIAL SLATE");
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
{
let mut w2 = wallet2.lock();
w2.open_with_credentials().unwrap();
slate = api_impl::foreign::receive_tx(&mut *w2, &slate, None, None, true).unwrap();
let mut w_lock = wallet2.lock();
let w2 = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
slate = api_impl::foreign::receive_tx(&mut **w2, &slate, None, None, true).unwrap();
w2.close().unwrap();
}
// Spit out slate for input to finalize_tx
if lock_tx {
api_impl::owner::tx_lock_outputs(&mut *w, &slate, 0).unwrap();
api_impl::owner::tx_lock_outputs(&mut **w, &slate, 0).unwrap();
}
println!("RECEIPIENT SLATE");
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
if finalize_tx {
slate = api_impl::owner::finalize_tx(&mut *w, &slate).unwrap();
slate = api_impl::owner::finalize_tx(&mut **w, &slate).unwrap();
error!("FINALIZED TX SLATE");
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
}
w.close().unwrap();
}
if perform_tx && lock_tx && finalize_tx {
@ -1437,7 +1470,7 @@ pub fn run_doctest_owner(
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 3 as usize, false);
}
let mut api_owner = Owner::new(wallet1.clone());
let mut api_owner = Owner::new(wallet1);
api_owner.doctest_mode = true;
let owner_api = &api_owner as &dyn OwnerRpc;
Ok(owner_api.handle_request(request).as_option())

View file

@ -1,6 +1,6 @@
[package]
name = "grin_wallet_config"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Configuration for grin wallet , a simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -16,7 +16,7 @@ serde_derive = "1"
toml = "0.4"
dirs = "1.0.3"
grin_wallet_util = { path = "../util", version = "2.0.1-beta.1" }
grin_wallet_util = { path = "../util", version = "2.1.0-beta.1" }
[dev-dependencies]
pretty_assertions = "0.5.1"

View file

@ -95,8 +95,14 @@ pub fn check_api_secret(api_secret_path: &PathBuf) -> Result<(), ConfigError> {
}
/// Check that the api secret file exists and is valid
fn check_api_secret_file(chain_type: &global::ChainTypes) -> Result<(), ConfigError> {
let grin_path = get_grin_path(chain_type)?;
fn check_api_secret_file(
chain_type: &global::ChainTypes,
data_path: Option<PathBuf>,
) -> Result<(), ConfigError> {
let grin_path = match data_path {
Some(p) => p,
None => get_grin_path(chain_type)?,
};
let mut api_secret_path = grin_path.clone();
api_secret_path.push(API_SECRET_FILE_NAME);
if !api_secret_path.exists() {
@ -109,28 +115,33 @@ fn check_api_secret_file(chain_type: &global::ChainTypes) -> Result<(), ConfigEr
/// Handles setup and detection of paths for wallet
pub fn initial_setup_wallet(
chain_type: &global::ChainTypes,
data_path: Option<PathBuf>,
) -> Result<GlobalWalletConfig, ConfigError> {
check_api_secret_file(chain_type)?;
check_api_secret_file(chain_type, data_path.clone())?;
// Use config file if current directory if it exists, .grin home otherwise
if let Some(p) = check_config_current_dir(WALLET_CONFIG_FILE_NAME) {
GlobalWalletConfig::new(p.to_str().unwrap())
} else {
// Check if grin dir exists
let grin_path = get_grin_path(chain_type)?;
let grin_path = match data_path {
Some(p) => p,
None => get_grin_path(chain_type)?,
};
// Get path to default config file
let mut config_path = grin_path.clone();
config_path.push(WALLET_CONFIG_FILE_NAME);
// Spit it out if it doesn't exist
// Return defaults if file doesn't exist
if !config_path.exists() {
let mut default_config = GlobalWalletConfig::for_chain(chain_type);
default_config.config_file_path = Some(config_path);
// update paths relative to current dir
default_config.update_paths(&grin_path);
default_config.write_to_file(config_path.to_str().unwrap())?;
Ok(default_config)
} else {
GlobalWalletConfig::new(config_path.to_str().unwrap())
}
GlobalWalletConfig::new(config_path.to_str().unwrap())
}
}
@ -170,9 +181,7 @@ impl GlobalWalletConfig {
defaults.api_listen_port = 23415;
defaults.check_node_api_http_addr = "http://127.0.0.1:23413".to_owned();
}
global::ChainTypes::AutomatedTesting => {
panic!("Can't run automated testing directly");
}
_ => {}
}
defaults_conf
}

View file

@ -1,6 +1,6 @@
[package]
name = "grin_wallet_controller"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Controllers for grin wallet instantiation"
license = "Apache-2.0"
@ -32,9 +32,9 @@ chrono = { version = "0.4.4", features = ["serde"] }
easy-jsonrpc = "0.5.1"
lazy_static = "1"
grin_wallet_util = { path = "../util", version = "2.0.1-beta.1" }
grin_wallet_util = { path = "../util", version = "2.1.0-beta.1" }
grin_wallet_api = { path = "../api", version = "2.0.1-beta.1" }
grin_wallet_impls = { path = "../impls", version = "2.0.1-beta.1" }
grin_wallet_libwallet = { path = "../libwallet", version = "2.0.1-beta.1" }
grin_wallet_config = { path = "../config", version = "2.0.1-beta.1" }
grin_wallet_api = { path = "../api", version = "2.1.0-beta.1" }
grin_wallet_impls = { path = "../impls", version = "2.1.0-beta.1" }
grin_wallet_libwallet = { path = "../libwallet", version = "2.1.0-beta.1" }
grin_wallet_config = { path = "../config", version = "2.1.0-beta.1" }

View file

@ -12,31 +12,33 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Grin wallet command-line function implementations
use crate::api::TLSConfig;
use crate::config::{WalletConfig, WALLET_CONFIG_FILE_NAME};
use crate::core::{core, global};
use crate::error::{Error, ErrorKind};
use crate::impls::{create_sender, KeybaseAllChannels, SlateGetter as _, SlateReceiver as _};
use crate::impls::{PathToSlate, SlatePutter};
use crate::keychain;
use crate::libwallet::{InitTxArgs, IssueInvoiceTxArgs, NodeClient, WalletInst, WalletLCProvider};
use crate::util::{Mutex, ZeroingString};
use std::collections::HashMap;
/// Grin wallet command-line function implementations
use crate::{controller, display};
use serde_json as json;
use std::fs::File;
use std::io::Write;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use serde_json as json;
use uuid::Uuid;
use crate::api::TLSConfig;
use crate::core::core;
use crate::keychain;
use crate::config::WalletConfig;
use crate::error::{Error, ErrorKind};
use crate::impls::{
instantiate_wallet, FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter,
LMDBBackend, NullWalletCommAdapter,
};
use crate::impls::{HTTPNodeClient, WalletSeed};
use crate::libwallet::{InitTxArgs, IssueInvoiceTxArgs, NodeClient, WalletInst};
use crate::{controller, display};
fn show_recovery_phrase(phrase: ZeroingString) {
println!("Your recovery phrase is:");
println!();
println!("{}", &*phrase);
println!();
println!("Please back-up these words in a non-digital format.");
}
/// Arguments common to all wallet commands
#[derive(Clone)]
@ -44,6 +46,7 @@ pub struct GlobalArgs {
pub account: String,
pub node_api_secret: Option<String>,
pub show_spent: bool,
pub chain_type: global::ChainTypes,
pub password: Option<ZeroingString>,
pub tls_conf: Option<TLSConfig>,
}
@ -58,21 +61,28 @@ pub struct InitArgs {
pub restore: bool,
}
pub fn init(g_args: &GlobalArgs, args: InitArgs) -> Result<(), Error> {
WalletSeed::init_file(
&args.config,
args.list_length,
pub fn init<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
g_args: &GlobalArgs,
args: InitArgs,
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
let mut w_lock = wallet.lock();
let p = w_lock.lc_provider()?;
p.create_config(&g_args.chain_type, WALLET_CONFIG_FILE_NAME)?;
p.create_wallet(
None,
args.recovery_phrase,
&args.password,
args.list_length,
args.password.clone(),
)?;
info!("Wallet seed file created");
let client_n = HTTPNodeClient::new(
&args.config.check_node_api_http_addr,
g_args.node_api_secret.clone(),
);
let _: LMDBBackend<HTTPNodeClient, keychain::ExtKeychain> =
LMDBBackend::new(args.config.clone(), &args.password, client_n)?;
info!("Wallet database backend created");
let m = p.get_mnemonic(None, args.password)?;
show_recovery_phrase(m);
Ok(())
}
@ -82,30 +92,23 @@ pub struct RecoverArgs {
pub passphrase: ZeroingString,
}
/// Check whether seed file exists
pub fn wallet_seed_exists(config: &WalletConfig) -> Result<(), Error> {
let res = WalletSeed::seed_file_exists(&config)?;
Ok(res)
}
pub fn recover(config: &WalletConfig, args: RecoverArgs) -> Result<(), Error> {
if args.recovery_phrase.is_none() {
let res = WalletSeed::from_file(config, &args.passphrase);
if let Err(e) = res {
error!("Error loading wallet seed (check password): {}", e);
return Err(e.into());
}
let _ = res.unwrap().show_recovery_phrase();
} else {
let res = WalletSeed::recover_from_phrase(
&config,
&args.recovery_phrase.as_ref().unwrap(),
&args.passphrase,
);
if let Err(e) = res {
error!("Error recovering seed - {}", e);
return Err(e.into());
pub fn recover<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
args: RecoverArgs,
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
let mut w_lock = wallet.lock();
let p = w_lock.lc_provider()?;
match args.recovery_phrase {
None => {
let m = p.get_mnemonic(None, args.passphrase)?;
show_recovery_phrase(m);
}
Some(phrase) => p.recover_from_mnemonic(phrase, args.passphrase)?,
}
Ok(())
}
@ -115,49 +118,36 @@ pub struct ListenArgs {
pub method: String,
}
pub fn listen(config: &WalletConfig, args: &ListenArgs, g_args: &GlobalArgs) -> Result<(), Error> {
let mut params = HashMap::new();
params.insert("api_listen_addr".to_owned(), config.api_listen_addr());
if let Some(t) = g_args.tls_conf.as_ref() {
params.insert("certificate".to_owned(), t.certificate.clone());
params.insert("private_key".to_owned(), t.private_key.clone());
}
pub fn listen<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>,
config: &WalletConfig,
args: &ListenArgs,
g_args: &GlobalArgs,
) -> Result<(), Error>
where
L: WalletLCProvider<'static, C, K> + 'static,
C: NodeClient + 'static,
K: keychain::Keychain + 'static,
{
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(TLSConfig::new(
s.to_owned(),
params.get("private_key").unwrap().to_owned(),
)),
None => None,
};
controller::foreign_listener(wallet.clone(), &listen_addr, tls_conf)?;
Ok(())
"http" => controller::foreign_listener(
wallet.clone(),
&config.api_listen_addr(),
g_args.tls_conf.clone(),
),
"keybase" => KeybaseAllChannels::new()?.listen(
config.clone(),
g_args.password.clone().unwrap(),
&g_args.account,
g_args.node_api_secret.clone(),
),
method => {
return Err(ErrorKind::ArgumentError(format!(
"No listener for method \"{}\".",
method
))
.into());
}
"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(()),
};
if let Err(e) = res {
@ -166,11 +156,16 @@ pub fn listen(config: &WalletConfig, args: &ListenArgs, g_args: &GlobalArgs) ->
Ok(())
}
pub fn owner_api(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn owner_api<L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>,
config: &WalletConfig,
g_args: &GlobalArgs,
) -> Result<(), Error> {
) -> Result<(), Error>
where
L: WalletLCProvider<'static, C, K> + Send + Sync + 'static,
C: NodeClient + 'static,
K: keychain::Keychain + 'static,
{
let res = controller::owner_listener(
wallet,
config.owner_api_listen_addr().as_str(),
@ -189,10 +184,15 @@ pub struct AccountArgs {
pub create: Option<String>,
}
pub fn account(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn account<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
args: AccountArgs,
) -> Result<(), Error> {
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
if args.create.is_none() {
let res = controller::owner_single_use(wallet, |api| {
let acct_mappings = api.accounts()?;
@ -237,11 +237,16 @@ pub struct SendArgs {
pub target_slate_version: Option<u16>,
}
pub fn send(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn send<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
args: SendArgs,
dark_scheme: bool,
) -> Result<(), Error> {
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
if args.estimate_selection_strategies {
let strategies = vec!["smallest", "all"]
@ -291,42 +296,41 @@ pub fn send(
return Err(e);
}
};
let adapter = match args.method.as_str() {
"http" => HTTPWalletCommAdapter::new(),
"file" => FileWalletCommAdapter::new(),
"keybase" => KeybaseWalletCommAdapter::new(),
"self" => NullWalletCommAdapter::new(),
_ => NullWalletCommAdapter::new(),
};
if adapter.supports_sync() {
slate = adapter.send_tx_sync(&args.dest, &slate)?;
api.tx_lock_outputs(&slate, 0)?;
if args.method == "self" {
match args.method.as_str() {
"file" => {
PathToSlate((&args.dest).into()).put_tx(&slate)?;
api.tx_lock_outputs(&slate, 0)?;
return Ok(());
}
"self" => {
api.tx_lock_outputs(&slate, 0)?;
controller::foreign_single_use(wallet, |api| {
slate = api.receive_tx(&slate, Some(&args.dest), None)?;
Ok(())
})?;
}
if let Err(e) = api.verify_slate_messages(&slate) {
error!("Error validating participant messages: {}", e);
return Err(e);
method => {
let sender = create_sender(method, &args.dest)?;
slate = sender.send_tx(&slate)?;
api.tx_lock_outputs(&slate, 0)?;
}
slate = api.finalize_tx(&slate)?;
} else {
adapter.send_tx_async(&args.dest, &slate)?;
api.tx_lock_outputs(&slate, 0)?;
}
if adapter.supports_sync() {
let result = api.post_tx(&slate.tx, args.fluff);
match result {
Ok(_) => {
info!("Tx sent ok",);
return Ok(());
}
Err(e) => {
error!("Tx sent fail: {}", e);
return Err(e);
}
api.verify_slate_messages(&slate).map_err(|e| {
error!("Error validating participant messages: {}", e);
e
})?;
slate = api.finalize_tx(&slate)?;
let result = api.post_tx(&slate.tx, args.fluff);
match result {
Ok(_) => {
info!("Tx sent ok",);
return Ok(());
}
Err(e) => {
error!("Tx sent fail: {}", e);
return Err(e);
}
}
}
@ -341,13 +345,17 @@ pub struct ReceiveArgs {
pub message: Option<String>,
}
pub fn receive(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn receive<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
g_args: &GlobalArgs,
args: ReceiveArgs,
) -> Result<(), Error> {
let adapter = FileWalletCommAdapter::new();
let mut slate = adapter.receive_tx_async(&args.input)?;
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
let mut slate = PathToSlate((&args.input).into()).get_tx()?;
controller::foreign_single_use(wallet, |api| {
if let Err(e) = api.verify_slate_messages(&slate) {
error!("Error validating participant messages: {}", e);
@ -356,8 +364,7 @@ pub fn receive(
slate = api.receive_tx(&slate, Some(&g_args.account), args.message.clone())?;
Ok(())
})?;
let send_tx = format!("{}.response", args.input);
adapter.send_tx_async(&send_tx, &slate)?;
PathToSlate(format!("{}.response", args.input).into()).put_tx(&slate)?;
info!(
"Response file {}.response generated, and can be sent back to the transaction originator.",
args.input
@ -371,12 +378,17 @@ pub struct FinalizeArgs {
pub fluff: bool,
}
pub fn finalize(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn finalize<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
args: FinalizeArgs,
) -> Result<(), Error> {
let adapter = FileWalletCommAdapter::new();
let mut slate = adapter.receive_tx_async(&args.input)?;
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
let mut slate = PathToSlate((&args.input).into()).get_tx()?;
// Rather than duplicating the entire command, we'll just
// try to determine what kind of finalization this is
// based on the slate contents
@ -440,10 +452,15 @@ pub struct IssueInvoiceArgs {
pub issue_args: IssueInvoiceTxArgs,
}
pub fn issue_invoice_tx(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn issue_invoice_tx<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
args: IssueInvoiceArgs,
) -> Result<(), Error> {
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let slate = api.issue_invoice_tx(args.issue_args)?;
let mut tx_file = File::create(args.dest.clone())?;
@ -467,13 +484,17 @@ pub struct ProcessInvoiceArgs {
}
/// Process invoice
pub fn process_invoice(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn process_invoice<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
args: ProcessInvoiceArgs,
dark_scheme: bool,
) -> Result<(), Error> {
let adapter = FileWalletCommAdapter::new();
let slate = adapter.receive_tx_async(&args.input)?;
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
let slate = PathToSlate((&args.input).into()).get_tx()?;
controller::owner_single_use(wallet.clone(), |api| {
if args.estimate_selection_strategies {
let strategies = vec!["smallest", "all"]
@ -526,24 +547,25 @@ pub fn process_invoice(
return Err(e);
}
};
let adapter = match args.method.as_str() {
"http" => HTTPWalletCommAdapter::new(),
"file" => FileWalletCommAdapter::new(),
"self" => NullWalletCommAdapter::new(),
_ => NullWalletCommAdapter::new(),
};
if adapter.supports_sync() {
slate = adapter.send_tx_sync(&args.dest, &slate)?;
api.tx_lock_outputs(&slate, 0)?;
if args.method == "self" {
match args.method.as_str() {
"file" => {
let slate_putter = PathToSlate((&args.dest).into());
slate_putter.put_tx(&slate)?;
api.tx_lock_outputs(&slate, 0)?;
}
"self" => {
api.tx_lock_outputs(&slate, 0)?;
controller::foreign_single_use(wallet, |api| {
slate = api.finalize_invoice_tx(&slate)?;
Ok(())
})?;
}
} else {
adapter.send_tx_async(&args.dest, &slate)?;
api.tx_lock_outputs(&slate, 0)?;
method => {
let sender = create_sender(method, &args.dest)?;
slate = sender.send_tx(&slate)?;
api.tx_lock_outputs(&slate, 0)?;
}
}
}
Ok(())
@ -555,12 +577,17 @@ pub struct InfoArgs {
pub minimum_confirmations: u64,
}
pub fn info(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn info<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
g_args: &GlobalArgs,
args: InfoArgs,
dark_scheme: bool,
) -> Result<(), Error> {
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let (validated, wallet_info) =
api.retrieve_summary_info(true, args.minimum_confirmations)?;
@ -570,11 +597,16 @@ pub fn info(
Ok(())
}
pub fn outputs(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn outputs<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
g_args: &GlobalArgs,
dark_scheme: bool,
) -> Result<(), Error> {
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let res = api.node_height()?;
let (validated, outputs) = api.retrieve_outputs(g_args.show_spent, true, None)?;
@ -590,12 +622,17 @@ pub struct TxsArgs {
pub tx_slate_id: Option<Uuid>,
}
pub fn txs(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn txs<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
g_args: &GlobalArgs,
args: TxsArgs,
dark_scheme: bool,
) -> Result<(), Error> {
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let res = api.node_height()?;
let (validated, txs) = api.retrieve_txs(true, args.id, args.tx_slate_id)?;
@ -645,10 +682,15 @@ pub struct RepostArgs {
pub fluff: bool,
}
pub fn repost(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn repost<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
args: RepostArgs,
) -> Result<(), Error> {
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let (_, txs) = api.retrieve_txs(true, Some(args.id), None)?;
let stored_tx = api.get_stored_tx(&txs[0])?;
@ -691,10 +733,15 @@ pub struct CancelArgs {
pub tx_id_string: String,
}
pub fn cancel(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn cancel<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
args: CancelArgs,
) -> Result<(), Error> {
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let result = api.cancel_tx(args.tx_id, args.tx_slate_id);
match result {
@ -711,9 +758,14 @@ pub fn cancel(
Ok(())
}
pub fn restore(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
) -> Result<(), Error> {
pub fn restore<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
let result = api.restore();
match result {
@ -736,10 +788,15 @@ pub struct CheckArgs {
pub delete_unconfirmed: bool,
}
pub fn check_repair(
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
pub fn check_repair<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
args: CheckArgs,
) -> Result<(), Error> {
) -> Result<(), Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
controller::owner_single_use(wallet.clone(), |api| {
warn!("Starting wallet check...",);
warn!("Updating all wallet outputs, please wait ...",);

View file

@ -17,11 +17,10 @@
use crate::api::{self, ApiServer, BasicAuthMiddleware, ResponseFuture, Router, TLSConfig};
use crate::keychain::Keychain;
use crate::libwallet::{
Error, ErrorKind, NodeClient, NodeVersionInfo, Slate, WalletBackend, CURRENT_SLATE_VERSION,
GRIN_BLOCK_HEADER_VERSION,
Error, ErrorKind, NodeClient, NodeVersionInfo, Slate, WalletInst, WalletLCProvider,
CURRENT_SLATE_VERSION, GRIN_BLOCK_HEADER_VERSION,
};
use crate::util::to_base64;
use crate::util::Mutex;
use crate::util::{to_base64, Mutex};
use failure::ResultExt;
use futures::future::{err, ok};
use futures::{Future, Stream};
@ -29,7 +28,6 @@ use hyper::header::HeaderValue;
use hyper::{Body, Request, Response, StatusCode};
use serde::{Deserialize, Serialize};
use serde_json;
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::sync::Arc;
@ -74,41 +72,47 @@ fn check_middleware(
/// Instantiate wallet Owner API for a single-use (command line) call
/// Return a function containing a loaded API context to call
pub fn owner_single_use<F, T: ?Sized, C, K>(wallet: Arc<Mutex<T>>, f: F) -> Result<(), Error>
pub fn owner_single_use<'a, L, F, C, K>(
lc_provider: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
f: F,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
F: FnOnce(&mut Owner<T, C, K>) -> Result<(), Error>,
C: NodeClient,
K: Keychain,
L: WalletLCProvider<'a, C, K>,
F: FnOnce(&mut Owner<'a, L, C, K>) -> Result<(), Error>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
f(&mut Owner::new(wallet.clone()))?;
f(&mut Owner::new(lc_provider))?;
Ok(())
}
/// Instantiate wallet Foreign API for a single-use (command line) call
/// Return a function containing a loaded API context to call
pub fn foreign_single_use<F, T: ?Sized, C, K>(wallet: Arc<Mutex<T>>, f: F) -> Result<(), Error>
pub fn foreign_single_use<'a, L, F, C, K>(
lc_provider: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
f: F,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
F: FnOnce(&mut Foreign<T, C, K>) -> Result<(), Error>,
C: NodeClient,
K: Keychain,
L: WalletLCProvider<'a, C, K>,
F: FnOnce(&mut Foreign<'a, L, C, K>) -> Result<(), Error>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
f(&mut Foreign::new(wallet.clone(), Some(check_middleware)))?;
f(&mut Foreign::new(lc_provider, Some(check_middleware)))?;
Ok(())
}
/// Listener version, providing same API but listening for requests on a
/// port and wrapping the calls
pub fn owner_listener<T: ?Sized, C, K>(
wallet: Arc<Mutex<T>>,
pub fn owner_listener<L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
addr: &str,
api_secret: Option<String>,
tls_config: Option<TLSConfig>,
owner_api_include_foreign: Option<bool>,
) -> Result<(), Error>
where
T: WalletBackend<C, K> + Send + Sync + 'static,
L: WalletLCProvider<'static, C, K> + 'static,
C: NodeClient + 'static,
K: Keychain + 'static,
{
@ -131,21 +135,22 @@ where
// If so configured, add the foreign API to the same port
if owner_api_include_foreign.unwrap_or(false) {
info!("Starting HTTP Foreign API on Owner server at {}.", addr);
let foreign_api_handler_v2 = ForeignAPIHandlerV2::new(wallet.clone());
warn!("Starting HTTP Foreign API on Owner server at {}.", addr);
let foreign_api_handler_v2 = ForeignAPIHandlerV2::new(wallet);
router
.add_route("/v2/foreign", Arc::new(foreign_api_handler_v2))
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
}
let mut apis = ApiServer::new();
info!("Starting HTTP Owner API server at {}.", addr);
warn!("Starting HTTP Owner API server at {}.", addr);
let socket_addr: SocketAddr = addr.parse().expect("unable to parse socket address");
let api_thread =
apis.start(socket_addr, router, tls_config)
.context(ErrorKind::GenericError(
"API thread failed to start".to_string(),
))?;
warn!("HTTP Owner listener started.");
api_thread
.join()
.map_err(|e| ErrorKind::GenericError(format!("API thread panicked :{:?}", e)).into())
@ -153,13 +158,13 @@ where
/// Listener version, providing same API but listening for requests on a
/// port and wrapping the calls
pub fn foreign_listener<T: ?Sized, C, K>(
wallet: Arc<Mutex<T>>,
pub fn foreign_listener<L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
addr: &str,
tls_config: Option<TLSConfig>,
) -> Result<(), Error>
where
T: WalletBackend<C, K> + Send + Sync + 'static,
L: WalletLCProvider<'static, C, K> + 'static,
C: NodeClient + 'static,
K: Keychain + 'static,
{
@ -189,37 +194,33 @@ where
type WalletResponseFuture = Box<dyn Future<Item = Response<Body>, Error = Error> + Send>;
/// V2 API Handler/Wrapper for owner functions
pub struct OwnerAPIHandlerV2<T: ?Sized, C, K>
pub struct OwnerAPIHandlerV2<L, C, K>
where
T: WalletBackend<C, K> + Send + Sync + 'static,
L: WalletLCProvider<'static, C, K> + 'static,
C: NodeClient + 'static,
K: Keychain + 'static,
{
/// Wallet instance
pub wallet: Arc<Mutex<T>>,
phantom: PhantomData<K>,
phantom_c: PhantomData<C>,
pub wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
}
impl<T: ?Sized, C, K> OwnerAPIHandlerV2<T, C, K>
impl<L, C, K> OwnerAPIHandlerV2<L, C, K>
where
T: WalletBackend<C, K> + Send + Sync + 'static,
L: WalletLCProvider<'static, C, K>,
C: NodeClient + 'static,
K: Keychain + 'static,
{
/// Create a new owner API handler for GET methods
pub fn new(wallet: Arc<Mutex<T>>) -> OwnerAPIHandlerV2<T, C, K> {
OwnerAPIHandlerV2 {
wallet,
phantom: PhantomData,
phantom_c: PhantomData,
}
pub fn new(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
) -> OwnerAPIHandlerV2<L, C, K> {
OwnerAPIHandlerV2 { wallet }
}
fn call_api(
&self,
req: Request<Body>,
api: Owner<T, C, K>,
api: Owner<'static, L, C, K>,
) -> Box<dyn Future<Item = serde_json::Value, Error = Error> + Send> {
Box::new(parse_body(req).and_then(move |val: serde_json::Value| {
let owner_api = &api as &dyn OwnerRpc;
@ -243,9 +244,9 @@ where
}
}
impl<T: ?Sized, C, K> api::Handler for OwnerAPIHandlerV2<T, C, K>
impl<L, C, K> api::Handler for OwnerAPIHandlerV2<L, C, K>
where
T: WalletBackend<C, K> + Send + Sync + 'static,
L: WalletLCProvider<'static, C, K> + 'static,
C: NodeClient + 'static,
K: Keychain + 'static,
{
@ -266,37 +267,33 @@ where
}
/// V2 API Handler/Wrapper for foreign functions
pub struct ForeignAPIHandlerV2<T: ?Sized, C, K>
pub struct ForeignAPIHandlerV2<L, C, K>
where
T: WalletBackend<C, K> + Send + Sync + 'static,
L: WalletLCProvider<'static, C, K> + 'static,
C: NodeClient + 'static,
K: Keychain + 'static,
{
/// Wallet instance
pub wallet: Arc<Mutex<T>>,
phantom: PhantomData<K>,
phantom_c: PhantomData<C>,
pub wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
}
impl<T: ?Sized, C, K> ForeignAPIHandlerV2<T, C, K>
impl<L, C, K> ForeignAPIHandlerV2<L, C, K>
where
T: WalletBackend<C, K> + Send + Sync + 'static,
L: WalletLCProvider<'static, C, K> + 'static,
C: NodeClient + 'static,
K: Keychain + 'static,
{
/// Create a new foreign API handler for GET methods
pub fn new(wallet: Arc<Mutex<T>>) -> ForeignAPIHandlerV2<T, C, K> {
ForeignAPIHandlerV2 {
wallet,
phantom: PhantomData,
phantom_c: PhantomData,
}
pub fn new(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
) -> ForeignAPIHandlerV2<L, C, K> {
ForeignAPIHandlerV2 { wallet }
}
fn call_api(
&self,
req: Request<Body>,
api: Foreign<T, C, K>,
api: Foreign<'static, L, C, K>,
) -> Box<dyn Future<Item = serde_json::Value, Error = Error> + Send> {
Box::new(parse_body(req).and_then(move |val: serde_json::Value| {
let foreign_api = &api as &dyn ForeignRpc;
@ -320,9 +317,9 @@ where
}
}
impl<T: ?Sized, C, K> api::Handler for ForeignAPIHandlerV2<T, C, K>
impl<L, C, K> api::Handler for ForeignAPIHandlerV2<L, C, K>
where
T: WalletBackend<C, K> + Send + Sync + 'static,
L: WalletLCProvider<'static, C, K> + 'static,
C: NodeClient + 'static,
K: Keychain + 'static,
{

View file

@ -19,47 +19,43 @@ extern crate grin_wallet_impls as impls;
use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_keychain as keychain;
use grin_wallet_util::grin_util as util;
use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::{ExtKeychain, Keychain};
use grin_wallet_libwallet as libwallet;
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use impls::test_framework::{self, LocalWalletClient};
use libwallet::InitTxArgs;
use std::fs;
use std::thread;
use std::time::Duration;
fn clean_output_dir(test_dir: &str) {
let _ = fs::remove_dir_all(test_dir);
}
fn setup(test_dir: &str) {
util::init_test_logger();
clean_output_dir(test_dir);
global::set_mining_mode(ChainTypes::AutomatedTesting);
}
#[macro_use]
mod common;
use common::{create_wallet_proxy, setup};
/// Various tests on accounts within the same wallet
fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
setup(test_dir);
// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
let mut wallet_proxy = create_wallet_proxy(test_dir);
let chain = wallet_proxy.chain.clone();
// Create a new wallet test client, and set its queues to communicate with the
// proxy
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let wallet1 =
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
// define recipient wallet, add to proxy
let wallet2 =
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
create_wallet_and_add!(
client1,
wallet1,
test_dir,
"wallet1",
None,
&mut wallet_proxy
);
create_wallet_and_add!(
client2,
wallet2,
test_dir,
"wallet2",
None,
&mut wallet_proxy
);
// Set the wallet proxy listener running
thread::spawn(move || {
@ -103,20 +99,20 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// Default wallet 2 to listen on that account
{
let mut w = wallet2.lock();
wallet_inst!(wallet2, w);
w.set_parent_key_id_by_name("listener_account")?;
}
// Mine into two different accounts in the same wallet
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("account1")?;
assert_eq!(w.parent_key_id(), ExtKeychain::derive_key_id(2, 1, 0, 0, 0));
}
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), 7, false);
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("account2")?;
assert_eq!(w.parent_key_id(), ExtKeychain::derive_key_id(2, 2, 0, 0, 0));
}
@ -136,7 +132,9 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
})?;
// now check second account
{
let mut w = wallet1.lock();
let mut w_lock = wallet1.lock();
let lc = w_lock.lc_provider()?;
let w = lc.wallet_inst()?;
w.set_parent_key_id_by_name("account1")?;
}
wallet::controller::owner_single_use(wallet1.clone(), |api| {
@ -156,7 +154,7 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// should be nothing in default account
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("default")?;
}
wallet::controller::owner_single_use(wallet1.clone(), |api| {
@ -175,7 +173,7 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// Send a tx to another wallet
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("account1")?;
}
@ -208,7 +206,7 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// other account should be untouched
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("account2")?;
}
wallet::controller::owner_single_use(wallet1.clone(), |api| {
@ -233,7 +231,7 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
})?;
// Default account on wallet 2 should be untouched
{
let mut w = wallet2.lock();
wallet_inst!(wallet2, w);
w.set_parent_key_id_by_name("default")?;
}
wallet::controller::owner_single_use(wallet2.clone(), |api| {

View file

@ -18,70 +18,60 @@ extern crate grin_wallet_controller as wallet;
extern crate grin_wallet_impls as impls;
use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_keychain as keychain;
use grin_wallet_util::grin_util as util;
use self::core::consensus;
use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::ExtKeychain;
use grin_wallet_libwallet as libwallet;
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use impls::FileWalletCommAdapter;
use libwallet::{InitTxArgs, WalletInst};
use std::fs;
use impls::test_framework::{self, LocalWalletClient};
use impls::{PathToSlate, SlatePutter as _};
use libwallet::InitTxArgs;
use std::thread;
use std::time::Duration;
use util::ZeroingString;
#[macro_use]
mod common;
use common::{create_wallet_proxy, setup};
macro_rules! send_to_dest {
($a:expr, $b:expr, $c:expr, $d:expr) => {
test_framework::send_to_dest::<
WalletInst<LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>($a, $b, $c, $d, false)
test_framework::send_to_dest($a, $b, $c, $d, false)
};
}
macro_rules! wallet_info {
($a:expr) => {
test_framework::wallet_info::<
WalletInst<LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>($a)
test_framework::wallet_info($a)
};
}
fn clean_output_dir(test_dir: &str) {
let _ = fs::remove_dir_all(test_dir);
}
fn setup(test_dir: &str) {
util::init_test_logger();
clean_output_dir(test_dir);
global::set_mining_mode(ChainTypes::AutomatedTesting);
}
/// Various tests on checking functionality
fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
fn check_repair_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
setup(test_dir);
// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
let mut wallet_proxy = create_wallet_proxy(test_dir);
let chain = wallet_proxy.chain.clone();
// Create a new wallet test client, and set its queues to communicate with the
// proxy
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let wallet1 =
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
// define recipient wallet, add to proxy
let wallet2 =
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
create_wallet_and_add!(
client1,
wallet1,
test_dir,
"wallet1",
None,
&mut wallet_proxy
);
create_wallet_and_add!(
client2,
wallet2,
test_dir,
"wallet2",
None,
&mut wallet_proxy
);
// Set the wallet proxy listener running
thread::spawn(move || {
@ -138,8 +128,7 @@ fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
let w1_outputs: Vec<libwallet::OutputData> =
w1_outputs_commits.into_iter().map(|m| m.output).collect();
{
let mut w = wallet1.lock();
w.open_with_credentials()?;
wallet_inst!(wallet1, w);
{
let mut batch = w.batch()?;
batch.delete(&w1_outputs[4].key_id, &None)?;
@ -149,7 +138,6 @@ fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
batch.save(accidental_spent)?;
batch.commit()?;
}
w.close()?;
}
// check we have a problem now
@ -187,11 +175,10 @@ fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
selection_strategy_is_use_all: true,
..Default::default()
};
let mut slate = api.init_send_tx(args)?;
let slate = api.init_send_tx(args)?;
// output tx file
let file_adapter = FileWalletCommAdapter::new();
let send_file = format!("{}/part_tx_1.tx", test_dir);
file_adapter.send_tx_async(&send_file, &mut slate)?;
PathToSlate(send_file.into()).put_tx(&slate)?;
api.tx_lock_outputs(&slate, 0)?;
Ok(())
})?;
@ -221,107 +208,107 @@ fn check_repair_impl(test_dir: &str) -> Result<(), libwallet::Error> {
Ok(())
}
fn two_wallets_one_seed_impl(test_dir: &str) -> Result<(), libwallet::Error> {
fn two_wallets_one_seed_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
setup(test_dir);
let seed_phrase = "affair pistol cancel crush garment candy ancient flag work \
market crush dry stand focus mutual weapon offer ceiling rival turn team spring \
where swift";
let seed_phrase = Some(ZeroingString::from(seed_phrase));
// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
let mut wallet_proxy = create_wallet_proxy(test_dir);
let chain = wallet_proxy.chain.clone();
// Create a new wallet test client, and set its queues to communicate with the
// proxy
let m_client = LocalWalletClient::new("miner", wallet_proxy.tx.clone());
let miner =
test_framework::create_wallet(&format!("{}/miner", test_dir), m_client.clone(), None);
wallet_proxy.add_wallet("miner", m_client.get_send_instance(), miner.clone());
create_wallet_and_add!(m_client, miner, test_dir, "miner", None, &mut wallet_proxy);
// non-mining recipient wallets
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let wallet1 = test_framework::create_wallet(
&format!("{}/wallet1", test_dir),
client1.clone(),
Some(seed_phrase),
create_wallet_and_add!(
client1,
wallet1,
test_dir,
"wallet1",
seed_phrase,
&mut wallet_proxy
);
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
let wallet2 = test_framework::create_wallet(
&format!("{}/wallet2", test_dir),
client2.clone(),
Some(seed_phrase),
create_wallet_and_add!(
client2,
wallet2,
test_dir,
"wallet2",
seed_phrase,
&mut wallet_proxy
);
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
// we'll restore into here
let client3 = LocalWalletClient::new("wallet3", wallet_proxy.tx.clone());
let wallet3 = test_framework::create_wallet(
&format!("{}/wallet3", test_dir),
client3.clone(),
Some(seed_phrase),
create_wallet_and_add!(
client3,
wallet3,
test_dir,
"wallet3",
seed_phrase,
&mut wallet_proxy
);
wallet_proxy.add_wallet("wallet3", client3.get_send_instance(), wallet3.clone());
// also restore into here
let client4 = LocalWalletClient::new("wallet4", wallet_proxy.tx.clone());
let wallet4 = test_framework::create_wallet(
&format!("{}/wallet4", test_dir),
client4.clone(),
Some(seed_phrase),
create_wallet_and_add!(
client4,
wallet4,
test_dir,
"wallet4",
seed_phrase,
&mut wallet_proxy
);
wallet_proxy.add_wallet("wallet4", client4.get_send_instance(), wallet4.clone());
// Simulate a recover from seed without restore into here
let client5 = LocalWalletClient::new("wallet5", wallet_proxy.tx.clone());
let wallet5 = test_framework::create_wallet(
&format!("{}/wallet5", test_dir),
client5.clone(),
Some(seed_phrase),
create_wallet_and_add!(
client5,
wallet5,
test_dir,
"wallet5",
seed_phrase,
&mut wallet_proxy
);
wallet_proxy.add_wallet("wallet5", client5.get_send_instance(), wallet5.clone());
//simulate a recover from seed without restore into here
let client6 = LocalWalletClient::new("wallet6", wallet_proxy.tx.clone());
let wallet6 = test_framework::create_wallet(
&format!("{}/wallet6", test_dir),
client6.clone(),
Some(seed_phrase),
create_wallet_and_add!(
client6,
wallet6,
test_dir,
"wallet6",
seed_phrase,
&mut wallet_proxy
);
wallet_proxy.add_wallet("wallet6", client6.get_send_instance(), wallet6.clone());
let client7 = LocalWalletClient::new("wallet7", wallet_proxy.tx.clone());
let wallet7 = test_framework::create_wallet(
&format!("{}/wallet7", test_dir),
client7.clone(),
Some(seed_phrase),
create_wallet_and_add!(
client7,
wallet7,
test_dir,
"wallet7",
seed_phrase,
&mut wallet_proxy
);
wallet_proxy.add_wallet("wallet7", client7.get_send_instance(), wallet7.clone());
let client8 = LocalWalletClient::new("wallet8", wallet_proxy.tx.clone());
let wallet8 = test_framework::create_wallet(
&format!("{}/wallet8", test_dir),
client8.clone(),
Some(seed_phrase),
create_wallet_and_add!(
client8,
wallet8,
test_dir,
"wallet8",
seed_phrase,
&mut wallet_proxy
);
wallet_proxy.add_wallet("wallet8", client8.get_send_instance(), wallet8.clone());
let client9 = LocalWalletClient::new("wallet9", wallet_proxy.tx.clone());
let wallet9 = test_framework::create_wallet(
&format!("{}/wallet9", test_dir),
client9.clone(),
Some(seed_phrase),
create_wallet_and_add!(
client9,
wallet9,
test_dir,
"wallet9",
seed_phrase,
&mut wallet_proxy
);
wallet_proxy.add_wallet("wallet9", client9.get_send_instance(), wallet9.clone());
let client10 = LocalWalletClient::new("wallet10", wallet_proxy.tx.clone());
let wallet10 = test_framework::create_wallet(
&format!("{}/wallet10", test_dir),
client10.clone(),
Some(seed_phrase),
create_wallet_and_add!(
client10,
wallet10,
test_dir,
"wallet10",
seed_phrase,
&mut wallet_proxy
);
wallet_proxy.add_wallet("wallet10", client10.get_send_instance(), wallet10.clone());
// Set the wallet proxy listener running
thread::spawn(move || {

View file

@ -0,0 +1,138 @@
// Copyright 2019 The Grin Developers
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! common functions for tests (instantiating wallet and proxy, mostly)
extern crate grin_wallet_controller as wallet;
extern crate grin_wallet_impls as impls;
extern crate grin_wallet_libwallet as libwallet;
use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_keychain as keychain;
use grin_wallet_util::grin_util as util;
use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::ExtKeychain;
use self::libwallet::WalletInst;
use impls::test_framework::{LocalWalletClient, WalletProxy};
use impls::{DefaultLCProvider, DefaultWalletImpl};
use std::fs;
use std::sync::Arc;
use util::{Mutex, ZeroingString};
#[macro_export]
macro_rules! wallet_inst {
($wallet:ident, $w: ident) => {
let mut w_lock = $wallet.lock();
let lc = w_lock.lc_provider()?;
let $w = lc.wallet_inst()?;
};
}
#[macro_export]
macro_rules! create_wallet_and_add {
($client:ident, $wallet: ident, $test_dir: expr, $name: expr, $seed_phrase: expr, $proxy: expr) => {
let $client = LocalWalletClient::new($name, $proxy.tx.clone());
let $wallet =
common::create_local_wallet($test_dir, $name, $seed_phrase.clone(), $client.clone());
$proxy.add_wallet($name, $client.get_send_instance(), $wallet.clone());
};
}
#[macro_export]
macro_rules! open_wallet_and_add {
($client:ident, $wallet: ident, $test_dir: expr, $name: expr, $proxy: expr) => {
let $client = LocalWalletClient::new($name, $proxy.tx.clone());
let $wallet = common::open_local_wallet($test_dir, $name, $client.clone());
$proxy.add_wallet($name, $client.get_send_instance(), $wallet.clone());
};
}
pub fn clean_output_dir(test_dir: &str) {
let _ = fs::remove_dir_all(test_dir);
}
pub fn setup(test_dir: &str) {
util::init_test_logger();
clean_output_dir(test_dir);
global::set_mining_mode(ChainTypes::AutomatedTesting);
}
pub fn create_wallet_proxy(
test_dir: &str,
) -> WalletProxy<DefaultLCProvider<LocalWalletClient, ExtKeychain>, LocalWalletClient, ExtKeychain>
{
WalletProxy::new(test_dir)
}
pub fn create_local_wallet(
test_dir: &str,
name: &str,
mnemonic: Option<ZeroingString>,
client: LocalWalletClient,
) -> Arc<
Mutex<
Box<
WalletInst<
'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>,
>,
> {
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
as Box<
WalletInst<
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>;
let lc = wallet.lc_provider().unwrap();
lc.set_wallet_directory(&format!("{}/{}", test_dir, name));
lc.create_wallet(None, mnemonic, 32, ZeroingString::from(""))
.unwrap();
lc.open_wallet(None, ZeroingString::from("")).unwrap();
Arc::new(Mutex::new(wallet))
}
pub fn open_local_wallet(
test_dir: &str,
name: &str,
client: LocalWalletClient,
) -> Arc<
Mutex<
Box<
WalletInst<
'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>,
>,
> {
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(client).unwrap())
as Box<
WalletInst<
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>;
let lc = wallet.lc_provider().unwrap();
lc.set_wallet_directory(&format!("{}/{}", test_dir, name));
lc.open_wallet(None, ZeroingString::from("")).unwrap();
Arc::new(Mutex::new(wallet))
}

View file

@ -17,17 +17,11 @@ extern crate log;
extern crate grin_wallet_controller as wallet;
extern crate grin_wallet_impls as impls;
use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_keychain as keychain;
use grin_wallet_util::grin_util as util;
use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::ExtKeychain;
use grin_wallet_libwallet as libwallet;
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use impls::FileWalletCommAdapter;
use std::fs;
use grin_wallet_util::grin_core as core;
use impls::test_framework::{self, LocalWalletClient};
use impls::{PathToSlate, SlateGetter as _, SlatePutter as _};
use std::thread;
use std::time::Duration;
@ -35,32 +29,36 @@ use grin_wallet_libwallet::InitTxArgs;
use serde_json;
fn clean_output_dir(test_dir: &str) {
let _ = fs::remove_dir_all(test_dir);
}
fn setup(test_dir: &str) {
util::init_test_logger();
clean_output_dir(test_dir);
global::set_mining_mode(ChainTypes::AutomatedTesting);
}
#[macro_use]
mod common;
use common::{create_wallet_proxy, setup};
/// self send impl
fn file_exchange_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
setup(test_dir);
// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
let mut wallet_proxy = create_wallet_proxy(test_dir);
let chain = wallet_proxy.chain.clone();
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let wallet1 =
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
let wallet2 =
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
// Create a new wallet test client, and set its queues to communicate with the
// proxy
create_wallet_and_add!(
client1,
wallet1,
test_dir,
"wallet1",
None,
&mut wallet_proxy
);
create_wallet_and_add!(
client2,
wallet2,
test_dir,
"wallet2",
None,
&mut wallet_proxy
);
// Set the wallet proxy listener running
thread::spawn(move || {
@ -88,7 +86,7 @@ fn file_exchange_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// Get some mining done
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("mining")?;
}
let mut bh = 10u64;
@ -119,20 +117,18 @@ fn file_exchange_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
};
let mut slate = api.init_send_tx(args)?;
// output tx file
let file_adapter = FileWalletCommAdapter::new();
file_adapter.send_tx_async(&send_file, &mut slate)?;
PathToSlate((&send_file).into()).put_tx(&mut slate)?;
api.tx_lock_outputs(&slate, 0)?;
Ok(())
})?;
// Get some mining done
{
let mut w = wallet2.lock();
wallet_inst!(wallet2, w);
w.set_parent_key_id_by_name("account1")?;
}
let adapter = FileWalletCommAdapter::new();
let mut slate = adapter.receive_tx_async(&send_file)?;
let mut slate = PathToSlate((&send_file).into()).get_tx()?;
let mut naughty_slate = slate.clone();
naughty_slate.participant_data[0].message = Some("I changed the message".to_owned());
@ -148,14 +144,13 @@ fn file_exchange_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// wallet 2 receives file, completes, sends file back
wallet::controller::foreign_single_use(wallet2.clone(), |api| {
slate = api.receive_tx(&slate, None, Some(sender2_message.clone()))?;
adapter.send_tx_async(&receive_file, &mut slate)?;
PathToSlate((&receive_file).into()).put_tx(&slate)?;
Ok(())
})?;
// wallet 1 finalises and posts
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let adapter = FileWalletCommAdapter::new();
let mut slate = adapter.receive_tx_async(&receive_file)?;
let mut slate = PathToSlate(receive_file.into()).get_tx()?;
api.verify_slate_messages(&slate)?;
slate = api.finalize_tx(&slate)?;
api.post_tx(&slate.tx, false)?;

View file

@ -17,54 +17,43 @@ extern crate log;
extern crate grin_wallet_controller as wallet;
extern crate grin_wallet_impls as impls;
use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_keychain as keychain;
use grin_wallet_util::grin_util as util;
use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::ExtKeychain;
use grin_wallet_libwallet as libwallet;
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use grin_wallet_util::grin_core as core;
use impls::test_framework::{self, LocalWalletClient};
use libwallet::{InitTxArgs, IssueInvoiceTxArgs, Slate};
use std::fs;
use std::thread;
use std::time::Duration;
fn clean_output_dir(test_dir: &str) {
let _ = fs::remove_dir_all(test_dir);
}
fn setup(test_dir: &str) {
util::init_test_logger();
clean_output_dir(test_dir);
global::set_mining_mode(ChainTypes::AutomatedTesting);
}
fn teardown(test_dir: &str) {
clean_output_dir(test_dir);
}
#[macro_use]
mod common;
use common::{clean_output_dir, create_wallet_proxy, setup};
/// self send impl
fn invoice_tx_impl(test_dir: &str) -> Result<(), libwallet::Error> {
fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
{
setup(test_dir);
// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> =
WalletProxy::new(test_dir);
let mut wallet_proxy = create_wallet_proxy(test_dir);
let chain = wallet_proxy.chain.clone();
// Create a new wallet test client, and set its queues to communicate with the proxy
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let wallet1 =
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
// wallet 2, will be recipient
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
let wallet2 =
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
create_wallet_and_add!(
client1,
wallet1,
test_dir,
"wallet1",
None,
&mut wallet_proxy
);
create_wallet_and_add!(
client2,
wallet2,
test_dir,
"wallet2",
None,
&mut wallet_proxy
);
// Set the wallet proxy listener running
thread::spawn(move || {
@ -85,7 +74,7 @@ fn invoice_tx_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// Get some mining done
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("mining")?;
}
let mut bh = 10u64;
@ -211,7 +200,7 @@ fn invoice_tx_impl(test_dir: &str) -> Result<(), libwallet::Error> {
thread::sleep(Duration::from_millis(200));
}
teardown(test_dir);
clean_output_dir(test_dir);
Ok(())
}

View file

@ -19,45 +19,43 @@ extern crate grin_wallet_impls as impls;
extern crate grin_wallet_libwallet as libwallet;
use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_keychain as keychain;
use grin_wallet_util::grin_util as util;
use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::ExtKeychain;
use self::libwallet::{InitTxArgs, Slate};
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use impls::FileWalletCommAdapter;
use std::fs;
use impls::test_framework::{self, LocalWalletClient};
use impls::{PathToSlate, SlateGetter as _, SlatePutter as _};
use std::thread;
use std::time::Duration;
fn clean_output_dir(test_dir: &str) {
let _ = fs::remove_dir_all(test_dir);
}
fn setup(test_dir: &str) {
util::init_test_logger();
clean_output_dir(test_dir);
global::set_mining_mode(ChainTypes::AutomatedTesting);
}
#[macro_use]
mod common;
use common::{create_wallet_proxy, setup};
/// self send impl
fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
setup(test_dir);
// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
let mut wallet_proxy = create_wallet_proxy(test_dir);
let chain = wallet_proxy.chain.clone();
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let wallet1 =
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
let wallet2 =
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
// Create a new wallet test client, and set its queues to communicate with the
// proxy
create_wallet_and_add!(
client1,
wallet1,
test_dir,
"wallet1",
None,
&mut wallet_proxy
);
create_wallet_and_add!(
client2,
wallet2,
test_dir,
"wallet2",
None,
&mut wallet_proxy
);
// Set the wallet proxy listener running
thread::spawn(move || {
@ -85,7 +83,7 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// Get some mining done
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("mining")?;
}
let mut bh = 10u64;
@ -112,10 +110,8 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
selection_strategy_is_use_all: true,
..Default::default()
};
let mut slate = api.init_send_tx(args)?;
// output tx file
let file_adapter = FileWalletCommAdapter::new();
file_adapter.send_tx_async(&send_file, &mut slate)?;
let slate = api.init_send_tx(args)?;
PathToSlate((&send_file).into()).put_tx(&slate)?;
api.tx_lock_outputs(&slate, 0)?;
Ok(())
})?;
@ -125,28 +121,26 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// wallet 1 receives file to different account, completes
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("listener")?;
}
wallet::controller::foreign_single_use(wallet1.clone(), |api| {
let adapter = FileWalletCommAdapter::new();
slate = adapter.receive_tx_async(&send_file)?;
slate = PathToSlate((&send_file).into()).get_tx()?;
slate = api.receive_tx(&slate, None, None)?;
adapter.send_tx_async(&receive_file, &mut slate)?;
PathToSlate((&receive_file).into()).put_tx(&slate)?;
Ok(())
})?;
// wallet 1 receives file to different account, completes
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("mining")?;
}
// wallet 1 finalize
wallet::controller::owner_single_use(wallet1.clone(), |api| {
let adapter = FileWalletCommAdapter::new();
slate = adapter.receive_tx_async(&receive_file)?;
slate = PathToSlate((&receive_file).into()).get_tx()?;
slate = api.finalize_tx(&slate)?;
Ok(())
})?;
@ -173,7 +167,7 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
})?;
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("listener")?;
}
@ -187,11 +181,11 @@ fn file_repost_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// as above, but syncronously
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("mining")?;
}
{
let mut w = wallet2.lock();
wallet_inst!(wallet2, w);
w.set_parent_key_id_by_name("account1")?;
}

View file

@ -18,44 +18,51 @@ extern crate grin_wallet_controller as wallet;
extern crate grin_wallet_impls as impls;
extern crate grin_wallet_libwallet as libwallet;
use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_keychain as keychain;
use grin_wallet_util::grin_util as util;
use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::{ExtKeychain, Identifier, Keychain};
use self::libwallet::{AcctPathMapping, InitTxArgs, Slate};
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use impls::test_framework::{self, LocalWalletClient};
use std::fs;
use std::sync::atomic::Ordering;
use std::thread;
use std::time::Duration;
fn clean_output_dir(test_dir: &str) {
let _ = fs::remove_dir_all(test_dir);
}
#[macro_use]
mod common;
use common::{create_wallet_proxy, setup};
fn setup(test_dir: &str) {
util::init_test_logger();
clean_output_dir(test_dir);
global::set_mining_mode(ChainTypes::AutomatedTesting);
}
fn restore_wallet(base_dir: &'static str, wallet_dir: &str) -> Result<(), libwallet::Error> {
let source_seed = format!("{}/{}/wallet_data/wallet.seed", base_dir, wallet_dir);
let dest_wallet_name = format!("{}_restore", wallet_dir);
let dest_dir = format!("{}/{}/wallet_data", base_dir, dest_wallet_name);
let mut wallet_proxy = create_wallet_proxy(base_dir);
create_wallet_and_add!(
client,
wallet,
base_dir,
&dest_wallet_name,
None,
&mut wallet_proxy
);
// close created wallet
let mut w_lock = wallet.lock();
let lc = w_lock.lc_provider()?;
lc.close_wallet(None)?;
fn restore_wallet(base_dir: &str, wallet_dir: &str) -> Result<(), libwallet::Error> {
let source_seed = format!("{}/{}/wallet.seed", base_dir, wallet_dir);
let dest_dir = format!("{}/{}_restore", base_dir, wallet_dir);
fs::create_dir_all(dest_dir.clone())?;
let dest_seed = format!("{}/wallet.seed", dest_dir);
println!("Source: {}, Dest: {}", source_seed, dest_seed);
fs::copy(source_seed, dest_seed)?;
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(base_dir);
let client = LocalWalletClient::new(wallet_dir, wallet_proxy.tx.clone());
let wallet = test_framework::create_wallet(&dest_dir, client.clone(), None);
wallet_proxy.add_wallet(wallet_dir, client.get_send_instance(), wallet.clone());
// reopen with new seed
open_wallet_and_add!(
client,
wallet,
&base_dir,
&dest_wallet_name,
&mut wallet_proxy
);
// Set the wallet proxy listener running
let wp_running = wallet_proxy.running.clone();
@ -79,39 +86,35 @@ fn restore_wallet(base_dir: &str, wallet_dir: &str) -> Result<(), libwallet::Err
}
fn compare_wallet_restore(
base_dir: &str,
base_dir: &'static str,
wallet_dir: &str,
account_path: &Identifier,
) -> Result<(), libwallet::Error> {
let restore_name = format!("{}_restore", wallet_dir);
let source_dir = format!("{}/{}", base_dir, wallet_dir);
let dest_dir = format!("{}/{}", base_dir, restore_name);
let mut wallet_proxy = create_wallet_proxy(base_dir);
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(base_dir);
let client = LocalWalletClient::new(wallet_dir, wallet_proxy.tx.clone());
let wallet_source = test_framework::create_wallet(&source_dir, client.clone(), None);
wallet_proxy.add_wallet(
open_wallet_and_add!(
client,
wallet_source,
&base_dir,
&wallet_dir,
client.get_send_instance(),
wallet_source.clone(),
&mut wallet_proxy
);
let client = LocalWalletClient::new(&restore_name, wallet_proxy.tx.clone());
let wallet_dest = test_framework::create_wallet(&dest_dir, client.clone(), None);
wallet_proxy.add_wallet(
open_wallet_and_add!(
client,
wallet_dest,
&base_dir,
&restore_name,
client.get_send_instance(),
wallet_dest.clone(),
&mut wallet_proxy
);
{
let mut w = wallet_source.lock();
wallet_inst!(wallet_source, w);
w.set_parent_key_id(account_path.clone());
}
{
let mut w = wallet_dest.lock();
wallet_inst!(wallet_dest, w);
w.set_parent_key_id(account_path.clone());
}
@ -181,24 +184,29 @@ fn compare_wallet_restore(
/// Build up 2 wallets, perform a few transactions on them
/// Then attempt to restore them in separate directories and check contents are the same
fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
fn setup_restore(test_dir: &'static str) -> Result<(), libwallet::Error> {
setup(test_dir);
// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
let mut wallet_proxy = create_wallet_proxy(test_dir);
let chain = wallet_proxy.chain.clone();
// Create a new wallet test client, and set its queues to communicate with the
// proxy
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let wallet1 =
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
// define recipient wallet, add to proxy
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
let wallet2 =
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
create_wallet_and_add!(
client1,
wallet1,
test_dir,
"wallet1",
None,
&mut wallet_proxy
);
create_wallet_and_add!(
client2,
wallet2,
test_dir,
"wallet2",
None,
&mut wallet_proxy
);
// wallet 2 will use another account
wallet::controller::owner_single_use(wallet2.clone(), |api| {
@ -209,15 +217,19 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
// Default wallet 2 to listen on that account
{
let mut w = wallet2.lock();
wallet_inst!(wallet2, w);
w.set_parent_key_id_by_name("account1")?;
}
// Another wallet
let client3 = LocalWalletClient::new("wallet3", wallet_proxy.tx.clone());
let wallet3 =
test_framework::create_wallet(&format!("{}/wallet3", test_dir), client3.clone(), None);
wallet_proxy.add_wallet("wallet3", client3.get_send_instance(), wallet3.clone());
create_wallet_and_add!(
client3,
wallet3,
test_dir,
"wallet3",
None,
&mut wallet_proxy
);
// Set the wallet proxy listener running
let wp_running = wallet_proxy.running.clone();
@ -301,7 +313,7 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
// Another listener account on wallet 2
{
let mut w = wallet2.lock();
wallet_inst!(wallet2, w);
w.set_parent_key_id_by_name("account2")?;
}
@ -350,7 +362,7 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
Ok(())
}
fn perform_restore(test_dir: &str) -> Result<(), libwallet::Error> {
fn perform_restore(test_dir: &'static str) -> Result<(), libwallet::Error> {
restore_wallet(test_dir, "wallet1")?;
compare_wallet_restore(
test_dir,

View file

@ -18,42 +18,34 @@ extern crate grin_wallet_controller as wallet;
extern crate grin_wallet_impls as impls;
use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_keychain as keychain;
use grin_wallet_util::grin_util as util;
use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::ExtKeychain;
use grin_wallet_libwallet as libwallet;
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use impls::test_framework::{self, LocalWalletClient};
use libwallet::InitTxArgs;
use std::fs;
use std::thread;
use std::time::Duration;
fn clean_output_dir(test_dir: &str) {
let _ = fs::remove_dir_all(test_dir);
}
fn setup(test_dir: &str) {
util::init_test_logger();
clean_output_dir(test_dir);
global::set_mining_mode(ChainTypes::AutomatedTesting);
}
#[macro_use]
mod common;
use common::{create_wallet_proxy, setup};
/// self send impl
fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
setup(test_dir);
// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
let mut wallet_proxy = create_wallet_proxy(test_dir);
let chain = wallet_proxy.chain.clone();
// Create a new wallet test client, and set its queues to communicate with the
// proxy
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let wallet1 =
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
create_wallet_and_add!(
client1,
wallet1,
test_dir,
"wallet1",
None,
&mut wallet_proxy
);
// Set the wallet proxy listener running
thread::spawn(move || {
@ -74,7 +66,7 @@ fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// Get some mining done
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("mining")?;
}
let mut bh = 10u64;
@ -123,7 +115,7 @@ fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
// Check total in 'listener' account
{
let mut w = wallet1.lock();
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("listener")?;
}
wallet::controller::owner_single_use(wallet1.clone(), |api| {

View file

@ -19,50 +19,42 @@ extern crate grin_wallet_impls as impls;
extern crate grin_wallet_libwallet as libwallet;
use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_keychain as keychain;
use grin_wallet_util::grin_util as util;
use self::core::core::transaction;
use self::core::global;
use self::core::global::ChainTypes;
use self::keychain::ExtKeychain;
use self::libwallet::{InitTxArgs, OutputStatus, Slate};
use impls::test_framework::{self, LocalWalletClient, WalletProxy};
use std::fs;
use impls::test_framework::{self, LocalWalletClient};
use std::thread;
use std::time::Duration;
fn clean_output_dir(test_dir: &str) {
let _ = fs::remove_dir_all(test_dir);
}
fn setup(test_dir: &str) {
util::init_test_logger();
clean_output_dir(test_dir);
global::set_mining_mode(ChainTypes::AutomatedTesting);
}
mod common;
use common::{create_wallet_proxy, setup};
/// Exercises the Transaction API fully with a test NodeClient operating
/// directly on a chain instance
/// Callable with any type of wallet
fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error> {
setup(test_dir);
// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
let mut wallet_proxy = create_wallet_proxy(test_dir);
let chain = wallet_proxy.chain.clone();
// Create a new wallet test client, and set its queues to communicate with the
// proxy
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let wallet1 =
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
// define recipient wallet, add to proxy
let wallet2 =
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
create_wallet_and_add!(
client1,
wallet1,
test_dir,
"wallet1",
None,
&mut wallet_proxy
);
create_wallet_and_add!(
client2,
wallet2,
test_dir,
"wallet2",
None,
&mut wallet_proxy
);
// Set the wallet proxy listener running
thread::spawn(move || {
@ -349,24 +341,28 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
/// Test rolling back transactions and outputs when a transaction is never
/// posted to a chain
fn tx_rollback(test_dir: &str) -> Result<(), libwallet::Error> {
fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
setup(test_dir);
// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
let mut wallet_proxy = create_wallet_proxy(test_dir);
let chain = wallet_proxy.chain.clone();
// Create a new wallet test client, and set its queues to communicate with the
// proxy
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let wallet1 =
test_framework::create_wallet(&format!("{}/wallet1", test_dir), client1.clone(), None);
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
// define recipient wallet, add to proxy
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
let wallet2 =
test_framework::create_wallet(&format!("{}/wallet2", test_dir), client2.clone(), None);
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
create_wallet_and_add!(
client1,
wallet1,
test_dir,
"wallet1",
None,
&mut wallet_proxy
);
create_wallet_and_add!(
client2,
wallet2,
test_dir,
"wallet2",
None,
&mut wallet_proxy
);
// Set the wallet proxy listener running
thread::spawn(move || {

View file

@ -1,6 +1,6 @@
[package]
name = "grin_wallet_impls"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Concrete types derived from libwallet traits"
license = "Apache-2.0"
@ -25,8 +25,8 @@ tokio-core = "0.1"
tokio-retry = "0.1"
uuid = { version = "0.7", features = ["serde", "v4"] }
chrono = { version = "0.4.4", features = ["serde"] }
url = "1.7.2"
grin_wallet_util = { path = "../util", version = "2.0.1-beta.1" }
grin_wallet_libwallet = { path = "../libwallet", version = "2.0.1-beta.1" }
grin_wallet_config = { path = "../config", version = "2.0.1-beta.1" }
grin_wallet_util = { path = "../util", version = "2.1.0-beta.1" }
grin_wallet_config = { path = "../config", version = "2.1.0-beta.1" }
grin_wallet_libwallet = { path = "../libwallet", version = "2.1.0-beta.1" }

View file

@ -16,32 +16,16 @@
use std::fs::File;
use std::io::{Read, Write};
use crate::config::WalletConfig;
use crate::libwallet::{Error, ErrorKind, Slate};
use crate::WalletCommAdapter;
use std::collections::HashMap;
use crate::{SlateGetter, SlatePutter};
use std::path::PathBuf;
#[derive(Clone)]
pub struct FileWalletCommAdapter {}
pub struct PathToSlate(pub PathBuf);
impl FileWalletCommAdapter {
/// Create
pub fn new() -> Box<dyn WalletCommAdapter> {
Box::new(FileWalletCommAdapter {})
}
}
impl WalletCommAdapter for FileWalletCommAdapter {
fn supports_sync(&self) -> bool {
false
}
fn send_tx_sync(&self, _dest: &str, _slate: &Slate) -> Result<Slate, Error> {
unimplemented!();
}
fn send_tx_async(&self, dest: &str, slate: &Slate) -> Result<(), Error> {
let mut pub_tx = File::create(dest)?;
impl SlatePutter for PathToSlate {
fn put_tx(&self, slate: &Slate) -> Result<(), Error> {
let mut pub_tx = File::create(&self.0)?;
pub_tx.write_all(
serde_json::to_string(slate)
.map_err(|_| ErrorKind::SlateSer)?
@ -50,22 +34,13 @@ impl WalletCommAdapter for FileWalletCommAdapter {
pub_tx.sync_all()?;
Ok(())
}
}
fn receive_tx_async(&self, params: &str) -> Result<Slate, Error> {
let mut pub_tx_f = File::open(params)?;
impl SlateGetter for PathToSlate {
fn get_tx(&self) -> Result<Slate, Error> {
let mut pub_tx_f = File::open(&self.0)?;
let mut content = String::new();
pub_tx_f.read_to_string(&mut content)?;
Ok(Slate::deserialize_upgrade(&content)?)
}
fn listen(
&self,
_params: HashMap<String, String>,
_config: WalletConfig,
_passphrase: &str,
_account: &str,
_node_api_secret: Option<String>,
) -> Result<(), Error> {
unimplemented!();
}
}

View file

@ -15,23 +15,28 @@
/// HTTP Wallet 'plugin' implementation
use crate::api;
use crate::libwallet::{Error, ErrorKind, Slate};
use crate::WalletCommAdapter;
use config::WalletConfig;
use crate::SlateSender;
use serde::Serialize;
use serde_json::{json, Value};
use std::collections::HashMap;
use url::Url;
#[derive(Clone)]
pub struct HTTPWalletCommAdapter {}
pub struct HttpSlateSender {
base_url: Url,
}
impl HTTPWalletCommAdapter {
/// Create
pub fn new() -> Box<dyn WalletCommAdapter> {
Box::new(HTTPWalletCommAdapter {})
impl HttpSlateSender {
/// Create, return Err if scheme is not "http"
pub fn new(base_url: Url) -> Result<HttpSlateSender, SchemeNotHttp> {
if base_url.scheme() != "http" {
Err(SchemeNotHttp)
} else {
Ok(HttpSlateSender { base_url })
}
}
/// Check version of the other wallet
fn check_other_version(&self, url: &str) -> Result<(), Error> {
/// Check version of the listening wallet
fn check_other_version(&self) -> Result<(), Error> {
let req = json!({
"jsonrpc": "2.0",
"method": "check_version",
@ -39,7 +44,7 @@ impl HTTPWalletCommAdapter {
"params": []
});
let res: String = post(url, None, &req).map_err(|e| {
let res: String = post(&self.base_url, None, &req).map_err(|e| {
let mut report = format!("Performing version check (is recipient listening?): {}", e);
let err_string = format!("{}", e);
if err_string.contains("404") {
@ -88,24 +93,15 @@ impl HTTPWalletCommAdapter {
}
}
impl WalletCommAdapter for HTTPWalletCommAdapter {
fn supports_sync(&self) -> bool {
true
}
fn send_tx_sync(&self, dest: &str, slate: &Slate) -> Result<Slate, Error> {
if &dest[..4] != "http" {
let err_str = format!(
"dest formatted as {} but send -d expected stdout or http://IP:port",
dest
);
error!("{}", err_str,);
Err(ErrorKind::Uri)?
}
let url = format!("{}/v2/foreign", dest);
impl SlateSender for HttpSlateSender {
fn send_tx(&self, slate: &Slate) -> Result<Slate, Error> {
let url: Url = self
.base_url
.join("/v2/foreign")
.expect("/v2/foreign is an invalid url path");
debug!("Posting transaction slate to {}", url);
self.check_other_version(&url)?;
self.check_other_version()?;
// Note: not using easy-jsonrpc as don't want the dependencies in this crate
let req = json!({
@ -120,7 +116,7 @@ impl WalletCommAdapter for HTTPWalletCommAdapter {
});
trace!("Sending receive_tx request: {}", req);
let res: String = post(url.as_str(), None, &req).map_err(|e| {
let res: String = post(&url, None, &req).map_err(|e| {
let report = format!("Posting transaction slate (is recipient listening?): {}", e);
error!("{}", report);
ErrorKind::ClientCallback(report)
@ -144,32 +140,24 @@ impl WalletCommAdapter for HTTPWalletCommAdapter {
Ok(slate)
}
}
fn send_tx_async(&self, _dest: &str, _slate: &Slate) -> Result<(), Error> {
unimplemented!();
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct SchemeNotHttp;
fn receive_tx_async(&self, _params: &str) -> Result<Slate, Error> {
unimplemented!();
}
fn listen(
&self,
_params: HashMap<String, String>,
_config: WalletConfig,
_passphrase: &str,
_account: &str,
_node_api_secret: Option<String>,
) -> Result<(), Error> {
unimplemented!();
impl Into<Error> for SchemeNotHttp {
fn into(self) -> Error {
let err_str = format!("url scheme must be http",);
ErrorKind::GenericError(err_str).into()
}
}
pub fn post<IN>(url: &str, api_secret: Option<String>, input: &IN) -> Result<String, api::Error>
pub fn post<IN>(url: &Url, api_secret: Option<String>, input: &IN) -> Result<String, api::Error>
where
IN: Serialize,
{
let req = api::client::create_post_request(url, api_secret, input)?;
// TODO: change create_post_request to accept a url instead of a &str
let req = api::client::create_post_request(url.as_str(), api_secret, input)?;
let res = api::client::send_request(req)?;
Ok(res)
}

View file

@ -14,11 +14,13 @@
// Keybase Wallet Plugin
use crate::adapters::{SlateReceiver, SlateSender};
use crate::config::WalletConfig;
use crate::keychain::ExtKeychain;
use crate::libwallet::api_impl::foreign;
use crate::libwallet::{Error, ErrorKind, Slate};
use crate::{instantiate_wallet, HTTPNodeClient, WalletCommAdapter};
use failure::ResultExt;
use crate::libwallet::{Error, ErrorKind, Slate, WalletInst};
use crate::util::ZeroingString;
use crate::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient};
use serde::Serialize;
use serde_json::{from_str, json, to_string, Value};
use std::collections::{HashMap, HashSet};
@ -36,25 +38,40 @@ const SLATE_NEW: &str = "grin_slate_new";
const SLATE_SIGNED: &str = "grin_slate_signed";
#[derive(Clone)]
pub struct KeybaseWalletCommAdapter {}
pub struct KeybaseChannel(String);
impl KeybaseWalletCommAdapter {
impl KeybaseChannel {
/// Check if keybase is installed and return an adapter object.
pub fn new() -> Box<WalletCommAdapter> {
let mut proc = if cfg!(target_os = "windows") {
Command::new("where")
} else {
Command::new("which")
};
proc.arg("keybase")
.stdout(Stdio::null())
.status()
.expect("Keybase executable not found, make sure it is installed and in your PATH");
pub fn new(channel: String) -> Result<KeybaseChannel, Error> {
// Limit only one recipient
if channel.matches(",").count() > 0 {
return Err(
ErrorKind::GenericError("Only one recipient is supported!".to_owned()).into(),
);
}
Box::new(KeybaseWalletCommAdapter {})
if !keybase_installed() {
return Err(ErrorKind::GenericError(
"Keybase executable not found, make sure it is installed and in your PATH"
.to_owned(),
)
.into());
}
Ok(KeybaseChannel(channel))
}
}
/// Check if keybase executable exists in path
fn keybase_installed() -> bool {
let mut proc = if cfg!(target_os = "windows") {
Command::new("where")
} else {
Command::new("which")
};
proc.arg("keybase").stdout(Stdio::null()).status().is_ok()
}
/// Send a json object to the keybase process. Type `keybase chat api --help` for a list of available methods.
fn api_send(payload: &str) -> Result<Value, Error> {
let mut proc = Command::new("keybase");
@ -280,23 +297,13 @@ fn poll(nseconds: u64, channel: &str) -> Option<Slate> {
None
}
impl WalletCommAdapter for KeybaseWalletCommAdapter {
fn supports_sync(&self) -> bool {
true
}
// Send a slate to a keybase username then wait for a response for TTL seconds.
fn send_tx_sync(&self, addr: &str, slate: &Slate) -> Result<Slate, Error> {
// Limit only one recipient
if addr.matches(",").count() > 0 {
error!("Only one recipient is supported!");
return Err(ErrorKind::GenericError("Tx rejected".to_owned()))?;
}
impl SlateSender for KeybaseChannel {
/// Send a slate to a keybase username then wait for a response for TTL seconds.
fn send_tx(&self, slate: &Slate) -> Result<Slate, Error> {
let id = slate.id;
// Send original slate to recipient with the SLATE_NEW topic
match send(&slate, addr, SLATE_NEW, TTL) {
match send(&slate, &self.0, SLATE_NEW, TTL) {
true => (),
false => {
return Err(ErrorKind::ClientCallback(
@ -304,9 +311,9 @@ impl WalletCommAdapter for KeybaseWalletCommAdapter {
))?;
}
}
info!("tx request has been sent to @{}, tx uuid: {}", addr, id);
info!("tx request has been sent to @{}, tx uuid: {}", &self.0, id);
// Wait for response from recipient with SLATE_SIGNED topic
match poll(TTL as u64, addr) {
match poll(TTL as u64, &self.0) {
Some(slate) => return Ok(slate),
None => {
return Err(ErrorKind::ClientCallback(
@ -315,30 +322,55 @@ impl WalletCommAdapter for KeybaseWalletCommAdapter {
}
}
}
}
/// Send a transaction asynchronously (result will be returned via the listener)
fn send_tx_async(&self, _addr: &str, _slate: &Slate) -> Result<(), Error> {
unimplemented!();
}
/// Receive a transaction async. (Actually just read it from wherever and return the slate)
fn receive_tx_async(&self, _params: &str) -> Result<Slate, Error> {
unimplemented!();
/// Receives slates on all channels with topic SLATE_NEW
pub struct KeybaseAllChannels {
_priv: (), // makes KeybaseAllChannels unconstructable without checking for existence of keybase executable
}
impl KeybaseAllChannels {
/// Create a KeybaseAllChannels, return error if keybase executable is not present
pub fn new() -> Result<KeybaseAllChannels, Error> {
if !keybase_installed() {
Err(ErrorKind::GenericError(
"Keybase executable not found, make sure it is installed and in your PATH"
.to_owned(),
)
.into())
} else {
Ok(KeybaseAllChannels { _priv: () })
}
}
}
impl SlateReceiver for KeybaseAllChannels {
/// Start a listener, passing received messages to the wallet api directly
#[allow(unreachable_code)]
fn listen(
&self,
_params: HashMap<String, String>,
config: WalletConfig,
passphrase: &str,
passphrase: ZeroingString,
account: &str,
node_api_secret: Option<String>,
) -> 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 mut wallet = Box::new(
DefaultWalletImpl::<'static, HTTPNodeClient>::new(node_client.clone()).unwrap(),
)
as Box<
WalletInst<
'static,
DefaultLCProvider<HTTPNodeClient, ExtKeychain>,
HTTPNodeClient,
ExtKeychain,
>,
>;
let lc = wallet.lc_provider().unwrap();
lc.set_wallet_directory(&config.data_file_dir);
lc.open_wallet(None, passphrase)?;
let wallet_inst = lc.wallet_inst()?;
wallet_inst.set_parent_key_id_by_name(account)?;
info!("Listening for transactions on keybase ...");
loop {
@ -379,10 +411,8 @@ impl WalletCommAdapter for KeybaseWalletCommAdapter {
return Err(e);
}
let res = {
let mut w = wallet.lock();
w.open_with_credentials()?;
let r = foreign::receive_tx(&mut *w, &slate, None, None, false);
w.close()?;
let r =
foreign::receive_tx(&mut **wallet_inst, &slate, None, None, false);
r
};
match res {

View file

@ -15,41 +15,81 @@
mod file;
mod http;
mod keybase;
mod null;
pub use self::file::FileWalletCommAdapter;
pub use self::http::HTTPWalletCommAdapter;
pub use self::keybase::KeybaseWalletCommAdapter;
pub use self::null::NullWalletCommAdapter;
pub use self::file::PathToSlate;
pub use self::http::HttpSlateSender;
pub use self::keybase::{KeybaseAllChannels, KeybaseChannel};
use crate::config::WalletConfig;
use crate::libwallet::{Error, Slate};
use std::collections::HashMap;
/// Encapsulate wallet to wallet communication functions
pub trait WalletCommAdapter {
/// Whether this adapter supports sync mode
fn supports_sync(&self) -> bool;
use crate::libwallet::{Error, ErrorKind, Slate};
use crate::util::ZeroingString;
/// Sends transactions to a corresponding SlateReceiver
pub trait SlateSender {
/// Send a transaction slate to another listening wallet and return result
/// TODO: Probably need a slate wrapper type
fn send_tx_sync(&self, addr: &str, slate: &Slate) -> Result<Slate, Error>;
/// Send a transaction asynchronously (result will be returned via the listener)
fn send_tx_async(&self, addr: &str, slate: &Slate) -> Result<(), Error>;
/// Receive a transaction async. (Actually just read it from wherever and return the slate)
fn receive_tx_async(&self, params: &str) -> Result<Slate, Error>;
fn send_tx(&self, slate: &Slate) -> Result<Slate, Error>;
}
pub trait SlateReceiver {
/// Start a listener, passing received messages to the wallet api directly
/// Takes a wallet config for now to avoid needing all sorts of awkward
/// type parameters on this trait
fn listen(
&self,
params: HashMap<String, String>,
config: WalletConfig,
passphrase: &str,
passphrase: ZeroingString,
account: &str,
node_api_secret: Option<String>,
) -> Result<(), Error>;
}
/// Posts slates to be read later by a corresponding getter
pub trait SlatePutter {
/// Send a transaction asynchronously
fn put_tx(&self, slate: &Slate) -> Result<(), Error>;
}
/// Checks for a transaction from a corresponding SlatePutter, returns the transaction if it exists
pub trait SlateGetter {
/// Receive a transaction async. (Actually just read it from wherever and return the slate)
fn get_tx(&self) -> Result<Slate, Error>;
}
/// select a SlateSender based on method and dest fields from, e.g., SendArgs
pub fn create_sender(method: &str, dest: &str) -> Result<Box<dyn SlateSender>, Error> {
use url::Url;
let invalid = || {
ErrorKind::WalletComms(format!(
"Invalid wallet comm type and destination. method: {}, dest: {}",
method, dest
))
};
Ok(match method {
"http" => {
let url: Url = dest.parse().map_err(|_| invalid())?;
Box::new(HttpSlateSender::new(url).map_err(|_| invalid())?)
}
"keybase" => Box::new(KeybaseChannel::new(dest.to_owned())?),
"self" => {
return Err(ErrorKind::WalletComms(
"No sender implementation for \"self\".".to_string(),
)
.into());
}
"file" => {
return Err(ErrorKind::WalletComms(
"File based transactions must be performed asynchronously.".to_string(),
)
.into());
}
_ => {
return Err(ErrorKind::WalletComms(format!(
"Wallet comm method \"{}\" does not exist.",
method
))
.into());
}
})
}

View file

@ -1,59 +0,0 @@
// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::config::WalletConfig;
/// Null Output 'plugin' implementation
use crate::libwallet::{Error, Slate};
use crate::WalletCommAdapter;
use std::collections::HashMap;
#[derive(Clone)]
pub struct NullWalletCommAdapter {}
impl NullWalletCommAdapter {
/// Create
pub fn new() -> Box<NullWalletCommAdapter> {
Box::new(NullWalletCommAdapter {})
}
}
impl WalletCommAdapter for NullWalletCommAdapter {
fn supports_sync(&self) -> bool {
true
}
fn send_tx_sync(&self, _dest: &str, slate: &Slate) -> Result<Slate, Error> {
Ok(slate.clone())
}
fn send_tx_async(&self, _dest: &str, _slate: &Slate) -> Result<(), Error> {
Ok(())
}
fn receive_tx_async(&self, _params: &str) -> Result<Slate, Error> {
unimplemented!();
}
fn listen(
&self,
_params: HashMap<String, String>,
_config: WalletConfig,
_passphrase: &str,
_account: &str,
_node_api_secret: Option<String>,
) -> Result<(), Error> {
unimplemented!();
}
}

View file

@ -15,9 +15,10 @@
use std::cell::RefCell;
use std::{fs, path};
// for writing storedtransaction files
// for writing stored transaction files
use std::fs::File;
use std::io::{Read, Write};
use std::marker::PhantomData;
use std::path::Path;
use failure::ResultExt;
@ -29,7 +30,7 @@ use crate::keychain::{ChildNumber, ExtKeychain, Identifier, Keychain, SwitchComm
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::core::ser;
use crate::libwallet::{check_repair, restore};
use crate::libwallet::{
AcctPathMapping, Context, Error, ErrorKind, NodeClient, OutputData, TxLogEntry, WalletBackend,
@ -37,9 +38,6 @@ use crate::libwallet::{
};
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";
@ -54,8 +52,8 @@ 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);
pub fn wallet_db_exists(data_file_dir: &str) -> bool {
let db_path = path::Path::new(data_file_dir).join(DB_DIR);
db_path.exists()
}
@ -92,25 +90,33 @@ where
Ok((ret_blind, ret_nonce))
}
pub struct LMDBBackend<C, K> {
pub struct LMDBBackend<'ck, C, K>
where
C: NodeClient + 'ck,
K: Keychain + 'ck,
{
db: store::Store,
config: WalletConfig,
/// passphrase: TODO better ways of dealing with this other than storing
passphrase: ZeroingString,
data_file_dir: String,
/// Keychain
pub keychain: Option<K>,
/// Parent path to use by default for output operations
parent_key_id: Identifier,
/// wallet to node client
w2n_client: C,
///phantom
_phantom: &'ck PhantomData<C>,
}
impl<C, K> LMDBBackend<C, K> {
pub fn new(config: WalletConfig, passphrase: &str, n_client: C) -> Result<Self, Error> {
let db_path = path::Path::new(&config.data_file_dir).join(DB_DIR);
impl<'ck, C, K> LMDBBackend<'ck, C, K>
where
C: NodeClient + 'ck,
K: Keychain + 'ck,
{
pub fn new(data_file_dir: &str, n_client: C) -> Result<Self, Error> {
let db_path = path::Path::new(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);
let stored_tx_path = path::Path::new(data_file_dir).join(TX_SAVE_DIR);
fs::create_dir_all(&stored_tx_path)
.expect("Couldn't create wallet backend tx storage directory!");
@ -136,11 +142,11 @@ impl<C, K> LMDBBackend<C, K> {
let res = LMDBBackend {
db: store,
config: config.clone(),
passphrase: ZeroingString::from(passphrase),
data_file_dir: data_file_dir.to_owned(),
keychain: None,
parent_key_id: LMDBBackend::<C, K>::default_path(),
w2n_client: n_client,
_phantom: &PhantomData,
};
Ok(res)
}
@ -154,38 +160,36 @@ impl<C, K> LMDBBackend<C, K> {
/// 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);
pub fn exists(data_file_dir: &str) -> bool {
let db_path = path::Path::new(data_file_dir).join(DB_DIR);
db_path.exists()
}
}
impl<C, K> WalletBackend<C, K> for LMDBBackend<C, K>
impl<'ck, C, K> WalletBackend<'ck, C, K> for LMDBBackend<'ck, C, K>
where
C: NodeClient,
K: Keychain,
C: NodeClient + 'ck,
K: Keychain + 'ck,
{
/// 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(())
/// Set the keychain, which should already have been opened
fn set_keychain(&mut self, k: Box<K>) {
self.keychain = Some(*k);
}
/// Close wallet and remove any stored credentials (TBD)
/// Close wallet
fn close(&mut self) -> Result<(), Error> {
//TODO: Ensure this is zeroed?
self.keychain = None;
Ok(())
}
/// Return the keychain being used
fn keychain(&mut self) -> &mut K {
self.keychain.as_mut().unwrap()
fn keychain(&mut self) -> Result<&mut K, Error> {
if self.keychain.is_some() {
Ok(self.keychain.as_mut().unwrap())
} else {
Err(ErrorKind::KeychainDoesntExist.into())
}
}
/// Return the node client being used
@ -199,16 +203,18 @@ where
amount: u64,
id: &Identifier,
) -> Result<Option<String>, Error> {
if self.config.no_commit_cache == Some(true) {
//TODO: Check if this is really necessary, it's the only thing
//preventing removing the need for config in the wallet backend
/*if self.config.no_commit_cache == Some(true) {
Ok(None)
} else {
Ok(Some(util::to_hex(
self.keychain()
.commit(amount, &id, &SwitchCommitmentType::Regular)?
.0
.to_vec(), // TODO: proper support for different switch commitment schemes
)))
}
} else {*/
Ok(Some(util::to_hex(
self.keychain()?
.commit(amount, &id, &SwitchCommitmentType::Regular)?
.0
.to_vec(), // TODO: proper support for different switch commitment schemes
)))
/*}*/
}
/// Set parent path by account name
@ -263,7 +269,7 @@ where
&mut slate_id.to_vec(),
participant_id as u64,
);
let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain(), slate_id)?;
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),
@ -294,7 +300,7 @@ where
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)
let path = path::Path::new(&self.data_file_dir)
.join(TX_SAVE_DIR)
.join(filename);
let path_buf = Path::new(&path).to_path_buf();
@ -310,7 +316,7 @@ where
Some(f) => f,
None => return Ok(None),
};
let path = path::Path::new(&self.config.data_file_dir)
let path = path::Path::new(&self.data_file_dir)
.join(TX_SAVE_DIR)
.join(filename);
let tx_file = Path::new(&path).to_path_buf();
@ -382,7 +388,7 @@ where
C: NodeClient,
K: Keychain,
{
_store: &'a LMDBBackend<C, K>,
_store: &'a LMDBBackend<'a, C, K>,
db: RefCell<Option<store::Batch<'a>>>,
/// Keychain
keychain: Option<K>,

View file

@ -1,4 +1,4 @@
// Copyright 2018 The Grin Developers
// Copyright 2019 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -29,40 +29,59 @@ use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_keychain as keychain;
use grin_wallet_util::grin_store as store;
use grin_wallet_util::grin_util as util;
extern crate grin_wallet_config as config;
use grin_wallet_config as config;
mod adapters;
mod backends;
mod error;
mod lifecycle;
mod node_clients;
mod seed;
pub mod test_framework;
pub use crate::adapters::{
FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter, NullWalletCommAdapter,
WalletCommAdapter,
create_sender, HttpSlateSender, KeybaseAllChannels, KeybaseChannel, PathToSlate, SlateGetter,
SlatePutter, SlateReceiver, SlateSender,
};
pub use crate::backends::{wallet_db_exists, LMDBBackend};
pub use crate::error::{Error, ErrorKind};
pub use crate::lifecycle::DefaultLCProvider;
pub use crate::node_clients::HTTPNodeClient;
pub use crate::seed::{EncryptedWalletSeed, WalletSeed, SEED_FILE};
use crate::util::Mutex;
use std::sync::Arc;
use crate::keychain::{ExtKeychain, Keychain};
use libwallet::{NodeClient, WalletBackend, WalletInst};
use libwallet::{NodeClient, WalletInst, WalletLCProvider};
/// Helper to create an instance of the LMDB wallet
pub fn instantiate_wallet(
wallet_config: config::WalletConfig,
node_client: impl NodeClient + 'static,
passphrase: &str,
account: &str,
) -> Result<Arc<Mutex<WalletInst<impl NodeClient, keychain::ExtKeychain>>>, Error> {
// First test decryption, so we can abort early if we have the wrong password
let _ = WalletSeed::from_file(&wallet_config, passphrase)?;
let mut db_wallet = LMDBBackend::new(wallet_config.clone(), passphrase, node_client)?;
db_wallet.set_parent_key_id_by_name(account)?;
info!("Using LMDB Backend for wallet");
Ok(Arc::new(Mutex::new(db_wallet)))
/// Main wallet instance
pub struct DefaultWalletImpl<'a, C>
where
C: NodeClient + 'a,
{
lc_provider: DefaultLCProvider<'a, C, ExtKeychain>,
}
impl<'a, C> DefaultWalletImpl<'a, C>
where
C: NodeClient + 'a,
{
pub fn new(node_client: C) -> Result<Self, Error> {
let lc_provider = DefaultLCProvider::new(node_client);
Ok(DefaultWalletImpl {
lc_provider: lc_provider,
})
}
}
impl<'a, L, C, K> WalletInst<'a, L, C, K> for DefaultWalletImpl<'a, C>
where
DefaultLCProvider<'a, C, ExtKeychain>: WalletLCProvider<'a, C, K>,
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
fn lc_provider(
&mut self,
) -> Result<&mut (dyn WalletLCProvider<'a, C, K> + 'a), libwallet::Error> {
Ok(&mut self.lc_provider)
}
}

View file

@ -0,0 +1,230 @@
// Copyright 2019 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Default wallet lifecycle provider
use crate::config::{config, GlobalWalletConfig, GRIN_WALLET_DIR};
use crate::core::global;
use crate::keychain::Keychain;
use crate::libwallet::{Error, ErrorKind, NodeClient, WalletBackend, WalletLCProvider};
use crate::lifecycle::seed::WalletSeed;
use crate::util::ZeroingString;
use crate::LMDBBackend;
use failure::ResultExt;
use std::path::PathBuf;
pub struct DefaultLCProvider<'a, C, K>
where
C: NodeClient + 'a,
K: Keychain + 'a,
{
data_dir: String,
node_client: C,
backend: Option<Box<dyn WalletBackend<'a, C, K> + 'a>>,
}
impl<'a, C, K> DefaultLCProvider<'a, C, K>
where
C: NodeClient + 'a,
K: Keychain + 'a,
{
/// Create new provider
pub fn new(node_client: C) -> Self {
DefaultLCProvider {
node_client,
data_dir: "default".to_owned(),
backend: None,
}
}
}
impl<'a, C, K> WalletLCProvider<'a, C, K> for DefaultLCProvider<'a, C, K>
where
C: NodeClient + 'a,
K: Keychain + 'a,
{
fn set_wallet_directory(&mut self, dir: &str) {
self.data_dir = dir.to_owned();
}
fn create_config(&self, chain_type: &global::ChainTypes, file_name: &str) -> Result<(), Error> {
let mut default_config = GlobalWalletConfig::for_chain(chain_type);
let mut config_file_name = PathBuf::from(self.data_dir.clone());
config_file_name.push(file_name);
let mut data_dir_name = PathBuf::from(self.data_dir.clone());
data_dir_name.push(GRIN_WALLET_DIR);
if config_file_name.exists() && data_dir_name.exists() {
let msg = format!(
"{} already exists in the target directory ({}). Please remove it first",
file_name,
config_file_name.to_str().unwrap()
);
return Err(ErrorKind::Lifecycle(msg).into());
}
// just leave as is if file exists but there's no data dir
if config_file_name.exists() {
return Ok(());
}
default_config.update_paths(&PathBuf::from(self.data_dir.clone()));
let res = default_config.write_to_file(config_file_name.to_str().unwrap());
if let Err(e) = res {
let msg = format!(
"Error creating config file as ({}): {}",
config_file_name.to_str().unwrap(),
e
);
return Err(ErrorKind::Lifecycle(msg).into());
}
info!(
"File {} configured and created",
config_file_name.to_str().unwrap(),
);
let mut api_secret_path = PathBuf::from(self.data_dir.clone());
api_secret_path.push(PathBuf::from(config::API_SECRET_FILE_NAME));
if !api_secret_path.exists() {
config::init_api_secret(&api_secret_path).unwrap();
} else {
config::check_api_secret(&api_secret_path).unwrap();
}
Ok(())
}
fn create_wallet(
&mut self,
_name: Option<&str>,
mnemonic: Option<ZeroingString>,
mnemonic_length: usize,
password: ZeroingString,
) -> Result<(), Error> {
let mut data_dir_name = PathBuf::from(self.data_dir.clone());
data_dir_name.push(GRIN_WALLET_DIR);
let data_dir_name = data_dir_name.to_str().unwrap();
let _ = WalletSeed::init_file(&data_dir_name, mnemonic_length, mnemonic, password);
info!("Wallet seed file created");
let _wallet: LMDBBackend<'a, C, K> =
match LMDBBackend::new(&data_dir_name, self.node_client.clone()) {
Err(e) => {
let msg = format!("Error creating wallet: {}, Data Dir: {}", e, &data_dir_name);
return Err(ErrorKind::Lifecycle(msg).into());
}
Ok(d) => d,
};
info!("Wallet database backend created at {}", data_dir_name);
Ok(())
}
fn open_wallet(&mut self, _name: Option<&str>, password: ZeroingString) -> Result<(), Error> {
let mut data_dir_name = PathBuf::from(self.data_dir.clone());
data_dir_name.push(GRIN_WALLET_DIR);
let data_dir_name = data_dir_name.to_str().unwrap();
let mut wallet: LMDBBackend<'a, C, K> =
match LMDBBackend::new(&data_dir_name, self.node_client.clone()) {
Err(e) => {
let msg = format!("Error opening wallet: {}, Data Dir: {}", e, &data_dir_name);
return Err(ErrorKind::Lifecycle(msg).into());
}
Ok(d) => d,
};
let wallet_seed = WalletSeed::from_file(&data_dir_name, password)
.context(ErrorKind::Lifecycle("Error opening wallet".into()))?;
let keychain = wallet_seed
.derive_keychain(global::is_floonet())
.context(ErrorKind::Lifecycle("Error deriving keychain".into()))?;
wallet.set_keychain(Box::new(keychain));
self.backend = Some(Box::new(wallet));
Ok(())
}
fn close_wallet(&mut self, _name: Option<&str>) -> Result<(), Error> {
match self.backend.as_mut() {
Some(b) => b.close()?,
None => {}
};
self.backend = None;
Ok(())
}
fn wallet_exists(&self, _name: Option<&str>) -> Result<bool, Error> {
let mut data_dir_name = PathBuf::from(self.data_dir.clone());
data_dir_name.push(GRIN_WALLET_DIR);
let data_dir_name = data_dir_name.to_str().unwrap();
let res = WalletSeed::seed_file_exists(&data_dir_name).context(ErrorKind::CallbackImpl(
"Error checking for wallet existence",
))?;
Ok(res)
}
fn get_mnemonic(
&self,
_name: Option<&str>,
password: ZeroingString,
) -> Result<ZeroingString, Error> {
let mut data_dir_name = PathBuf::from(self.data_dir.clone());
data_dir_name.push(GRIN_WALLET_DIR);
let data_dir_name = data_dir_name.to_str().unwrap();
let wallet_seed = WalletSeed::from_file(&data_dir_name, password).context(
ErrorKind::Lifecycle("Error opening wallet seed file".into()),
)?;
let res = wallet_seed
.to_mnemonic()
.context(ErrorKind::Lifecycle("Error recovering wallet seed".into()))?;
Ok(ZeroingString::from(res))
}
fn validate_mnemonic(&self, mnemonic: ZeroingString) -> Result<(), Error> {
match WalletSeed::from_mnemonic(mnemonic) {
Ok(_) => Ok(()),
Err(_) => Err(ErrorKind::GenericError("Validating mnemonic".into()))?,
}
}
fn recover_from_mnemonic(
&self,
mnemonic: ZeroingString,
password: ZeroingString,
) -> Result<(), Error> {
let mut data_dir_name = PathBuf::from(self.data_dir.clone());
data_dir_name.push(GRIN_WALLET_DIR);
let data_dir_name = data_dir_name.to_str().unwrap();
WalletSeed::recover_from_phrase(data_dir_name, mnemonic, password).context(
ErrorKind::Lifecycle("Error recovering from mnemonic".into()),
)?;
Ok(())
}
fn change_password(&self, _old: String, _new: String) -> Result<(), Error> {
unimplemented!()
}
fn delete_wallet(&self, _name: Option<String>, _password: String) -> Result<(), Error> {
unimplemented!()
}
fn wallet_inst(&mut self) -> Result<&mut Box<dyn WalletBackend<'a, C, K> + 'a>, Error> {
match self.backend.as_mut() {
None => {
let msg = "Wallet has not been opened".into();
Err(ErrorKind::Lifecycle(msg).into())
}
Some(_) => Ok(&mut *self.backend.as_mut().unwrap()),
}
}
}

View file

@ -0,0 +1,18 @@
// Copyright 2019 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
mod default;
mod seed;
pub use self::default::DefaultLCProvider;

View file

@ -27,7 +27,6 @@ use ring::{digest, pbkdf2};
use crate::keychain::{mnemonic, Keychain};
use crate::util;
use crate::{Error, ErrorKind};
use config::WalletConfig;
use failure::ResultExt;
pub const SEED_FILE: &'static str = "wallet.seed";
@ -40,21 +39,21 @@ impl WalletSeed {
WalletSeed(bytes.to_vec())
}
pub fn from_mnemonic(word_list: &str) -> Result<WalletSeed, Error> {
let res = mnemonic::to_entropy(word_list);
pub fn from_mnemonic(word_list: util::ZeroingString) -> Result<WalletSeed, Error> {
let res = mnemonic::to_entropy(&word_list);
match res {
Ok(s) => Ok(WalletSeed::from_bytes(&s)),
Err(_) => Err(ErrorKind::Mnemonic.into()),
}
}
pub fn from_hex(hex: &str) -> Result<WalletSeed, Error> {
pub fn _from_hex(hex: &str) -> Result<WalletSeed, Error> {
let bytes = util::from_hex(hex.to_string())
.context(ErrorKind::GenericError("Invalid hex".to_owned()))?;
Ok(WalletSeed::from_bytes(&bytes))
}
pub fn to_hex(&self) -> String {
pub fn _to_hex(&self) -> String {
util::to_hex(self.0.to_vec())
}
@ -66,7 +65,7 @@ impl WalletSeed {
}
}
pub fn derive_keychain_old(old_wallet_seed: [u8; 32], password: &str) -> Vec<u8> {
pub fn _derive_keychain_old(old_wallet_seed: [u8; 32], password: &str) -> Vec<u8> {
let seed = blake2::blake2b::blake2b(64, password.as_bytes(), &old_wallet_seed);
seed.as_bytes().to_vec()
}
@ -85,35 +84,27 @@ impl WalletSeed {
WalletSeed(seed)
}
pub fn seed_file_exists(wallet_config: &WalletConfig) -> Result<(), Error> {
let seed_file_path = &format!(
"{}{}{}",
wallet_config.data_file_dir, MAIN_SEPARATOR, SEED_FILE,
);
pub fn seed_file_exists(data_file_dir: &str) -> Result<bool, Error> {
let seed_file_path = &format!("{}{}{}", data_file_dir, MAIN_SEPARATOR, SEED_FILE,);
println!("Seed file path: {}", seed_file_path);
if Path::new(seed_file_path).exists() {
return Err(ErrorKind::WalletSeedExists(seed_file_path.to_owned()))?;
Ok(true)
} else {
Ok(false)
}
Ok(())
}
pub fn backup_seed(wallet_config: &WalletConfig) -> Result<(), Error> {
let seed_file_name = &format!(
"{}{}{}",
wallet_config.data_file_dir, MAIN_SEPARATOR, SEED_FILE,
);
pub fn backup_seed(data_file_dir: &str) -> Result<(), Error> {
let seed_file_name = &format!("{}{}{}", data_file_dir, MAIN_SEPARATOR, SEED_FILE,);
let mut path = Path::new(seed_file_name).to_path_buf();
path.pop();
let mut backup_seed_file_name = format!(
"{}{}{}.bak",
wallet_config.data_file_dir, MAIN_SEPARATOR, SEED_FILE
);
let mut backup_seed_file_name =
format!("{}{}{}.bak", data_file_dir, MAIN_SEPARATOR, SEED_FILE);
let mut i = 1;
while Path::new(&backup_seed_file_name).exists() {
backup_seed_file_name = format!(
"{}{}{}.bak.{}",
wallet_config.data_file_dir, MAIN_SEPARATOR, SEED_FILE, i
);
backup_seed_file_name =
format!("{}{}{}.bak.{}", data_file_dir, MAIN_SEPARATOR, SEED_FILE, i);
i += 1;
}
path.push(backup_seed_file_name.clone());
@ -127,20 +118,19 @@ impl WalletSeed {
}
pub fn recover_from_phrase(
wallet_config: &WalletConfig,
word_list: &str,
password: &str,
data_file_dir: &str,
word_list: util::ZeroingString,
password: util::ZeroingString,
) -> Result<(), Error> {
let seed_file_path = &format!(
"{}{}{}",
wallet_config.data_file_dir, MAIN_SEPARATOR, SEED_FILE,
);
if WalletSeed::seed_file_exists(wallet_config).is_err() {
WalletSeed::backup_seed(wallet_config)?;
let seed_file_path = &format!("{}{}{}", data_file_dir, MAIN_SEPARATOR, SEED_FILE,);
println!("data file dir: {}", data_file_dir);
if let Ok(true) = WalletSeed::seed_file_exists(data_file_dir) {
println!("seed file exists");
WalletSeed::backup_seed(data_file_dir)?;
}
if !Path::new(&wallet_config.data_file_dir).exists() {
if !Path::new(&data_file_dir).exists() {
return Err(ErrorKind::WalletDoesntExist(
wallet_config.data_file_dir.clone(),
data_file_dir.to_owned(),
"To create a new wallet from a recovery phrase, use 'grin-wallet init -r'"
.to_owned(),
))?;
@ -155,34 +145,22 @@ impl WalletSeed {
Ok(())
}
pub fn show_recovery_phrase(&self) -> Result<(), Error> {
println!("Your recovery phrase is:");
println!();
println!("{}", self.to_mnemonic()?);
println!();
println!("Please back-up these words in a non-digital format.");
Ok(())
}
pub fn init_file(
wallet_config: &WalletConfig,
data_file_dir: &str,
seed_length: usize,
recovery_phrase: Option<util::ZeroingString>,
password: &str,
password: util::ZeroingString,
) -> Result<WalletSeed, Error> {
// create directory if it doesn't exist
fs::create_dir_all(&wallet_config.data_file_dir).context(ErrorKind::IO)?;
fs::create_dir_all(data_file_dir).context(ErrorKind::IO)?;
let seed_file_path = &format!(
"{}{}{}",
wallet_config.data_file_dir, MAIN_SEPARATOR, SEED_FILE,
);
let seed_file_path = &format!("{}{}{}", data_file_dir, MAIN_SEPARATOR, SEED_FILE,);
warn!("Generating wallet seed file at: {}", seed_file_path);
let _ = WalletSeed::seed_file_exists(wallet_config)?;
let _ = WalletSeed::seed_file_exists(data_file_dir)?;
let seed = match recovery_phrase {
Some(p) => WalletSeed::from_mnemonic(&p)?,
Some(p) => WalletSeed::from_mnemonic(p)?,
None => WalletSeed::init_new(seed_length),
};
@ -191,18 +169,18 @@ impl WalletSeed {
let mut file = File::create(seed_file_path).context(ErrorKind::IO)?;
file.write_all(&enc_seed_json.as_bytes())
.context(ErrorKind::IO)?;
seed.show_recovery_phrase()?;
Ok(seed)
}
pub fn from_file(wallet_config: &WalletConfig, password: &str) -> Result<WalletSeed, Error> {
pub fn from_file(
data_file_dir: &str,
password: util::ZeroingString,
) -> Result<WalletSeed, Error> {
// TODO: Is this desirable any more?
// create directory if it doesn't exist
fs::create_dir_all(&wallet_config.data_file_dir).context(ErrorKind::IO)?;
fs::create_dir_all(data_file_dir).context(ErrorKind::IO)?;
let seed_file_path = &format!(
"{}{}{}",
wallet_config.data_file_dir, MAIN_SEPARATOR, SEED_FILE,
);
let seed_file_path = &format!("{}{}{}", data_file_dir, MAIN_SEPARATOR, SEED_FILE,);
debug!("Using wallet seed file at: {}", seed_file_path);
@ -212,7 +190,7 @@ impl WalletSeed {
file.read_to_string(&mut buffer).context(ErrorKind::IO)?;
let enc_seed: EncryptedWalletSeed =
serde_json::from_str(&buffer).context(ErrorKind::Format)?;
let wallet_seed = enc_seed.decrypt(password)?;
let wallet_seed = enc_seed.decrypt(&password)?;
Ok(wallet_seed)
} else {
error!(
@ -240,7 +218,10 @@ pub struct EncryptedWalletSeed {
impl EncryptedWalletSeed {
/// Create a new encrypted seed from the given seed + password
pub fn from_seed(seed: &WalletSeed, password: &str) -> Result<EncryptedWalletSeed, Error> {
pub fn from_seed(
seed: &WalletSeed,
password: util::ZeroingString,
) -> Result<EncryptedWalletSeed, Error> {
let salt: [u8; 8] = thread_rng().gen();
let nonce: [u8; 12] = thread_rng().gen();
let password = password.as_bytes();
@ -289,3 +270,28 @@ impl EncryptedWalletSeed {
Ok(WalletSeed::from_bytes(&decrypted_data))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::ZeroingString;
#[test]
fn wallet_seed_encrypt() {
let password = ZeroingString::from("passwoid");
let wallet_seed = WalletSeed::init_new(32);
let mut enc_wallet_seed =
EncryptedWalletSeed::from_seed(&wallet_seed, password.clone()).unwrap();
println!("EWS: {:?}", enc_wallet_seed);
let decrypted_wallet_seed = enc_wallet_seed.decrypt(&password).unwrap();
assert_eq!(wallet_seed, decrypted_wallet_seed);
// Wrong password
let decrypted_wallet_seed = enc_wallet_seed.decrypt("");
assert!(decrypted_wallet_seed.is_err());
// Wrong nonce
enc_wallet_seed.nonce = "wrongnonce".to_owned();
let decrypted_wallet_seed = enc_wallet_seed.decrypt(&password);
assert!(decrypted_wallet_seed.is_err());
}
}

View file

@ -15,7 +15,6 @@
use crate::api;
use crate::chain;
use crate::chain::Chain;
use crate::config::WalletConfig;
use crate::core;
use crate::core::core::{OutputFeatures, OutputIdentifier, Transaction};
use crate::core::{consensus, global, pow};
@ -23,13 +22,10 @@ use crate::keychain;
use crate::libwallet;
use crate::libwallet::api_impl::{foreign, owner};
use crate::libwallet::{
BlockFees, CbData, InitTxArgs, NodeClient, WalletBackend, WalletInfo, WalletInst,
BlockFees, CbData, InitTxArgs, NodeClient, WalletInfo, WalletInst, WalletLCProvider,
};
use crate::util;
use crate::util::secp::pedersen;
use crate::util::Mutex;
use crate::LMDBBackend;
use crate::WalletSeed;
use chrono::Duration;
use std::sync::Arc;
use std::thread;
@ -105,14 +101,15 @@ pub fn add_block_with_reward(chain: &Chain, txs: Vec<&Transaction>, reward: CbDa
/// adds a reward output to a wallet, includes that reward in a block, mines
/// the block and adds it to the chain, with option transactions included.
/// Helpful for building up precise wallet balances for testing.
pub fn award_block_to_wallet<C, K>(
pub fn award_block_to_wallet<'a, L, C, K>(
chain: &Chain,
txs: Vec<&Transaction>,
wallet: Arc<Mutex<dyn WalletInst<C, K>>>,
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>,
) -> Result<(), libwallet::Error>
where
C: NodeClient,
K: keychain::Keychain,
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
// build block fees
let prev = chain.head_header().unwrap();
@ -124,10 +121,9 @@ where
};
// build coinbase (via api) and add block
let coinbase_tx = {
let mut w = wallet.lock();
w.open_with_credentials()?;
let res = foreign::build_coinbase(&mut *w, &block_fees, false)?;
w.close()?;
let mut w_lock = wallet.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
let res = foreign::build_coinbase(&mut **w, &block_fees, false)?;
res
};
add_block_with_reward(chain, txs, coinbase_tx.clone());
@ -135,15 +131,16 @@ where
}
/// Award a blocks to a wallet directly
pub fn award_blocks_to_wallet<C, K>(
pub fn award_blocks_to_wallet<'a, L, C, K>(
chain: &Chain,
wallet: Arc<Mutex<dyn WalletInst<C, K>>>,
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>,
number: usize,
pause_between: bool,
) -> Result<(), libwallet::Error>
where
C: NodeClient,
K: keychain::Keychain,
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
for _ in 0..number {
award_block_to_wallet(chain, vec![], wallet.clone())?;
@ -154,50 +151,22 @@ where
Ok(())
}
/// dispatch a db wallet
pub fn create_wallet<C, K>(
dir: &str,
n_client: C,
rec_phrase: Option<&str>,
) -> Arc<Mutex<dyn WalletInst<C, K>>>
where
C: NodeClient + 'static,
K: keychain::Keychain + 'static,
{
let z_string = match rec_phrase {
Some(s) => Some(util::ZeroingString::from(s)),
None => None,
};
let mut wallet_config = WalletConfig::default();
wallet_config.data_file_dir = String::from(dir);
let _ = WalletSeed::init_file(&wallet_config, 32, z_string, "");
let mut wallet = LMDBBackend::new(wallet_config.clone(), "", n_client)
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config));
wallet.open_with_credentials().unwrap_or_else(|e| {
panic!(
"Error initializing wallet: {:?} Config: {:?}",
e, wallet_config
)
});
Arc::new(Mutex::new(wallet))
}
/// send an amount to a destination
pub fn send_to_dest<T: ?Sized, C, K>(
wallet: Arc<Mutex<dyn WalletInst<C, K>>>,
pub fn send_to_dest<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
client: LocalWalletClient,
dest: &str,
amount: u64,
test_mode: bool,
) -> Result<(), libwallet::Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: keychain::Keychain,
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
let slate = {
let mut w = wallet.lock();
w.open_with_credentials()?;
let mut w_lock = wallet.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
let args = InitTxArgs {
src_acct_name: None,
amount,
@ -207,15 +176,15 @@ where
selection_strategy_is_use_all: true,
..Default::default()
};
let slate_i = owner::init_send_tx(&mut *w, args, test_mode)?;
let slate_i = owner::init_send_tx(&mut **w, args, test_mode)?;
let slate = client.send_tx_slate_direct(dest, &slate_i)?;
owner::tx_lock_outputs(&mut *w, &slate, 0)?;
let slate = owner::finalize_tx(&mut *w, &slate)?;
w.close()?;
owner::tx_lock_outputs(&mut **w, &slate, 0)?;
let slate = owner::finalize_tx(&mut **w, &slate)?;
slate
};
let client = {
let mut w = wallet.lock();
let mut w_lock = wallet.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
w.w2n_client().clone()
};
owner::post_tx(&client, &slate.tx, false)?; // mines a block
@ -223,18 +192,17 @@ where
}
/// get wallet info totals
pub fn wallet_info<T: ?Sized, C, K>(
wallet: Arc<Mutex<dyn WalletInst<C, K>>>,
pub fn wallet_info<'a, L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
) -> Result<WalletInfo, libwallet::Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: keychain::Keychain,
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: keychain::Keychain + 'a,
{
let mut w = wallet.lock();
w.open_with_credentials()?;
let (wallet_refreshed, wallet_info) = owner::retrieve_summary_info(&mut *w, true, 1)?;
let mut w_lock = wallet.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
let (wallet_refreshed, wallet_info) = owner::retrieve_summary_info(&mut **w, true, 1)?;
assert!(wallet_refreshed);
w.close()?;
Ok(wallet_info)
}

View file

@ -19,23 +19,23 @@
use crate::api;
use crate::chain::types::NoopAdapter;
use crate::chain::Chain;
use crate::config::WalletConfig;
use crate::core::core::verifier_cache::LruVerifierCache;
use crate::core::core::Transaction;
use crate::core::global::{set_mining_mode, ChainTypes};
use crate::core::{pow, ser};
use crate::keychain::Keychain;
use crate::libwallet;
use crate::libwallet::api_impl::foreign;
use crate::libwallet::{NodeClient, NodeVersionInfo, Slate, TxWrapper, WalletInst};
use crate::libwallet::{
NodeClient, NodeVersionInfo, Slate, TxWrapper, WalletInst, WalletLCProvider,
};
use crate::util;
use crate::util::secp::pedersen;
use crate::util::secp::pedersen::Commitment;
use crate::util::{Mutex, RwLock};
use crate::{libwallet, WalletCommAdapter};
use failure::ResultExt;
use serde_json;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
@ -57,10 +57,11 @@ pub struct WalletProxyMessage {
/// communicates with a chain instance or other wallet
/// listener APIs via message queues
pub struct WalletProxy<C, K>
pub struct WalletProxy<'a, L, C, K>
where
C: NodeClient,
K: Keychain,
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
/// directory to create the chain in
pub chain_dir: String,
@ -71,7 +72,7 @@ where
String,
(
Sender<WalletProxyMessage>,
Arc<Mutex<dyn WalletInst<LocalWalletClient, K>>>,
Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>,
),
>,
/// simulate json send to another client
@ -81,16 +82,13 @@ where
pub rx: Receiver<WalletProxyMessage>,
/// queue control
pub running: Arc<AtomicBool>,
/// Phantom
phantom_c: PhantomData<C>,
/// Phantom
phantom_k: PhantomData<K>,
}
impl<C, K> WalletProxy<C, K>
impl<'a, L, C, K> WalletProxy<'a, L, C, K>
where
C: NodeClient,
K: Keychain,
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
/// Create a new client that will communicate with the given grin node
pub fn new(chain_dir: &str) -> Self {
@ -115,8 +113,6 @@ where
rx: rx,
wallets: HashMap::new(),
running: Arc::new(AtomicBool::new(false)),
phantom_c: PhantomData,
phantom_k: PhantomData,
};
retval
}
@ -126,7 +122,7 @@ where
&mut self,
addr: &str,
tx: Sender<WalletProxyMessage>,
wallet: Arc<Mutex<dyn WalletInst<LocalWalletClient, K>>>,
wallet: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K> + 'a>>>,
) {
self.wallets.insert(addr.to_owned(), (tx, wallet));
}
@ -212,11 +208,10 @@ where
)?;
;
{
let mut w = wallet.1.lock();
w.open_with_credentials()?;
let mut w_lock = wallet.1.lock();
let w = w_lock.lc_provider()?.wallet_inst()?;
// receive tx
slate = foreign::receive_tx(&mut *w, &slate, None, None, false)?;
w.close()?;
slate = foreign::receive_tx(&mut **w, &slate, None, None, false)?;
}
Ok(WalletProxyMessage {
@ -344,55 +339,6 @@ impl LocalWalletClient {
}
}
impl WalletCommAdapter for LocalWalletClient {
fn supports_sync(&self) -> bool {
true
}
/// Send the slate to a listening wallet instance
fn send_tx_sync(&self, dest: &str, slate: &Slate) -> Result<Slate, libwallet::Error> {
let m = WalletProxyMessage {
sender_id: self.id.clone(),
dest: dest.to_owned(),
method: "send_tx_slate".to_owned(),
body: serde_json::to_string(slate).unwrap(),
};
{
let p = self.proxy_tx.lock();
p.send(m).context(libwallet::ErrorKind::ClientCallback(
"Send TX Slate".to_owned(),
))?;
}
let r = self.rx.lock();
let m = r.recv().unwrap();
trace!("Received send_tx_slate response: {:?}", m.clone());
Ok(
serde_json::from_str(&m.body).context(libwallet::ErrorKind::ClientCallback(
"Parsing send_tx_slate response".to_owned(),
))?,
)
}
fn send_tx_async(&self, _dest: &str, _slate: &Slate) -> Result<(), libwallet::Error> {
unimplemented!();
}
fn receive_tx_async(&self, _params: &str) -> Result<Slate, libwallet::Error> {
unimplemented!();
}
fn listen(
&self,
_params: HashMap<String, String>,
_config: WalletConfig,
_passphrase: &str,
_account: &str,
_node_api_secret: Option<String>,
) -> Result<(), libwallet::Error> {
unimplemented!();
}
}
impl NodeClient for LocalWalletClient {
fn node_url(&self) -> &str {
"node"
@ -535,3 +481,10 @@ impl NodeClient for LocalWalletClient {
Ok((o.highest_index, o.last_retrieved_index, api_outputs))
}
}
unsafe impl<'a, L, C, K> Send for WalletProxy<'a, L, C, K>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
}

View file

@ -1,36 +0,0 @@
// Copyright 2018 The Grin Developers
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Wallet seed encryption tests
extern crate grin_wallet_impls as impls;
use impls::{EncryptedWalletSeed, WalletSeed};
#[test]
fn wallet_seed_encrypt() {
let password = "passwoid";
let wallet_seed = WalletSeed::init_new(32);
let mut enc_wallet_seed = EncryptedWalletSeed::from_seed(&wallet_seed, password).unwrap();
println!("EWS: {:?}", enc_wallet_seed);
let decrypted_wallet_seed = enc_wallet_seed.decrypt(password).unwrap();
assert_eq!(wallet_seed, decrypted_wallet_seed);
// Wrong password
let decrypted_wallet_seed = enc_wallet_seed.decrypt("");
assert!(decrypted_wallet_seed.is_err());
// Wrong nonce
enc_wallet_seed.nonce = "wrongnonce".to_owned();
let decrypted_wallet_seed = enc_wallet_seed.decrypt(password);
assert!(decrypted_wallet_seed.is_err());
}

View file

@ -1,6 +1,6 @@
[package]
name = "grin_wallet_libwallet"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format."
license = "Apache-2.0"
@ -25,7 +25,7 @@ lazy_static = "1"
strum = "0.15"
strum_macros = "0.15"
grin_wallet_util = { path = "../util", version = "2.0.1-beta.1" }
grin_wallet_util = { path = "../util", version = "2.1.0-beta.1" }
[dev-dependencies]
grin_wallet_config = { path = "../config", version = "2.0.1-beta.1" }
grin_wallet_config = { path = "../config", version = "2.1.0-beta.1" }

View file

@ -35,15 +35,15 @@ pub fn check_version() -> VersionInfo {
}
/// Build a coinbase transaction
pub fn build_coinbase<T: ?Sized, C, K>(
pub fn build_coinbase<'a, T: ?Sized, C, K>(
w: &mut T,
block_fees: &BlockFees,
test_mode: bool,
) -> Result<CbData, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
updater::build_coinbase(&mut *w, block_fees, test_mode)
}
@ -54,7 +54,7 @@ pub fn verify_slate_messages(slate: &Slate) -> Result<(), Error> {
}
/// Receive a tx as recipient
pub fn receive_tx<T: ?Sized, C, K>(
pub fn receive_tx<'a, T: ?Sized, C, K>(
w: &mut T,
slate: &Slate,
dest_acct_name: Option<&str>,
@ -62,9 +62,9 @@ pub fn receive_tx<T: ?Sized, C, K>(
use_test_rng: bool,
) -> Result<Slate, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut ret_slate = slate.clone();
let parent_key_id = match dest_acct_name {
@ -113,11 +113,11 @@ where
}
/// Receive an tx that this wallet has issued
pub fn finalize_invoice_tx<T: ?Sized, C, K>(w: &mut T, slate: &Slate) -> Result<Slate, Error>
pub fn finalize_invoice_tx<'a, T: ?Sized, C, K>(w: &mut T, slate: &Slate) -> Result<Slate, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut sl = slate.clone();
let context = w.get_private_context(sl.id.as_bytes(), 1)?;

View file

@ -33,46 +33,46 @@ use crate::{
const USER_MESSAGE_MAX_LEN: usize = 256;
/// List of accounts
pub fn accounts<T: ?Sized, C, K>(w: &mut T) -> Result<Vec<AcctPathMapping>, Error>
pub fn accounts<'a, T: ?Sized, C, K>(w: &mut T) -> Result<Vec<AcctPathMapping>, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
keys::accounts(&mut *w)
}
/// new account path
pub fn create_account_path<T: ?Sized, C, K>(w: &mut T, label: &str) -> Result<Identifier, Error>
pub fn create_account_path<'a, T: ?Sized, C, K>(w: &mut T, label: &str) -> Result<Identifier, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
keys::new_acct_path(&mut *w, label)
}
/// set active account
pub fn set_active_account<T: ?Sized, C, K>(w: &mut T, label: &str) -> Result<(), Error>
pub fn set_active_account<'a, T: ?Sized, C, K>(w: &mut T, label: &str) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
w.set_parent_key_id_by_name(label)
}
/// retrieve outputs
pub fn retrieve_outputs<T: ?Sized, C, K>(
pub fn retrieve_outputs<'a, T: ?Sized, C, K>(
w: &mut T,
include_spent: bool,
refresh_from_node: bool,
tx_id: Option<u32>,
) -> Result<(bool, Vec<OutputCommitMapping>), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let parent_key_id = w.parent_key_id();
@ -88,16 +88,16 @@ where
}
/// Retrieve txs
pub fn retrieve_txs<T: ?Sized, C, K>(
pub fn retrieve_txs<'a, T: ?Sized, C, K>(
w: &mut T,
refresh_from_node: bool,
tx_id: Option<u32>,
tx_slate_id: Option<Uuid>,
) -> Result<(bool, Vec<TxLogEntry>), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let parent_key_id = w.parent_key_id();
@ -113,15 +113,15 @@ where
}
/// Retrieve summary info
pub fn retrieve_summary_info<T: ?Sized, C, K>(
pub fn retrieve_summary_info<'a, T: ?Sized, C, K>(
w: &mut T,
refresh_from_node: bool,
minimum_confirmations: u64,
) -> Result<(bool, WalletInfo), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let parent_key_id = w.parent_key_id();
@ -135,15 +135,15 @@ where
}
/// Initiate tx as sender
pub fn init_send_tx<T: ?Sized, C, K>(
pub fn init_send_tx<'a, T: ?Sized, C, K>(
w: &mut T,
args: InitTxArgs,
use_test_rng: bool,
) -> Result<Slate, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let parent_key_id = match args.src_acct_name {
Some(d) => {
@ -211,15 +211,15 @@ where
}
/// Initiate a transaction as the recipient (invoicing)
pub fn issue_invoice_tx<T: ?Sized, C, K>(
pub fn issue_invoice_tx<'a, T: ?Sized, C, K>(
w: &mut T,
args: IssueInvoiceTxArgs,
use_test_rng: bool,
) -> Result<Slate, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let parent_key_id = match args.dest_acct_name {
Some(d) => {
@ -268,16 +268,16 @@ where
/// Receive an invoice tx, essentially adding inputs to whatever
/// output was specified
pub fn process_invoice_tx<T: ?Sized, C, K>(
pub fn process_invoice_tx<'a, T: ?Sized, C, K>(
w: &mut T,
slate: &Slate,
args: InitTxArgs,
use_test_rng: bool,
) -> Result<Slate, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut ret_slate = slate.clone();
let parent_key_id = match args.src_acct_name {
@ -345,26 +345,26 @@ where
}
/// Lock sender outputs
pub fn tx_lock_outputs<T: ?Sized, C, K>(
pub fn tx_lock_outputs<'a, T: ?Sized, C, K>(
w: &mut T,
slate: &Slate,
participant_id: usize,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let context = w.get_private_context(slate.id.as_bytes(), participant_id)?;
selection::lock_tx_context(&mut *w, slate, &context)
}
/// Finalize slate
pub fn finalize_tx<T: ?Sized, C, K>(w: &mut T, slate: &Slate) -> Result<Slate, Error>
pub fn finalize_tx<'a, T: ?Sized, C, K>(w: &mut T, slate: &Slate) -> Result<Slate, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut sl = slate.clone();
let context = w.get_private_context(sl.id.as_bytes(), 0)?;
@ -380,15 +380,15 @@ where
}
/// cancel tx
pub fn cancel_tx<T: ?Sized, C, K>(
pub fn cancel_tx<'a, T: ?Sized, C, K>(
w: &mut T,
tx_id: Option<u32>,
tx_slate_id: Option<Uuid>,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let parent_key_id = w.parent_key_id();
if !update_outputs(w, false) {
@ -400,23 +400,23 @@ where
}
/// get stored tx
pub fn get_stored_tx<T: ?Sized, C, K>(
pub fn get_stored_tx<'a, T: ?Sized, C, K>(
w: &T,
entry: &TxLogEntry,
) -> Result<Option<Transaction>, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
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<C>(client: &C, tx: &Transaction, fluff: bool) -> Result<(), Error>
pub fn post_tx<'a, C>(client: &C, tx: &Transaction, fluff: bool) -> Result<(), Error>
where
C: NodeClient,
C: NodeClient + 'a,
{
let tx_hex = grin_util::to_hex(ser::ser_vec(tx).unwrap());
let res = client.post_tx(&TxWrapper { tx_hex: tx_hex }, fluff);
@ -439,32 +439,32 @@ pub fn verify_slate_messages(slate: &Slate) -> Result<(), Error> {
}
/// Attempt to restore contents of wallet
pub fn restore<T: ?Sized, C, K>(w: &mut T) -> Result<(), Error>
pub fn restore<'a, T: ?Sized, C, K>(w: &mut T) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
w.restore()
}
/// check repair
pub fn check_repair<T: ?Sized, C, K>(w: &mut T, delete_unconfirmed: bool) -> Result<(), Error>
pub fn check_repair<'a, T: ?Sized, C, K>(w: &mut T, delete_unconfirmed: bool) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
update_outputs(w, true);
w.check_repair(delete_unconfirmed)
}
/// node height
pub fn node_height<T: ?Sized, C, K>(w: &mut T) -> Result<NodeHeightResult, Error>
pub fn node_height<'a, T: ?Sized, C, K>(w: &mut T) -> Result<NodeHeightResult, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let res = w.w2n_client().get_chain_height();
match res {
@ -487,11 +487,11 @@ where
}
/// Attempt to update outputs in wallet, return whether it was successful
fn update_outputs<T: ?Sized, C, K>(w: &mut T, update_all: bool) -> bool
fn update_outputs<'a, T: ?Sized, C, K>(w: &mut T, update_all: bool) -> bool
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let parent_key_id = w.parent_key_id();
match updater::refresh_outputs(&mut *w, &parent_key_id, update_all) {

View file

@ -197,6 +197,14 @@ pub enum ErrorKind {
#[fail(display = "Compatibility Error: {}", _0)]
Compatibility(String),
/// Keychain doesn't exist (wallet not openend)
#[fail(display = "Keychain doesn't exist (has wallet been opened?)")]
KeychainDoesntExist,
/// Lifecycle Error
#[fail(display = "Lifecycle Error: {}", _0)]
Lifecycle(String),
/// Other
#[fail(display = "Generic error: {}", _0)]
GenericError(String),

View file

@ -18,26 +18,26 @@ use crate::grin_keychain::{ChildNumber, ExtKeychain, Identifier, Keychain};
use crate::types::{AcctPathMapping, NodeClient, WalletBackend};
/// Get next available key in the wallet for a given parent
pub fn next_available_key<T: ?Sized, C, K>(wallet: &mut T) -> Result<Identifier, Error>
pub fn next_available_key<'a, T: ?Sized, C, K>(wallet: &mut T) -> Result<Identifier, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let child = wallet.next_child()?;
Ok(child)
}
/// Retrieve an existing key from a wallet
pub fn retrieve_existing_key<T: ?Sized, C, K>(
pub fn retrieve_existing_key<'a, T: ?Sized, C, K>(
wallet: &T,
key_id: Identifier,
mmr_index: Option<u64>,
) -> Result<(Identifier, u32), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let existing = wallet.get(&key_id, &mmr_index)?;
let key_id = existing.key_id.clone();
@ -46,21 +46,21 @@ where
}
/// Returns a list of account to BIP32 path mappings
pub fn accounts<T: ?Sized, C, K>(wallet: &mut T) -> Result<Vec<AcctPathMapping>, Error>
pub fn accounts<'a, T: ?Sized, C, K>(wallet: &mut T) -> Result<Vec<AcctPathMapping>, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
Ok(wallet.acct_path_iter().collect())
}
/// Adds an new parent account path with a given label
pub fn new_acct_path<T: ?Sized, C, K>(wallet: &mut T, label: &str) -> Result<Identifier, Error>
pub fn new_acct_path<'a, T: ?Sized, C, K>(wallet: &mut T, label: &str) -> Result<Identifier, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let label = label.to_owned();
if let Some(_) = wallet.acct_path_iter().find(|l| l.label == label) {
@ -97,15 +97,15 @@ where
}
/// Adds/sets a particular account path with a given label
pub fn set_acct_path<T: ?Sized, C, K>(
pub fn set_acct_path<'a, T: ?Sized, C, K>(
wallet: &mut T,
label: &str,
path: &Identifier,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let label = label.to_owned();
let save_path = AcctPathMapping {

View file

@ -58,14 +58,14 @@ struct RestoredTxStats {
pub num_outputs: usize,
}
fn identify_utxo_outputs<T, C, K>(
fn identify_utxo_outputs<'a, T, C, K>(
wallet: &mut T,
outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
) -> Result<Vec<OutputResult>, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut wallet_outputs: Vec<OutputResult> = Vec::new();
@ -74,7 +74,7 @@ where
outputs.len(),
);
let keychain = wallet.keychain();
let keychain = wallet.keychain()?;
let legacy_builder = proof::LegacyProofBuilder::new(keychain);
let builder = proof::ProofBuilder::new(keychain);
let legacy_version = HeaderVersion(1);
@ -136,11 +136,11 @@ where
Ok(wallet_outputs)
}
fn collect_chain_outputs<T, C, K>(wallet: &mut T) -> Result<Vec<OutputResult>, Error>
fn collect_chain_outputs<'a, T, C, K>(wallet: &mut T) -> Result<Vec<OutputResult>, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let batch_size = 1000;
let mut start_index = 1;
@ -167,16 +167,16 @@ where
}
///
fn restore_missing_output<T, C, K>(
fn restore_missing_output<'a, T, C, K>(
wallet: &mut T,
output: OutputResult,
found_parents: &mut HashMap<Identifier, u32>,
tx_stats: &mut Option<&mut HashMap<Identifier, RestoredTxStats>>,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let commit = wallet.calc_commit_for_cache(output.value, &output.key_id)?;
let mut batch = wallet.batch()?;
@ -250,11 +250,11 @@ where
}
///
fn cancel_tx_log_entry<T, C, K>(wallet: &mut T, output: &OutputData) -> Result<(), Error>
fn cancel_tx_log_entry<'a, T, C, K>(wallet: &mut T, output: &OutputData) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let parent_key_id = output.key_id.parent_path();
let updated_tx_entry = if output.tx_log_entry.is_some() {
@ -290,11 +290,11 @@ where
/// Check / repair wallet contents
/// assume wallet contents have been freshly updated with contents
/// of latest block
pub fn check_repair<T, C, K>(wallet: &mut T, delete_unconfirmed: bool) -> Result<(), Error>
pub fn check_repair<'a, T, C, K>(wallet: &mut T, delete_unconfirmed: bool) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
// First, get a definitive list of outputs we own from the chain
warn!("Starting wallet check.");
@ -412,11 +412,11 @@ where
}
/// Restore a wallet
pub fn restore<T, C, K>(wallet: &mut T) -> Result<(), Error>
pub fn restore<'a, T, C, K>(wallet: &mut T) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
// Don't proceed if wallet_data has anything in it
let is_empty = wallet.iter().next().is_none();

View file

@ -32,7 +32,7 @@ use std::collections::HashMap;
/// and saves the private wallet identifiers of our selected outputs
/// into our transaction context
pub fn build_send_tx<T: ?Sized, C, K>(
pub fn build_send_tx<'a, T: ?Sized, C, K>(
wallet: &mut T,
slate: &mut Slate,
minimum_confirmations: u64,
@ -43,9 +43,9 @@ pub fn build_send_tx<T: ?Sized, C, K>(
use_test_nonce: bool,
) -> Result<Context, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let (elems, inputs, change_amounts_derivations, fee) = select_send_tx(
wallet,
@ -58,7 +58,7 @@ where
selection_strategy_is_use_all,
&parent_key_id,
)?;
let keychain = wallet.keychain();
let keychain = wallet.keychain()?;
let blinding = slate.add_transaction_elements(keychain, &ProofBuilder::new(keychain), elems)?;
slate.fee = fee;
@ -95,15 +95,15 @@ where
/// Locks all corresponding outputs in the context, creates
/// change outputs and tx log entry
pub fn lock_tx_context<T: ?Sized, C, K>(
pub fn lock_tx_context<'a, T: ?Sized, C, K>(
wallet: &mut T,
slate: &Slate,
context: &Context,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut output_commits: HashMap<Identifier, (Option<String>, u64)> = HashMap::new();
// Store cached commits before locking wallet
@ -172,20 +172,20 @@ where
/// Creates a new output in the wallet for the recipient,
/// returning the key of the fresh output
/// Also creates a new transaction containing the output
pub fn build_recipient_output<T: ?Sized, C, K>(
pub fn build_recipient_output<'a, T: ?Sized, C, K>(
wallet: &mut T,
slate: &mut Slate,
parent_key_id: Identifier,
use_test_rng: bool,
) -> Result<(Identifier, Context), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
// Create a potential output for this transaction
let key_id = keys::next_available_key(wallet).unwrap();
let keychain = wallet.keychain().clone();
let keychain = wallet.keychain()?.clone();
let key_id_inner = key_id.clone();
let amount = slate.amount;
let height = slate.height;
@ -201,7 +201,7 @@ where
let mut context = Context::new(
keychain.secp(),
blinding
.secret_key(wallet.keychain().clone().secp())
.secret_key(wallet.keychain()?.clone().secp())
.unwrap(),
&parent_key_id,
use_test_rng,
@ -240,7 +240,7 @@ where
/// Builds a transaction to send to someone from the HD seed associated with the
/// wallet and the amount to send. Handles reading through the wallet data file,
/// selecting outputs to spend and building the change.
pub fn select_send_tx<T: ?Sized, C, K, B>(
pub fn select_send_tx<'a, T: ?Sized, C, K, B>(
wallet: &mut T,
amount: u64,
current_height: u64,
@ -260,9 +260,9 @@ pub fn select_send_tx<T: ?Sized, C, K, B>(
Error,
>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
B: ProofBuild,
{
let (coins, _total, amount, fee) = select_coins_and_fee(
@ -288,7 +288,7 @@ where
}
/// Select outputs and calculating fee.
pub fn select_coins_and_fee<T: ?Sized, C, K>(
pub fn select_coins_and_fee<'a, T: ?Sized, C, K>(
wallet: &mut T,
amount: u64,
current_height: u64,
@ -307,9 +307,9 @@ pub fn select_coins_and_fee<T: ?Sized, C, K>(
Error,
>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
// select some spendable coins from the wallet
let (max_outputs, mut coins) = select_coins(
@ -393,7 +393,7 @@ where
}
/// Selects inputs and change for a transaction
pub fn inputs_and_change<T: ?Sized, C, K, B>(
pub fn inputs_and_change<'a, T: ?Sized, C, K, B>(
coins: &Vec<OutputData>,
wallet: &mut T,
amount: u64,
@ -407,9 +407,9 @@ pub fn inputs_and_change<T: ?Sized, C, K, B>(
Error,
>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
B: ProofBuild,
{
let mut parts = vec![];
@ -471,7 +471,7 @@ where
/// we should pass something other than a bool in.
/// TODO: Possibly move this into another trait to be owned by a wallet?
pub fn select_coins<T: ?Sized, C, K>(
pub fn select_coins<'a, T: ?Sized, C, K>(
wallet: &mut T,
amount: u64,
current_height: u64,
@ -482,9 +482,9 @@ pub fn select_coins<T: ?Sized, C, K>(
) -> (usize, Vec<OutputData>)
// max_outputs_available, Outputs
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
// first find all eligible outputs based on number of confirmations
let mut eligible = wallet

View file

@ -32,16 +32,16 @@ lazy_static! {
/// Creates a new slate for a transaction, can be called by anyone involved in
/// the transaction (sender(s), receiver(s))
pub fn new_tx_slate<T: ?Sized, C, K>(
pub fn new_tx_slate<'a, T: ?Sized, C, K>(
wallet: &mut T,
amount: u64,
num_participants: usize,
use_test_rng: bool,
) -> Result<Slate, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let current_height = wallet.w2n_client().get_chain_height()?;
let mut slate = Slate::blank(num_participants);
@ -68,7 +68,7 @@ where
}
/// Estimates locked amount and fee for the transaction without creating one
pub fn estimate_send_tx<T: ?Sized, C, K>(
pub fn estimate_send_tx<'a, T: ?Sized, C, K>(
wallet: &mut T,
amount: u64,
minimum_confirmations: u64,
@ -84,9 +84,9 @@ pub fn estimate_send_tx<T: ?Sized, C, K>(
Error,
>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
// Get lock height
let current_height = wallet.w2n_client().get_chain_height()?;
@ -114,7 +114,7 @@ where
}
/// Add inputs to the slate (effectively becoming the sender)
pub fn add_inputs_to_slate<T: ?Sized, C, K>(
pub fn add_inputs_to_slate<'a, T: ?Sized, C, K>(
wallet: &mut T,
slate: &mut Slate,
minimum_confirmations: u64,
@ -128,9 +128,9 @@ pub fn add_inputs_to_slate<T: ?Sized, C, K>(
use_test_rng: bool,
) -> Result<Context, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
// sender should always refresh outputs
updater::refresh_outputs(wallet, parent_key_id, false)?;
@ -157,7 +157,7 @@ where
// the offset in the slate's transaction kernel, and adds our public key
// information to the slate
let _ = slate.fill_round_1(
wallet.keychain(),
wallet.keychain()?,
&mut context.sec_key,
&context.sec_nonce,
participant_id,
@ -168,7 +168,7 @@ where
if !is_initator {
// perform partial sig
let _ = slate.fill_round_2(
wallet.keychain(),
wallet.keychain()?,
&context.sec_key,
&context.sec_nonce,
participant_id,
@ -179,7 +179,7 @@ where
}
/// Add receiver output to the slate
pub fn add_output_to_slate<T: ?Sized, C, K>(
pub fn add_output_to_slate<'a, T: ?Sized, C, K>(
wallet: &mut T,
slate: &mut Slate,
parent_key_id: &Identifier,
@ -189,9 +189,9 @@ pub fn add_output_to_slate<T: ?Sized, C, K>(
use_test_rng: bool,
) -> Result<Context, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
// create an output using the amount in the slate
let (_, mut context) =
@ -199,7 +199,7 @@ where
// fill public keys
let _ = slate.fill_round_1(
wallet.keychain(),
wallet.keychain()?,
&mut context.sec_key,
&context.sec_nonce,
1,
@ -210,7 +210,7 @@ where
if !is_initiator {
// perform partial sig
let _ = slate.fill_round_2(
wallet.keychain(),
wallet.keychain()?,
&context.sec_key,
&context.sec_nonce,
participant_id,
@ -221,40 +221,40 @@ where
}
/// Complete a transaction
pub fn complete_tx<T: ?Sized, C, K>(
pub fn complete_tx<'a, T: ?Sized, C, K>(
wallet: &mut T,
slate: &mut Slate,
participant_id: usize,
context: &Context,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let _ = slate.fill_round_2(
wallet.keychain(),
wallet.keychain()?,
&context.sec_key,
&context.sec_nonce,
participant_id,
)?;
// Final transaction can be built by anyone at this stage
slate.finalize(wallet.keychain())?;
slate.finalize(wallet.keychain()?)?;
Ok(())
}
/// Rollback outputs associated with a transaction in the wallet
pub fn cancel_tx<T: ?Sized, C, K>(
pub fn cancel_tx<'a, T: ?Sized, C, K>(
wallet: &mut T,
parent_key_id: &Identifier,
tx_id: Option<u32>,
tx_slate_id: Option<Uuid>,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut tx_id_string = String::new();
if let Some(tx_id) = tx_id {
@ -281,15 +281,15 @@ where
}
/// Update the stored transaction (this update needs to happen when the TX is finalised)
pub fn update_stored_tx<T: ?Sized, C, K>(
pub fn update_stored_tx<'a, T: ?Sized, C, K>(
wallet: &mut T,
slate: &Slate,
is_invoiced: bool,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
// finalize command
let tx_vec = updater::retrieve_txs(wallet, None, Some(slate.id), None, false)?;
@ -314,11 +314,11 @@ where
}
/// Update the transaction participant messages
pub fn update_message<T: ?Sized, C, K>(wallet: &mut T, slate: &Slate) -> Result<(), Error>
pub fn update_message<'a, T: ?Sized, C, K>(wallet: &mut T, slate: &Slate) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let tx_vec = updater::retrieve_txs(wallet, None, Some(slate.id), None, false)?;
if tx_vec.is_empty() {

View file

@ -34,16 +34,16 @@ use crate::types::{
use crate::{BlockFees, CbData, OutputCommitMapping};
/// Retrieve all of the outputs (doesn't attempt to update from node)
pub fn retrieve_outputs<T: ?Sized, C, K>(
pub fn retrieve_outputs<'a, T: ?Sized, C, K>(
wallet: &mut T,
show_spent: bool,
tx_id: Option<u32>,
parent_key_id: Option<&Identifier>,
) -> Result<Vec<OutputCommitMapping>, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
// just read the wallet here, no need for a write lock
let mut outputs = wallet
@ -68,7 +68,7 @@ where
}
outputs.sort_by_key(|out| out.n_child);
let keychain = wallet.keychain().clone();
let keychain = wallet.keychain()?.clone();
let res = outputs
.into_iter()
@ -87,7 +87,7 @@ where
/// Retrieve all of the transaction entries, or a particular entry
/// if `parent_key_id` is set, only return entries from that key
pub fn retrieve_txs<T: ?Sized, C, K>(
pub fn retrieve_txs<'a, T: ?Sized, C, K>(
wallet: &mut T,
tx_id: Option<u32>,
tx_slate_id: Option<Uuid>,
@ -95,9 +95,9 @@ pub fn retrieve_txs<T: ?Sized, C, K>(
outstanding_only: bool,
) -> Result<Vec<TxLogEntry>, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut txs: Vec<TxLogEntry> = wallet
.tx_log_iter()
@ -131,15 +131,15 @@ where
/// Refreshes the outputs in a wallet with the latest information
/// from a node
pub fn refresh_outputs<T: ?Sized, C, K>(
pub fn refresh_outputs<'a, T: ?Sized, C, K>(
wallet: &mut T,
parent_key_id: &Identifier,
update_all: bool,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let height = wallet.w2n_client().get_chain_height()?;
refresh_output_state(wallet, height, parent_key_id, update_all)?;
@ -148,19 +148,19 @@ where
/// build a local map of wallet outputs keyed by commit
/// and a list of outputs we want to query the node for
pub fn map_wallet_outputs<T: ?Sized, C, K>(
pub fn map_wallet_outputs<'a, T: ?Sized, C, K>(
wallet: &mut T,
parent_key_id: &Identifier,
update_all: bool,
) -> Result<HashMap<pedersen::Commitment, (Identifier, Option<u64>)>, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut wallet_outputs: HashMap<pedersen::Commitment, (Identifier, Option<u64>)> =
HashMap::new();
let keychain = wallet.keychain().clone();
let keychain = wallet.keychain()?.clone();
let unspents: Vec<OutputData> = wallet
.iter()
.filter(|x| x.root_key_id == *parent_key_id && x.status != OutputStatus::Spent)
@ -199,16 +199,16 @@ where
}
/// Cancel transaction and associated outputs
pub fn cancel_tx_and_outputs<T: ?Sized, C, K>(
pub fn cancel_tx_and_outputs<'a, T: ?Sized, C, K>(
wallet: &mut T,
tx: TxLogEntry,
outputs: Vec<OutputData>,
parent_key_id: &Identifier,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let mut batch = wallet.batch()?;
@ -235,7 +235,7 @@ where
}
/// Apply refreshed API output data to the wallet
pub fn apply_api_outputs<T: ?Sized, C, K>(
pub fn apply_api_outputs<'a, T: ?Sized, C, K>(
wallet: &mut T,
wallet_outputs: &HashMap<pedersen::Commitment, (Identifier, Option<u64>)>,
api_outputs: &HashMap<pedersen::Commitment, (String, u64, u64)>,
@ -243,9 +243,9 @@ pub fn apply_api_outputs<T: ?Sized, C, K>(
parent_key_id: &Identifier,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
// now for each commit, find the output in the wallet and the corresponding
// api output (if it exists) and refresh it in-place in the wallet.
@ -315,16 +315,16 @@ where
/// Builds a single api query to retrieve the latest output data from the node.
/// So we can refresh the local wallet outputs.
fn refresh_output_state<T: ?Sized, C, K>(
fn refresh_output_state<'a, T: ?Sized, C, K>(
wallet: &mut T,
height: u64,
parent_key_id: &Identifier,
update_all: bool,
) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
debug!("Refreshing wallet outputs");
@ -342,11 +342,11 @@ where
Ok(())
}
fn clean_old_unconfirmed<T: ?Sized, C, K>(wallet: &mut T, height: u64) -> Result<(), Error>
fn clean_old_unconfirmed<'a, T: ?Sized, C, K>(wallet: &mut T, height: u64) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
if height < 50 {
return Ok(());
@ -371,15 +371,15 @@ where
/// Retrieve summary info about the wallet
/// caller should refresh first if desired
pub fn retrieve_info<T: ?Sized, C, K>(
pub fn retrieve_info<'a, T: ?Sized, C, K>(
wallet: &mut T,
parent_key_id: &Identifier,
minimum_confirmations: u64,
) -> Result<WalletInfo, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let current_height = wallet.last_confirmed_height()?;
let outputs = wallet
@ -434,15 +434,15 @@ where
}
/// Build a coinbase output and insert into wallet
pub fn build_coinbase<T: ?Sized, C, K>(
pub fn build_coinbase<'a, T: ?Sized, C, K>(
wallet: &mut T,
block_fees: &BlockFees,
test_mode: bool,
) -> Result<CbData, Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let (out, kern, block_fees) = receive_coinbase(wallet, block_fees, test_mode)?;
@ -455,15 +455,15 @@ where
//TODO: Split up the output creation and the wallet insertion
/// Build a coinbase output and the corresponding kernel
pub fn receive_coinbase<T: ?Sized, C, K>(
pub fn receive_coinbase<'a, T: ?Sized, C, K>(
wallet: &mut T,
block_fees: &BlockFees,
test_mode: bool,
) -> Result<(Output, TxKernel, BlockFees), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
T: WalletBackend<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
let height = block_fees.height;
let lock_height = height + global::coinbase_maturity();
@ -510,7 +510,7 @@ where
debug!("receive_coinbase: {:?}", block_fees);
let keychain = wallet.keychain();
let keychain = wallet.keychain()?;
let (out, kern) = reward::output(
keychain,
&ProofBuilder::new(keychain),

View file

@ -63,5 +63,5 @@ pub use internal::restore::{check_repair, restore};
pub use types::{
AcctPathMapping, BlockIdentifier, Context, NodeClient, NodeVersionInfo, OutputData,
OutputStatus, TxLogEntry, TxLogEntryType, TxWrapper, WalletBackend, WalletInfo, WalletInst,
WalletOutputBatch,
WalletLCProvider, WalletOutputBatch,
};

View file

@ -19,10 +19,11 @@ use crate::error::{Error, ErrorKind};
use crate::grin_core::core::hash::Hash;
use crate::grin_core::core::Transaction;
use crate::grin_core::libtx::{aggsig, secp_ser};
use crate::grin_core::ser;
use crate::grin_core::{global, ser};
use crate::grin_keychain::{Identifier, Keychain};
use crate::grin_util::secp::key::{PublicKey, SecretKey};
use crate::grin_util::secp::{self, pedersen, Secp256k1};
use crate::grin_util::ZeroingString;
use crate::slate::ParticipantMessages;
use chrono::prelude::*;
use failure::ResultExt;
@ -33,37 +34,92 @@ use std::fmt;
use uuid::Uuid;
/// Combined trait to allow dynamic wallet dispatch
pub trait WalletInst<C, K>: WalletBackend<C, K> + Send + Sync + 'static
pub trait WalletInst<'a, L, C, K>: Send + Sync
where
C: NodeClient,
K: Keychain,
L: WalletLCProvider<'a, C, K> + Send + Sync,
C: NodeClient + 'a,
K: Keychain + 'a,
{
/// Return the stored instance
fn lc_provider(&mut self) -> Result<&mut (dyn WalletLCProvider<'a, C, K> + 'a), Error>;
}
impl<T, C, K> WalletInst<C, K> for T
/// Trait for a provider of wallet lifecycle methods
pub trait WalletLCProvider<'a, C, K>: Send + Sync
where
T: WalletBackend<C, K> + Send + Sync + 'static,
C: NodeClient,
K: Keychain,
C: NodeClient + 'a,
K: Keychain + 'a,
{
/// Sets the top level system wallet directory
/// default is assumed to be ~/.grin/main/wallet_data (or floonet equivalent)
fn set_wallet_directory(&mut self, dir: &str);
/// Output a grin-wallet.toml file into the current top-level system wallet directory
fn create_config(&self, chain_type: &global::ChainTypes, file_name: &str) -> Result<(), Error>;
///
fn create_wallet(
&mut self,
name: Option<&str>,
mnemonic: Option<ZeroingString>,
mnemonic_length: usize,
password: ZeroingString,
) -> Result<(), Error>;
///
fn open_wallet(&mut self, name: Option<&str>, password: ZeroingString) -> Result<(), Error>;
///
fn close_wallet(&mut self, name: Option<&str>) -> Result<(), Error>;
/// whether a wallet exists at the given directory
fn wallet_exists(&self, name: Option<&str>) -> Result<bool, Error>;
/// return mnemonic of given wallet
fn get_mnemonic(
&self,
name: Option<&str>,
password: ZeroingString,
) -> Result<ZeroingString, Error>;
/// Check whether a provided mnemonic string is valid
fn validate_mnemonic(&self, mnemonic: ZeroingString) -> Result<(), Error>;
/// Recover a seed from phrase, without destroying existing data
/// should back up seed
fn recover_from_mnemonic(
&self,
mnemonic: ZeroingString,
password: ZeroingString,
) -> Result<(), Error>;
/// changes password
fn change_password(&self, old: String, new: String) -> Result<(), Error>;
/// deletes wallet
fn delete_wallet(&self, name: Option<String>, password: String) -> Result<(), Error>;
/// return wallet instance
fn wallet_inst(&mut self) -> Result<&mut Box<dyn WalletBackend<'a, C, K> + 'a>, Error>;
}
/// TODO:
/// Wallets should implement this backend for their storage. All functions
/// here expect that the wallet instance has instantiated itself or stored
/// whatever credentials it needs
pub trait WalletBackend<C, K>
pub trait WalletBackend<'ck, C, K>: Send + Sync
where
C: NodeClient,
K: Keychain,
C: NodeClient + 'ck,
K: Keychain + 'ck,
{
/// Initialize with whatever stored credentials we have
fn open_with_credentials(&mut self) -> Result<(), Error>;
/// Set the keychain, which should already be initialized
fn set_keychain(&mut self, k: Box<K>);
/// Close wallet and remove any stored credentials (TBD)
fn close(&mut self) -> Result<(), Error>;
/// Return the keychain being used
fn keychain(&mut self) -> &mut K;
fn keychain(&mut self) -> Result<&mut K, Error>;
/// Return the client being used to communicate with the node
fn w2n_client(&mut self) -> &mut C;
@ -205,7 +261,7 @@ where
/// Encapsulate all wallet-node communication functions. No functions within libwallet
/// should care about communication details
pub trait NodeClient: Sync + Send + Clone {
pub trait NodeClient: Send + Sync + Clone {
/// Return the URL of the check node
fn node_url(&self) -> &str;

View file

@ -1,63 +0,0 @@
// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// Grin configuration file output command
use crate::config::{config, GlobalWalletConfig, GRIN_WALLET_DIR};
use crate::core::global;
use std::env;
/// Create a config file in the current directory
pub fn config_command_wallet(chain_type: &global::ChainTypes, file_name: &str) {
let mut default_config = GlobalWalletConfig::for_chain(chain_type);
let current_dir = env::current_dir().unwrap_or_else(|e| {
panic!("Error creating config file: {}", e);
});
let mut config_file_name = current_dir.clone();
config_file_name.push(file_name);
let mut data_dir_name = current_dir.clone();
data_dir_name.push(GRIN_WALLET_DIR);
if config_file_name.exists() && data_dir_name.exists() {
panic!(
"{} already exists in the target directory. Please remove it first",
file_name
);
}
// just leave as is if file exists but there's no data dir
if config_file_name.exists() {
return;
}
default_config.update_paths(&current_dir);
default_config
.write_to_file(config_file_name.to_str().unwrap())
.unwrap_or_else(|e| {
panic!("Error creating config file: {}", e);
});
println!(
"File {} configured and created",
config_file_name.to_str().unwrap(),
);
let mut api_secret_path = current_dir.clone();
api_secret_path.push(config::API_SECRET_FILE_NAME);
if !api_secret_path.exists() {
config::init_api_secret(&api_secret_path).unwrap();
} else {
config::check_api_secret(&api_secret_path).unwrap();
}
}

View file

@ -12,10 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
mod config;
mod wallet;
mod wallet_args;
mod wallet_tests;
pub use self::config::config_command_wallet;
pub use self::wallet::{seed_exists, wallet_command};
pub use self::wallet::wallet_command;

View file

@ -15,34 +15,14 @@
use crate::cmd::wallet_args;
use crate::config::GlobalWalletConfig;
use clap::ArgMatches;
use grin_wallet_config::WalletConfig;
use grin_wallet_impls::{HTTPNodeClient, WalletSeed, SEED_FILE};
use grin_wallet_impls::HTTPNodeClient;
use grin_wallet_libwallet::NodeClient;
use semver::Version;
use std::path::PathBuf;
use std::thread;
use std::time::Duration;
const MIN_COMPAT_NODE_VERSION: &str = "2.0.0-beta.1";
pub fn _init_wallet_seed(wallet_config: WalletConfig, password: &str) {
if let Err(_) = WalletSeed::from_file(&wallet_config, password) {
WalletSeed::init_file(&wallet_config, 32, None, password)
.expect("Failed to create wallet seed file.");
};
}
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(SEED_FILE);
if data_file_dir.exists() {
true
} else {
false
}
}
pub fn wallet_command(wallet_args: &ArgMatches<'_>, config: GlobalWalletConfig) -> i32 {
// just get defaults from the global config
let wallet_config = config.members.unwrap().wallet;

View file

@ -13,6 +13,7 @@
// limitations under the License.
use crate::api::TLSConfig;
use crate::config::GRIN_WALLET_DIR;
use crate::util::file::get_first_line;
use crate::util::{Mutex, ZeroingString};
/// Argument parsing and error handling for wallet commands
@ -21,24 +22,20 @@ use failure::Fail;
use grin_wallet_config::WalletConfig;
use grin_wallet_controller::command;
use grin_wallet_controller::{Error, ErrorKind};
use grin_wallet_impls::{instantiate_wallet, WalletSeed};
use grin_wallet_libwallet::{IssueInvoiceTxArgs, NodeClient, WalletInst};
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl};
use grin_wallet_impls::{PathToSlate, SlateGetter as _};
use grin_wallet_libwallet::Slate;
use grin_wallet_libwallet::{IssueInvoiceTxArgs, NodeClient, WalletInst, WalletLCProvider};
use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_core::core::amount_to_hr_string;
use grin_wallet_util::grin_core::global;
use grin_wallet_util::grin_keychain as keychain;
use linefeed::terminal::Signal;
use linefeed::{Interface, ReadResult};
use rpassword;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::sync::Arc;
// shut up test compilation warnings
#[cfg(not(test))]
use grin_wallet_impls::FileWalletCommAdapter;
#[cfg(not(test))]
use grin_wallet_libwallet::Slate;
#[cfg(not(test))]
use grin_wallet_util::grin_core::core::amount_to_hr_string;
// define what to do on argument error
macro_rules! arg_parse {
( $r:expr ) => {
@ -116,7 +113,15 @@ fn prompt_replace_seed() -> Result<bool, ParseError> {
}
}
fn prompt_recovery_phrase() -> Result<ZeroingString, ParseError> {
fn prompt_recovery_phrase<L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>,
) -> Result<ZeroingString, ParseError>
where
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>,
L: WalletLCProvider<'static, C, K>,
C: NodeClient + 'static,
K: keychain::Keychain + 'static,
{
let interface = Arc::new(Interface::new("recover")?);
let mut phrase = ZeroingString::from("");
interface.set_report_signal(Signal::Interrupt, true);
@ -133,7 +138,11 @@ fn prompt_recovery_phrase() -> Result<ZeroingString, ParseError> {
}
}
ReadResult::Input(line) => {
if WalletSeed::from_mnemonic(&line).is_ok() {
let mut w_lock = wallet.lock();
let p = w_lock.lc_provider().unwrap();
if p.validate_mnemonic(ZeroingString::from(line.clone()))
.is_ok()
{
phrase = ZeroingString::from(line);
break;
} else {
@ -148,7 +157,6 @@ fn prompt_recovery_phrase() -> Result<ZeroingString, ParseError> {
Ok(phrase)
}
#[cfg(not(test))]
fn prompt_pay_invoice(slate: &Slate, method: &str, dest: &str) -> Result<bool, ParseError> {
let interface = Arc::new(Interface::new("pay")?);
let amount = amount_to_hr_string(slate.amount, false);
@ -207,27 +215,21 @@ fn prompt_pay_invoice(slate: &Slate, method: &str, dest: &str) -> Result<bool, P
// instantiate wallet (needed by most functions)
pub fn inst_wallet(
pub fn inst_wallet<L, C, K>(
config: WalletConfig,
g_args: &command::GlobalArgs,
node_client: impl NodeClient + 'static,
) -> Result<Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>, ParseError> {
let passphrase = prompt_password(&g_args.password);
let res = instantiate_wallet(config.clone(), node_client, &passphrase, &g_args.account);
match res {
Ok(p) => Ok(p),
Err(e) => {
let msg = {
match e.kind() {
grin_wallet_impls::ErrorKind::Encryption => {
format!("Error decrypting wallet seed (check provided password)")
}
_ => format!("Error instantiating wallet: {}", e),
}
};
Err(ParseError::ArgumentError(msg))
}
}
node_client: C,
) -> Result<Arc<Mutex<Box<WalletInst<'static, L, C, K>>>>, ParseError>
where
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>,
L: WalletLCProvider<'static, C, K>,
C: NodeClient + 'static,
K: keychain::Keychain + 'static,
{
let mut wallet = Box::new(DefaultWalletImpl::<'static, C>::new(node_client.clone()).unwrap())
as Box<WalletInst<'static, L, C, K>>;
let lc = wallet.lc_provider().unwrap();
lc.set_wallet_directory(&config.data_file_dir);
Ok(Arc::new(Mutex::new(wallet)))
}
// parses a required value, or throws error with message otherwise
@ -283,30 +285,42 @@ pub fn parse_global_args(
}
};
let chain_type = match config.chain_type.clone() {
None => {
let param_ref = global::CHAIN_TYPE.read();
param_ref.clone()
}
Some(c) => c,
};
Ok(command::GlobalArgs {
account: account.to_owned(),
show_spent: show_spent,
chain_type: chain_type,
node_api_secret: node_api_secret,
password: password,
tls_conf: tls_conf,
})
}
pub fn parse_init_args(
pub fn parse_init_args<L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>,
config: &WalletConfig,
g_args: &command::GlobalArgs,
args: &ArgMatches,
) -> Result<command::InitArgs, ParseError> {
if let Err(e) = WalletSeed::seed_file_exists(config) {
let msg = format!("Not creating wallet - {}", e.inner);
return Err(ParseError::ArgumentError(msg));
}
) -> Result<command::InitArgs, ParseError>
where
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>,
L: WalletLCProvider<'static, C, K>,
C: NodeClient + 'static,
K: keychain::Keychain + 'static,
{
let list_length = match args.is_present("short_wordlist") {
false => 32,
true => 16,
};
let recovery_phrase = match args.is_present("recover") {
true => Some(prompt_recovery_phrase()?),
true => Some(prompt_recovery_phrase(wallet)?),
false => None,
};
@ -330,17 +344,25 @@ pub fn parse_init_args(
})
}
pub fn parse_recover_args(
config: &WalletConfig,
pub fn parse_recover_args<L, C, K>(
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>,
g_args: &command::GlobalArgs,
args: &ArgMatches,
) -> Result<command::RecoverArgs, ParseError> {
) -> Result<command::RecoverArgs, ParseError>
where
DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>,
L: WalletLCProvider<'static, C, K>,
C: NodeClient + 'static,
K: keychain::Keychain + 'static,
{
let (passphrase, recovery_phrase) = {
match args.is_present("display") {
true => (prompt_password(&g_args.password), None),
false => {
let cont = {
if command::wallet_seed_exists(config).is_err() {
let mut w_lock = wallet.lock();
let p = w_lock.lc_provider().unwrap();
if p.wallet_exists(None).unwrap() {
prompt_replace_seed()?
} else {
true
@ -349,7 +371,7 @@ pub fn parse_recover_args(
if !cont {
return Err(ParseError::CancelledError);
}
let phrase = prompt_recovery_phrase()?;
let phrase = prompt_recovery_phrase(wallet.clone())?;
println!("Please provide a new password for the recovered wallet");
(prompt_password_confirm(), Some(phrase.to_owned()))
}
@ -363,15 +385,8 @@ pub fn parse_recover_args(
pub fn parse_listen_args(
config: &mut WalletConfig,
g_args: &mut command::GlobalArgs,
args: &ArgMatches,
) -> Result<command::ListenArgs, ParseError> {
// listen args
let pass = match g_args.password.clone() {
Some(p) => Some(p.to_owned()),
None => Some(prompt_password(&None)),
};
g_args.password = pass;
if let Some(port) = args.value_of("port") {
config.api_listen_port = port.parse().unwrap();
}
@ -621,18 +636,17 @@ pub fn parse_process_invoice_args(
// file input only
let tx_file = parse_required(args, "input")?;
// Now we need to prompt the user whether they want to do this,
// which requires reading the slate
#[cfg(not(test))]
let adapter = FileWalletCommAdapter::new();
#[cfg(not(test))]
let slate = match adapter.receive_tx_async(&tx_file) {
Ok(s) => s,
Err(e) => return Err(ParseError::ArgumentError(format!("{}", e))),
};
if cfg!(not(test)) {
// Now we need to prompt the user whether they want to do this,
// which requires reading the slate
#[cfg(not(test))] // don't prompt during automated testing
prompt_pay_invoice(&slate, method, dest)?;
let slate = match PathToSlate((&tx_file).into()).get_tx() {
Ok(s) => s,
Err(e) => return Err(ParseError::ArgumentError(format!("{}", e))),
};
prompt_pay_invoice(&slate, method, dest)?;
}
Ok(command::ProcessInvoiceArgs {
message: message,
@ -736,11 +750,14 @@ pub fn parse_cancel_args(args: &ArgMatches) -> Result<command::CancelArgs, Parse
})
}
pub fn wallet_command(
pub fn wallet_command<C>(
wallet_args: &ArgMatches,
mut wallet_config: WalletConfig,
mut node_client: impl NodeClient + 'static,
) -> Result<String, Error> {
mut node_client: C,
) -> Result<String, Error>
where
C: NodeClient + 'static + Clone,
{
if let Some(t) = wallet_config.chain_type.clone() {
core::global::set_mining_mode(t);
}
@ -762,68 +779,104 @@ pub fn wallet_command(
node_client.set_node_url(&wallet_config.check_node_api_http_addr);
node_client.set_node_api_secret(global_wallet_args.node_api_secret.clone());
// closure to instantiate wallet as needed by each subcommand
let inst_wallet = || {
let res = inst_wallet(wallet_config.clone(), &global_wallet_args, node_client);
res.unwrap_or_else(|e| {
// legacy hack to avoid the need for changes in existing grin-wallet.toml files
// remove `wallet_data` from end of path as
// new lifecycle provider assumes grin_wallet.toml is in root of data directory
let mut top_level_wallet_dir = PathBuf::from(wallet_config.clone().data_file_dir);
if top_level_wallet_dir.ends_with(GRIN_WALLET_DIR) {
top_level_wallet_dir.pop();
wallet_config.data_file_dir = top_level_wallet_dir.to_str().unwrap().into();
}
// Instantiate wallet (doesn't open the wallet)
let wallet =
inst_wallet::<DefaultLCProvider<C, keychain::ExtKeychain>, C, keychain::ExtKeychain>(
wallet_config.clone(),
node_client,
)
.unwrap_or_else(|e| {
println!("{}", e);
std::process::exit(1);
})
};
});
{
let mut wallet_lock = wallet.lock();
let lc = wallet_lock.lc_provider().unwrap();
lc.set_wallet_directory(&wallet_config.data_file_dir);
}
// don't open wallet for certain lifecycle commands
match wallet_args.subcommand() {
("init", Some(_)) => {}
("recover", _) => {}
_ => {
let mut wallet_lock = wallet.lock();
let lc = wallet_lock.lc_provider().unwrap();
lc.open_wallet(None, prompt_password(&global_wallet_args.password))?;
if let Some(account) = wallet_args.value_of("account") {
let wallet_inst = lc.wallet_inst()?;
wallet_inst.set_parent_key_id_by_name(account)?;
}
}
}
let res = match wallet_args.subcommand() {
("init", Some(args)) => {
let a = arg_parse!(parse_init_args(&wallet_config, &global_wallet_args, &args));
command::init(&global_wallet_args, a)
}
("recover", Some(args)) => {
let a = arg_parse!(parse_recover_args(
let a = arg_parse!(parse_init_args(
wallet.clone(),
&wallet_config,
&global_wallet_args,
&args
));
command::recover(&wallet_config, a)
command::init(wallet, &global_wallet_args, a)
}
("recover", Some(args)) => {
let a = arg_parse!(parse_recover_args(
wallet.clone(),
&global_wallet_args,
&args
));
command::recover(wallet, a)
}
("listen", Some(args)) => {
let mut c = wallet_config.clone();
let mut g = global_wallet_args.clone();
let a = arg_parse!(parse_listen_args(&mut c, &mut g, &args));
command::listen(&c, &a, &g)
let a = arg_parse!(parse_listen_args(&mut c, &args));
command::listen(wallet, &c, &a, &global_wallet_args.clone())
}
("owner_api", Some(_)) => {
let mut g = global_wallet_args.clone();
g.tls_conf = None;
command::owner_api(inst_wallet(), &wallet_config, &g)
command::owner_api(wallet, &wallet_config, &g)
}
("web", Some(_)) => command::owner_api(inst_wallet(), &wallet_config, &global_wallet_args),
("web", Some(_)) => command::owner_api(wallet, &wallet_config, &global_wallet_args),
("account", Some(args)) => {
let a = arg_parse!(parse_account_args(&args));
command::account(inst_wallet(), a)
command::account(wallet, a)
}
("send", Some(args)) => {
let a = arg_parse!(parse_send_args(&args));
command::send(
inst_wallet(),
wallet,
a,
wallet_config.dark_background_color_scheme.unwrap_or(true),
)
}
("receive", Some(args)) => {
let a = arg_parse!(parse_receive_args(&args));
command::receive(inst_wallet(), &global_wallet_args, a)
command::receive(wallet, &global_wallet_args, a)
}
("finalize", Some(args)) => {
let a = arg_parse!(parse_finalize_args(&args));
command::finalize(inst_wallet(), a)
command::finalize(wallet, a)
}
("invoice", Some(args)) => {
let a = arg_parse!(parse_issue_invoice_args(&args));
command::issue_invoice_tx(inst_wallet(), a)
command::issue_invoice_tx(wallet, a)
}
("pay", Some(args)) => {
let a = arg_parse!(parse_process_invoice_args(&args));
command::process_invoice(
inst_wallet(),
wallet,
a,
wallet_config.dark_background_color_scheme.unwrap_or(true),
)
@ -831,21 +884,21 @@ pub fn wallet_command(
("info", Some(args)) => {
let a = arg_parse!(parse_info_args(&args));
command::info(
inst_wallet(),
wallet,
&global_wallet_args,
a,
wallet_config.dark_background_color_scheme.unwrap_or(true),
)
}
("outputs", Some(_)) => command::outputs(
inst_wallet(),
wallet,
&global_wallet_args,
wallet_config.dark_background_color_scheme.unwrap_or(true),
),
("txs", Some(args)) => {
let a = arg_parse!(parse_txs_args(&args));
command::txs(
inst_wallet(),
wallet,
&global_wallet_args,
a,
wallet_config.dark_background_color_scheme.unwrap_or(true),
@ -853,16 +906,16 @@ pub fn wallet_command(
}
("repost", Some(args)) => {
let a = arg_parse!(parse_repost_args(&args));
command::repost(inst_wallet(), a)
command::repost(wallet, a)
}
("cancel", Some(args)) => {
let a = arg_parse!(parse_cancel_args(&args));
command::cancel(inst_wallet(), a)
command::cancel(wallet, a)
}
("restore", Some(_)) => command::restore(inst_wallet()),
("restore", Some(_)) => command::restore(wallet),
("check", Some(args)) => {
let a = arg_parse!(parse_check_args(&args));
command::check_repair(inst_wallet(), a)
command::check_repair(wallet, a)
}
_ => {
let msg = format!("Unknown wallet command, use 'grin help wallet' for details");

View file

@ -20,15 +20,16 @@ mod wallet_tests {
use grin_wallet_impls::test_framework::{self, LocalWalletClient, WalletProxy};
use clap::{App, ArgMatches};
use std::path::PathBuf;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use std::{env, fs};
use util::Mutex;
use util::{Mutex, ZeroingString};
use grin_wallet_config::{GlobalWalletConfig, WalletConfig};
use grin_wallet_impls::{LMDBBackend, WalletSeed};
use grin_wallet_libwallet::{WalletBackend, WalletInst};
use grin_wallet_config::{GlobalWalletConfig, WalletConfig, GRIN_WALLET_DIR};
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl};
use grin_wallet_libwallet::WalletInst;
use grin_wallet_util::grin_core::global::{self, ChainTypes};
use grin_wallet_util::grin_keychain::ExtKeychain;
@ -121,15 +122,45 @@ mod wallet_tests {
node_client: LocalWalletClient,
passphrase: &str,
account: &str,
) -> Result<Arc<Mutex<WalletInst<LocalWalletClient, ExtKeychain>>>, grin_wallet_controller::Error>
{
) -> Result<
Arc<
Mutex<
Box<
WalletInst<
'static,
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>,
>,
>,
grin_wallet_controller::Error,
> {
wallet_config.chain_type = None;
// First test decryption, so we can abort early if we have the wrong password
let _ = WalletSeed::from_file(&wallet_config, passphrase)?;
let mut db_wallet = LMDBBackend::new(wallet_config.clone(), passphrase, node_client)?;
db_wallet.set_parent_key_id_by_name(account)?;
info!("Using LMDB Backend for wallet");
Ok(Arc::new(Mutex::new(db_wallet)))
let mut wallet = Box::new(DefaultWalletImpl::<LocalWalletClient>::new(node_client).unwrap())
as Box<
WalletInst<
DefaultLCProvider<'static, LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
>,
>;
let lc = wallet.lc_provider().unwrap();
// legacy hack to avoid the need for changes in existing grin-wallet.toml files
// remove `wallet_data` from end of path as
// new lifecycle provider assumes grin_wallet.toml is in root of data directory
let mut top_level_wallet_dir = PathBuf::from(wallet_config.clone().data_file_dir);
if top_level_wallet_dir.ends_with(GRIN_WALLET_DIR) {
top_level_wallet_dir.pop();
wallet_config.data_file_dir = top_level_wallet_dir.to_str().unwrap().into();
}
lc.set_wallet_directory(&wallet_config.data_file_dir);
lc.open_wallet(None, ZeroingString::from(passphrase))
.unwrap();
let wallet_inst = lc.wallet_inst()?;
wallet_inst.set_parent_key_id_by_name(account)?;
Ok(Arc::new(Mutex::new(wallet)))
}
fn execute_command(
@ -151,8 +182,11 @@ mod wallet_tests {
fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::Error> {
setup(test_dir);
// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> =
WalletProxy::new(test_dir);
let mut wallet_proxy: WalletProxy<
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient,
ExtKeychain,
> = WalletProxy::new(test_dir);
let chain = wallet_proxy.chain.clone();
// load app yaml. If it don't exist, just say so and exit

View file

@ -26,7 +26,7 @@ use grin_wallet_config as config;
use grin_wallet_util::grin_api as api;
use grin_wallet_util::grin_core as core;
use grin_wallet_util::grin_util as util;
use std::process::exit;
use std::env;
mod cmd;
@ -79,43 +79,42 @@ fn real_main() -> i32 {
global::ChainTypes::Mainnet
};
// Deal with configuration file creation
let mut current_dir = None;
// special cases for certain lifecycle commands
match args.subcommand() {
// wallet init command should spit out its config file then continue
// (if desired)
("init", Some(init_args)) => {
if init_args.is_present("here") {
cmd::config_command_wallet(&chain_type, config::WALLET_CONFIG_FILE_NAME);
current_dir = Some(env::current_dir().unwrap_or_else(|e| {
panic!("Error creating config file: {}", e);
}));
}
}
("recover", _) => {}
_ => {}
}
// Load relevant config, try and load a wallet config file
let mut w = config::initial_setup_wallet(&chain_type).unwrap_or_else(|e| {
// Use defaults for configuration if config file not found anywhere
let mut config = config::initial_setup_wallet(&chain_type, current_dir).unwrap_or_else(|e| {
panic!("Error loading wallet configuration: {}", e);
});
if !cmd::seed_exists(w.members.as_ref().unwrap().wallet.clone()) {
if "init" == args.subcommand().0 || "recover" == args.subcommand().0 {
} else {
println!("Wallet seed file doesn't exist. Run `grin-wallet init` first");
exit(1);
}
}
config.members.as_mut().unwrap().wallet.chain_type = Some(chain_type);
// Load logging config
let l = w.members.as_mut().unwrap().logging.clone().unwrap();
let l = config.members.as_mut().unwrap().logging.clone().unwrap();
init_logger(Some(l));
info!(
"Using wallet configuration file at {}",
w.config_file_path.as_ref().unwrap().to_str().unwrap()
config.config_file_path.as_ref().unwrap().to_str().unwrap()
);
log_build_info();
global::set_mining_mode(
w.members
config
.members
.as_ref()
.unwrap()
.wallet
@ -125,5 +124,5 @@ fn real_main() -> i32 {
.clone(),
);
cmd::wallet_command(&args, w)
cmd::wallet_command(&args, config)
}

View file

@ -1,6 +1,6 @@
[package]
name = "grin_wallet_util"
version = "2.0.1-beta.1"
version = "2.1.0-beta.1"
authors = ["Grin Developers <mimblewimble@lists.launchpad.net>"]
description = "Util, for generic utilities and to re-export grin crates"
license = "Apache-2.0"