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 f52a8d2c7cdfb8583af5716ad621eb560811d6ee.

* 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
54 changed files with 2405 additions and 1942 deletions
+5 -5
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"
+74 -53
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));
};
}
+70 -36
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;
+2
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;
+118 -113
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));
};
}
+68 -35
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())