mirror of
https://github.com/mimblewimble/grin-wallet.git
synced 2025-01-20 19:11:09 +03:00
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:
parent
7b6d597d39
commit
c40e8a915b
54 changed files with 2405 additions and 1942 deletions
57
Cargo.lock
generated
57
Cargo.lock
generated
|
@ -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)",
|
||||
|
|
14
Cargo.toml
14
Cargo.toml
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
231
api/src/owner.rs
231
api/src/owner.rs
|
@ -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));
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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 ...",);
|
||||
|
|
|
@ -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,
|
||||
{
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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 || {
|
||||
|
|
138
controller/tests/common/mod.rs
Normal file
138
controller/tests/common/mod.rs
Normal 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))
|
||||
}
|
|
@ -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)?;
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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")?;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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 || {
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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!();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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!();
|
||||
}
|
||||
}
|
|
@ -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>,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
230
impls/src/lifecycle/default.rs
Normal file
230
impls/src/lifecycle/default.rs
Normal 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()),
|
||||
}
|
||||
}
|
||||
}
|
18
impls/src/lifecycle/mod.rs
Normal file
18
impls/src/lifecycle/mod.rs
Normal 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;
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -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" }
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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(¤t_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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue