mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
Provide wallet 'plugin' architecture (#1983)
* remove receive_coinbase fn from wallet to wallet client * start moving clients into separate mod * rustfmt * move wallet client + start listener into wallet 'clients' * rustfmt * refactor API to make it more modular and completely decouple sending * rustfmt * further decouple API from sending methods * rustfmt * remove wallet to wallet client trait * rustfmt * rename / refactor client + adapters * rustfmt * add adapter concept * add midding node_clients dir * add file and null adapters * rustfmt * remove receive from owner api * factor out receiving slates into trait * rustfmt * adding listen trait * rustfmt * change listener to use trait * rustfmt * add test for file-based exchange * replace http api send command * rustfmt * move controller out of libwallet and into top-level wallet dir * rustfmt * add moved controller
This commit is contained in:
parent
66acee8f71
commit
5ba163fa66
31 changed files with 1145 additions and 976 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,6 +2,7 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.grin*
|
.grin*
|
||||||
node*
|
node*
|
||||||
|
!node_clients
|
||||||
target
|
target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
*.iml
|
*.iml
|
||||||
|
|
|
@ -92,8 +92,8 @@ impl From<Context<ErrorKind>> for Error {
|
||||||
|
|
||||||
/// TLS config
|
/// TLS config
|
||||||
pub struct TLSConfig {
|
pub struct TLSConfig {
|
||||||
certificate: String,
|
pub certificate: String,
|
||||||
private_key: String,
|
pub private_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TLSConfig {
|
impl TLSConfig {
|
||||||
|
|
|
@ -24,7 +24,7 @@ the code is organized into the following components (from highest-level to lowes
|
||||||
* **libTx** - Library that provides lower-level transaction building, rangeproof and signing functions, highly-reusable by wallet implementors.
|
* **libTx** - Library that provides lower-level transaction building, rangeproof and signing functions, highly-reusable by wallet implementors.
|
||||||
* **Wallet Traits** - A set of generic traits defined within libWallet and the `keychain` crate . A wallet implementation such as Grin's current
|
* **Wallet Traits** - A set of generic traits defined within libWallet and the `keychain` crate . A wallet implementation such as Grin's current
|
||||||
default only needs to implement these traits in order to provide a wallet:
|
default only needs to implement these traits in order to provide a wallet:
|
||||||
* **WalletToNodeClient** - Defines communication between the wallet, a running grin node and/or other wallets
|
* **NodeClient** - Defines communication between the wallet, a running grin node and/or other wallets
|
||||||
* **WalletBackend** - Defines the storage implementation of the wallet
|
* **WalletBackend** - Defines the storage implementation of the wallet
|
||||||
* **KeyChain** - Defines key derivation operations
|
* **KeyChain** - Defines key derivation operations
|
||||||
|
|
||||||
|
@ -66,22 +66,22 @@ pub fn retrieve_outputs<T: ?Sized, C, K>(
|
||||||
) -> Result<Vec<OutputData>, Error>
|
) -> Result<Vec<OutputData>, Error>
|
||||||
where
|
where
|
||||||
!·T: WalletBackend<C, K>,
|
!·T: WalletBackend<C, K>,
|
||||||
!·C: WalletToNodeClient,
|
!·C: NodeClient,
|
||||||
!·K: Keychain,
|
!·K: Keychain,
|
||||||
{
|
{
|
||||||
```
|
```
|
||||||
|
|
||||||
With `T` in this instance being a class that implements the `WalletBackend` trait, which is further parameterized with implementations of
|
With `T` in this instance being a class that implements the `WalletBackend` trait, which is further parameterized with implementations of
|
||||||
`WalletToNodeClient` and `Keychain`.
|
`NodeClient` and `Keychain`.
|
||||||
|
|
||||||
There is currently only a single implementation of the Keychain trait within the Grin code, in the `keychain` crate exported as `ExtKeyChain`.
|
There is currently only a single implementation of the Keychain trait within the Grin code, in the `keychain` crate exported as `ExtKeyChain`.
|
||||||
The `Keychain` trait makes several assumptions about the underlying implementation, particularly that it will adhere to a
|
The `Keychain` trait makes several assumptions about the underlying implementation, particularly that it will adhere to a
|
||||||
[BIP-38 style](https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki) 'master key -> child key' model.
|
[BIP-38 style](https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki) 'master key -> child key' model.
|
||||||
|
|
||||||
There are two implementations of `WalletToNodeClient` within the code, the main version being the `HTTPWalletToNodeClient` found within `wallet/src/client.rs` and
|
There are two implementations of `NodeClient` within the code, the main version being the `HTTPNodeClient` found within `wallet/src/client.rs` and
|
||||||
the seconds a test client that communicates with an in-process instance of a chain. The WalletToNodeClient isolates all network calls, so upgrading wallet
|
the seconds a test client that communicates with an in-process instance of a chain. The NodeClient isolates all network calls, so upgrading wallet
|
||||||
communication from the current simple http interaction to a more secure protocol (or allowing for many options) should be a simple
|
communication from the current simple http interaction to a more secure protocol (or allowing for many options) should be a simple
|
||||||
matter of dropping in different `WalletToNodeClient` implementations.
|
matter of dropping in different `NodeClient` implementations.
|
||||||
|
|
||||||
There are also two implementations of `WalletBackend` within the code at the base of the `wallet` crate. `LMDBBackend` found within
|
There are also two implementations of `WalletBackend` within the code at the base of the `wallet` crate. `LMDBBackend` found within
|
||||||
`wallet/src/lmdb_wallet.rs` is the main implementation, and is now used by all grin wallet commands. The earlier `FileWallet` still exists
|
`wallet/src/lmdb_wallet.rs` is the main implementation, and is now used by all grin wallet commands. The earlier `FileWallet` still exists
|
||||||
|
|
|
@ -40,7 +40,7 @@ folder "Provided by Grin" as services {
|
||||||
package "Traits Implemented by Wallets" as traits {
|
package "Traits Implemented by Wallets" as traits {
|
||||||
database "WalletBackend" as wallet_backend
|
database "WalletBackend" as wallet_backend
|
||||||
database "KeyChain" as keychain
|
database "KeyChain" as keychain
|
||||||
component "WalletToNodeClient" as wallet_client
|
component "NodeClient" as wallet_client
|
||||||
}
|
}
|
||||||
|
|
||||||
note left of wallet_client
|
note left of wallet_client
|
||||||
|
|
|
@ -30,7 +30,7 @@ use std::{fs, thread, time};
|
||||||
use util::Mutex;
|
use util::Mutex;
|
||||||
|
|
||||||
use framework::keychain::Keychain;
|
use framework::keychain::Keychain;
|
||||||
use wallet::{HTTPWalletToNodeClient, HTTPWalletToWalletClient, LMDBBackend, WalletConfig};
|
use wallet::{HTTPNodeClient, HTTPWalletCommAdapter, LMDBBackend, WalletCommAdapter, WalletConfig};
|
||||||
|
|
||||||
/// Just removes all results from previous runs
|
/// Just removes all results from previous runs
|
||||||
pub fn clean_all_output(test_name_dir: &str) {
|
pub fn clean_all_output(test_name_dir: &str) {
|
||||||
|
@ -265,27 +265,19 @@ impl LocalServerContainer {
|
||||||
let _ = fs::create_dir_all(self.wallet_config.clone().data_file_dir);
|
let _ = fs::create_dir_all(self.wallet_config.clone().data_file_dir);
|
||||||
let r = wallet::WalletSeed::init_file(&self.wallet_config);
|
let r = wallet::WalletSeed::init_file(&self.wallet_config);
|
||||||
|
|
||||||
let client_n =
|
let client_n = HTTPNodeClient::new(&self.wallet_config.check_node_api_http_addr, None);
|
||||||
HTTPWalletToNodeClient::new(&self.wallet_config.check_node_api_http_addr, None);
|
|
||||||
let client_w = HTTPWalletToWalletClient::new();
|
|
||||||
|
|
||||||
if let Err(_e) = r {
|
if let Err(_e) = r {
|
||||||
//panic!("Error initializing wallet seed: {}", e);
|
//panic!("Error initializing wallet seed: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
let wallet: LMDBBackend<
|
let wallet: LMDBBackend<HTTPNodeClient, keychain::ExtKeychain> =
|
||||||
HTTPWalletToNodeClient,
|
LMDBBackend::new(self.wallet_config.clone(), "", client_n).unwrap_or_else(|e| {
|
||||||
HTTPWalletToWalletClient,
|
|
||||||
keychain::ExtKeychain,
|
|
||||||
> =
|
|
||||||
LMDBBackend::new(self.wallet_config.clone(), "", client_n, client_w).unwrap_or_else(
|
|
||||||
|e| {
|
|
||||||
panic!(
|
panic!(
|
||||||
"Error creating wallet: {:?} Config: {:?}",
|
"Error creating wallet: {:?} Config: {:?}",
|
||||||
e, self.wallet_config
|
e, self.wallet_config
|
||||||
)
|
)
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
wallet::controller::foreign_listener(
|
wallet::controller::foreign_listener(
|
||||||
Arc::new(Mutex::new(wallet)),
|
Arc::new(Mutex::new(wallet)),
|
||||||
|
@ -316,9 +308,8 @@ impl LocalServerContainer {
|
||||||
let keychain: keychain::ExtKeychain = wallet_seed
|
let keychain: keychain::ExtKeychain = wallet_seed
|
||||||
.derive_keychain("")
|
.derive_keychain("")
|
||||||
.expect("Failed to derive keychain from seed file and passphrase.");
|
.expect("Failed to derive keychain from seed file and passphrase.");
|
||||||
let client_n = HTTPWalletToNodeClient::new(&config.check_node_api_http_addr, None);
|
let client_n = HTTPNodeClient::new(&config.check_node_api_http_addr, None);
|
||||||
let client_w = HTTPWalletToWalletClient::new();
|
let mut wallet = LMDBBackend::new(config.clone(), "", client_n)
|
||||||
let mut wallet = LMDBBackend::new(config.clone(), "", client_n, client_w)
|
|
||||||
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config));
|
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config));
|
||||||
wallet.keychain = Some(keychain);
|
wallet.keychain = Some(keychain);
|
||||||
let parent_id = keychain::ExtKeychain::derive_key_id(2, 0, 0, 0, 0);
|
let parent_id = keychain::ExtKeychain::derive_key_id(2, 0, 0, 0, 0);
|
||||||
|
@ -344,35 +335,33 @@ impl LocalServerContainer {
|
||||||
.derive_keychain("")
|
.derive_keychain("")
|
||||||
.expect("Failed to derive keychain from seed file and passphrase.");
|
.expect("Failed to derive keychain from seed file and passphrase.");
|
||||||
|
|
||||||
let client_n = HTTPWalletToNodeClient::new(&config.check_node_api_http_addr, None);
|
let client_n = HTTPNodeClient::new(&config.check_node_api_http_addr, None);
|
||||||
let client_w = HTTPWalletToWalletClient::new();
|
let client_w = HTTPWalletCommAdapter::new();
|
||||||
|
|
||||||
let max_outputs = 500;
|
let max_outputs = 500;
|
||||||
let change_outputs = 1;
|
let change_outputs = 1;
|
||||||
|
|
||||||
let mut wallet = LMDBBackend::new(config.clone(), "", client_n, client_w)
|
let mut wallet = LMDBBackend::new(config.clone(), "", client_n)
|
||||||
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config));
|
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config));
|
||||||
wallet.keychain = Some(keychain);
|
wallet.keychain = Some(keychain);
|
||||||
let _ = wallet::controller::owner_single_use(Arc::new(Mutex::new(wallet)), |api| {
|
let _ = wallet::controller::owner_single_use(Arc::new(Mutex::new(wallet)), |api| {
|
||||||
let result = api.issue_send_tx(
|
let (mut slate, lock_fn) = api.initiate_tx(
|
||||||
|
None,
|
||||||
amount,
|
amount,
|
||||||
minimum_confirmations,
|
minimum_confirmations,
|
||||||
dest,
|
|
||||||
max_outputs,
|
max_outputs,
|
||||||
change_outputs,
|
change_outputs,
|
||||||
selection_strategy == "all",
|
selection_strategy == "all",
|
||||||
);
|
)?;
|
||||||
match result {
|
slate = client_w.send_tx_sync(dest, &slate)?;
|
||||||
Ok(_) => println!(
|
api.finalize_tx(&mut slate)?;
|
||||||
|
api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
|
println!(
|
||||||
"Tx sent: {} grin to {} (strategy '{}')",
|
"Tx sent: {} grin to {} (strategy '{}')",
|
||||||
core::core::amount_to_hr_string(amount, false),
|
core::core::amount_to_hr_string(amount, false),
|
||||||
dest,
|
dest,
|
||||||
selection_strategy,
|
selection_strategy,
|
||||||
),
|
);
|
||||||
Err(e) => {
|
|
||||||
println!("Tx not sent to {}: {:?}", dest, e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}).unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config));
|
}).unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config));
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,11 +37,10 @@ use core::core::hash::Hashed;
|
||||||
use core::global::{self, ChainTypes};
|
use core::global::{self, ChainTypes};
|
||||||
|
|
||||||
use wallet::controller;
|
use wallet::controller;
|
||||||
use wallet::libtx::slate::Slate;
|
|
||||||
use wallet::libwallet::types::{WalletBackend, WalletInst};
|
use wallet::libwallet::types::{WalletBackend, WalletInst};
|
||||||
use wallet::lmdb_wallet::LMDBBackend;
|
use wallet::lmdb_wallet::LMDBBackend;
|
||||||
use wallet::WalletConfig;
|
use wallet::WalletConfig;
|
||||||
use wallet::{HTTPWalletToNodeClient, HTTPWalletToWalletClient};
|
use wallet::{HTTPNodeClient, HTTPWalletCommAdapter, WalletCommAdapter};
|
||||||
|
|
||||||
use framework::{
|
use framework::{
|
||||||
config, stop_all_servers, LocalServerContainerConfig, LocalServerContainerPool,
|
config, stop_all_servers, LocalServerContainerConfig, LocalServerContainerPool,
|
||||||
|
@ -880,19 +879,15 @@ fn long_fork_test_case_6(s: &Vec<servers::Server>) {
|
||||||
|
|
||||||
pub fn create_wallet(
|
pub fn create_wallet(
|
||||||
dir: &str,
|
dir: &str,
|
||||||
client_n: HTTPWalletToNodeClient,
|
client_n: HTTPNodeClient,
|
||||||
client_w: HTTPWalletToWalletClient,
|
) -> Arc<Mutex<WalletInst<HTTPNodeClient, keychain::ExtKeychain>>> {
|
||||||
) -> Arc<Mutex<WalletInst<HTTPWalletToNodeClient, HTTPWalletToWalletClient, keychain::ExtKeychain>>>
|
|
||||||
{
|
|
||||||
let mut wallet_config = WalletConfig::default();
|
let mut wallet_config = WalletConfig::default();
|
||||||
wallet_config.data_file_dir = String::from(dir);
|
wallet_config.data_file_dir = String::from(dir);
|
||||||
let _ = wallet::WalletSeed::init_file(&wallet_config);
|
let _ = wallet::WalletSeed::init_file(&wallet_config);
|
||||||
let mut wallet: LMDBBackend<
|
let mut wallet: LMDBBackend<HTTPNodeClient, keychain::ExtKeychain> =
|
||||||
HTTPWalletToNodeClient,
|
LMDBBackend::new(wallet_config.clone(), "", client_n).unwrap_or_else(|e| {
|
||||||
HTTPWalletToWalletClient,
|
panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config)
|
||||||
keychain::ExtKeychain,
|
});
|
||||||
> = LMDBBackend::new(wallet_config.clone(), "", client_n, client_w)
|
|
||||||
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config));
|
|
||||||
wallet.open_with_credentials().unwrap_or_else(|e| {
|
wallet.open_with_credentials().unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
"Error initializing wallet: {:?} Config: {:?}",
|
"Error initializing wallet: {:?} Config: {:?}",
|
||||||
|
@ -912,26 +907,17 @@ fn replicate_tx_fluff_failure() {
|
||||||
|
|
||||||
// Create Wallet 1 (Mining Input) and start it listening
|
// Create Wallet 1 (Mining Input) and start it listening
|
||||||
// Wallet 1 post to another node, just for fun
|
// Wallet 1 post to another node, just for fun
|
||||||
let client1 = HTTPWalletToNodeClient::new("http://127.0.0.1:23003", None);
|
let client1 = HTTPNodeClient::new("http://127.0.0.1:23003", None);
|
||||||
let client1_w = HTTPWalletToWalletClient::new();
|
let client1_w = HTTPWalletCommAdapter::new();
|
||||||
let wallet1 = create_wallet(
|
let wallet1 = create_wallet("target/tmp/tx_fluff/wallet1", client1.clone());
|
||||||
"target/tmp/tx_fluff/wallet1",
|
|
||||||
client1.clone(),
|
|
||||||
client1_w.clone(),
|
|
||||||
);
|
|
||||||
let wallet1_handle = thread::spawn(move || {
|
let wallet1_handle = thread::spawn(move || {
|
||||||
controller::foreign_listener(wallet1, "127.0.0.1:33000", None)
|
controller::foreign_listener(wallet1, "127.0.0.1:33000", None)
|
||||||
.unwrap_or_else(|e| panic!("Error creating wallet1 listener: {:?}", e,));
|
.unwrap_or_else(|e| panic!("Error creating wallet1 listener: {:?}", e,));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create Wallet 2 (Recipient) and launch
|
// Create Wallet 2 (Recipient) and launch
|
||||||
let client2 = HTTPWalletToNodeClient::new("http://127.0.0.1:23001", None);
|
let client2 = HTTPNodeClient::new("http://127.0.0.1:23001", None);
|
||||||
let client2_w = HTTPWalletToWalletClient::new();
|
let wallet2 = create_wallet("target/tmp/tx_fluff/wallet2", client2.clone());
|
||||||
let wallet2 = create_wallet(
|
|
||||||
"target/tmp/tx_fluff/wallet2",
|
|
||||||
client2.clone(),
|
|
||||||
client2_w.clone(),
|
|
||||||
);
|
|
||||||
let wallet2_handle = thread::spawn(move || {
|
let wallet2_handle = thread::spawn(move || {
|
||||||
controller::foreign_listener(wallet2, "127.0.0.1:33001", None)
|
controller::foreign_listener(wallet2, "127.0.0.1:33001", None)
|
||||||
.unwrap_or_else(|e| panic!("Error creating wallet2 listener: {:?}", e,));
|
.unwrap_or_else(|e| panic!("Error creating wallet2 listener: {:?}", e,));
|
||||||
|
@ -971,26 +957,23 @@ fn replicate_tx_fluff_failure() {
|
||||||
thread::sleep(time::Duration::from_secs(10));
|
thread::sleep(time::Duration::from_secs(10));
|
||||||
|
|
||||||
// get another instance of wallet1 (to update contents and perform a send)
|
// get another instance of wallet1 (to update contents and perform a send)
|
||||||
let wallet1 = create_wallet(
|
let wallet1 = create_wallet("target/tmp/tx_fluff/wallet1", client1.clone());
|
||||||
"target/tmp/tx_fluff/wallet1",
|
|
||||||
client1.clone(),
|
|
||||||
client1_w.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let amount = 30_000_000_000;
|
let amount = 30_000_000_000;
|
||||||
let mut slate = Slate::blank(1);
|
let dest = "http://127.0.0.1:33001";
|
||||||
|
|
||||||
wallet::controller::owner_single_use(wallet1, |api| {
|
wallet::controller::owner_single_use(wallet1, |api| {
|
||||||
slate = api
|
let (mut slate, lock_fn) = api.initiate_tx(
|
||||||
.issue_send_tx(
|
None, amount, // amount
|
||||||
amount, // amount
|
|
||||||
2, // minimum confirmations
|
2, // minimum confirmations
|
||||||
"http://127.0.0.1:33001", // dest
|
|
||||||
500, // max outputs
|
500, // max outputs
|
||||||
1000, // num change outputs
|
1000, // num change outputs
|
||||||
true, // select all outputs
|
true, // select all outputs
|
||||||
).unwrap();
|
)?;
|
||||||
api.post_tx(&slate, false).unwrap();
|
slate = client1_w.send_tx_sync(dest, &slate)?;
|
||||||
|
api.finalize_tx(&mut slate)?;
|
||||||
|
api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
|
api.post_tx(&slate, false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
||||||
|
@ -998,11 +981,7 @@ fn replicate_tx_fluff_failure() {
|
||||||
thread::sleep(time::Duration::from_secs(200));
|
thread::sleep(time::Duration::from_secs(200));
|
||||||
|
|
||||||
// get another instance of wallet (to check contents)
|
// get another instance of wallet (to check contents)
|
||||||
let wallet2 = create_wallet(
|
let wallet2 = create_wallet("target/tmp/tx_fluff/wallet2", client2.clone());
|
||||||
"target/tmp/tx_fluff/wallet2",
|
|
||||||
client2.clone(),
|
|
||||||
client2_w.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
wallet::controller::owner_single_use(wallet2, |api| {
|
wallet::controller::owner_single_use(wallet2, |api| {
|
||||||
let res = api.retrieve_summary_info(true).unwrap();
|
let res = api.retrieve_summary_info(true).unwrap();
|
||||||
|
|
|
@ -13,15 +13,11 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use serde_json as json;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
/// Wallet commands processing
|
/// Wallet commands processing
|
||||||
use std::sync::Arc;
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use util::Mutex;
|
|
||||||
|
|
||||||
use api::TLSConfig;
|
use api::TLSConfig;
|
||||||
use config::GlobalWalletConfig;
|
use config::GlobalWalletConfig;
|
||||||
|
@ -29,8 +25,8 @@ use core::{core, global};
|
||||||
use grin_wallet::libwallet::ErrorKind;
|
use grin_wallet::libwallet::ErrorKind;
|
||||||
use grin_wallet::{self, controller, display, libwallet};
|
use grin_wallet::{self, controller, display, libwallet};
|
||||||
use grin_wallet::{
|
use grin_wallet::{
|
||||||
HTTPWalletToNodeClient, HTTPWalletToWalletClient, LMDBBackend, WalletBackend, WalletConfig,
|
instantiate_wallet, FileWalletCommAdapter, HTTPNodeClient, HTTPWalletCommAdapter, LMDBBackend,
|
||||||
WalletInst, WalletSeed,
|
NullWalletCommAdapter, WalletConfig, WalletSeed,
|
||||||
};
|
};
|
||||||
use keychain;
|
use keychain;
|
||||||
use servers::start_webwallet_server;
|
use servers::start_webwallet_server;
|
||||||
|
@ -52,31 +48,6 @@ pub fn seed_exists(wallet_config: WalletConfig) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn instantiate_wallet(
|
|
||||||
wallet_config: WalletConfig,
|
|
||||||
passphrase: &str,
|
|
||||||
account: &str,
|
|
||||||
node_api_secret: Option<String>,
|
|
||||||
) -> Arc<Mutex<WalletInst<HTTPWalletToNodeClient, HTTPWalletToWalletClient, keychain::ExtKeychain>>>
|
|
||||||
{
|
|
||||||
let client_n =
|
|
||||||
HTTPWalletToNodeClient::new(&wallet_config.check_node_api_http_addr, node_api_secret);
|
|
||||||
let client_w = HTTPWalletToWalletClient::new();
|
|
||||||
let mut db_wallet = LMDBBackend::new(wallet_config.clone(), passphrase, client_n, client_w)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
panic!(
|
|
||||||
"Error creating DB wallet: {} Config: {:?}",
|
|
||||||
e, wallet_config
|
|
||||||
);
|
|
||||||
});
|
|
||||||
db_wallet
|
|
||||||
.set_parent_key_id_by_name(account)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
panic!("Error starting wallet: {}", e);
|
|
||||||
});
|
|
||||||
info!("Using LMDB Backend for wallet");
|
|
||||||
Arc::new(Mutex::new(db_wallet))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i32 {
|
pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i32 {
|
||||||
// just get defaults from the global config
|
// just get defaults from the global config
|
||||||
|
@ -110,13 +81,9 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
|
||||||
WalletSeed::init_file(&wallet_config).expect("Failed to init wallet seed file.");
|
WalletSeed::init_file(&wallet_config).expect("Failed to init wallet seed file.");
|
||||||
info!("Wallet seed file created");
|
info!("Wallet seed file created");
|
||||||
let client_n =
|
let client_n =
|
||||||
HTTPWalletToNodeClient::new(&wallet_config.check_node_api_http_addr, node_api_secret);
|
HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, node_api_secret);
|
||||||
let client_w = HTTPWalletToWalletClient::new();
|
let _: LMDBBackend<HTTPNodeClient, keychain::ExtKeychain> =
|
||||||
let _: LMDBBackend<
|
LMDBBackend::new(wallet_config.clone(), "", client_n).unwrap_or_else(|e| {
|
||||||
HTTPWalletToNodeClient,
|
|
||||||
HTTPWalletToWalletClient,
|
|
||||||
keychain::ExtKeychain,
|
|
||||||
> = LMDBBackend::new(wallet_config.clone(), "", client_n, client_w).unwrap_or_else(|e| {
|
|
||||||
panic!(
|
panic!(
|
||||||
"Error creating DB for wallet: {} Config: {:?}",
|
"Error creating DB for wallet: {} Config: {:?}",
|
||||||
e, wallet_config
|
e, wallet_config
|
||||||
|
@ -145,13 +112,6 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
};
|
};
|
||||||
|
|
||||||
let wallet = instantiate_wallet(
|
|
||||||
wallet_config.clone(),
|
|
||||||
passphrase,
|
|
||||||
account,
|
|
||||||
node_api_secret.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle listener startup commands
|
// Handle listener startup commands
|
||||||
{
|
{
|
||||||
let api_secret = get_first_line(wallet_config.api_secret_path.clone());
|
let api_secret = get_first_line(wallet_config.api_secret_path.clone());
|
||||||
|
@ -173,10 +133,23 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
|
||||||
if let Some(port) = listen_args.value_of("port") {
|
if let Some(port) = listen_args.value_of("port") {
|
||||||
wallet_config.api_listen_port = port.parse().unwrap();
|
wallet_config.api_listen_port = port.parse().unwrap();
|
||||||
}
|
}
|
||||||
controller::foreign_listener(
|
let mut params = HashMap::new();
|
||||||
wallet.clone(),
|
params.insert(
|
||||||
&wallet_config.api_listen_addr(),
|
"api_listen_addr".to_owned(),
|
||||||
tls_conf,
|
wallet_config.api_listen_addr(),
|
||||||
|
);
|
||||||
|
if let Some(t) = tls_conf {
|
||||||
|
params.insert("certificate".to_owned(), t.certificate);
|
||||||
|
params.insert("private_key".to_owned(), t.private_key);
|
||||||
|
}
|
||||||
|
let adapter = HTTPWalletCommAdapter::new();
|
||||||
|
adapter
|
||||||
|
.listen(
|
||||||
|
params,
|
||||||
|
wallet_config.clone(),
|
||||||
|
passphrase,
|
||||||
|
account,
|
||||||
|
node_api_secret.clone(),
|
||||||
).unwrap_or_else(|e| {
|
).unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
"Error creating wallet listener: {:?} Config: {:?}",
|
"Error creating wallet listener: {:?} Config: {:?}",
|
||||||
|
@ -185,6 +158,12 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
("owner_api", Some(_api_args)) => {
|
("owner_api", Some(_api_args)) => {
|
||||||
|
let wallet = instantiate_wallet(
|
||||||
|
wallet_config.clone(),
|
||||||
|
passphrase,
|
||||||
|
account,
|
||||||
|
node_api_secret.clone(),
|
||||||
|
);
|
||||||
// TLS is disabled because we bind to localhost
|
// TLS is disabled because we bind to localhost
|
||||||
controller::owner_listener(wallet.clone(), "127.0.0.1:13420", api_secret, None)
|
controller::owner_listener(wallet.clone(), "127.0.0.1:13420", api_secret, None)
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
|
@ -195,6 +174,12 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
("web", Some(_api_args)) => {
|
("web", Some(_api_args)) => {
|
||||||
|
let wallet = instantiate_wallet(
|
||||||
|
wallet_config.clone(),
|
||||||
|
passphrase,
|
||||||
|
account,
|
||||||
|
node_api_secret.clone(),
|
||||||
|
);
|
||||||
// start owner listener and run static file server
|
// start owner listener and run static file server
|
||||||
start_webwallet_server();
|
start_webwallet_server();
|
||||||
controller::owner_listener(wallet.clone(), "127.0.0.1:13420", api_secret, tls_conf)
|
controller::owner_listener(wallet.clone(), "127.0.0.1:13420", api_secret, tls_conf)
|
||||||
|
@ -209,6 +194,13 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let wallet = instantiate_wallet(
|
||||||
|
wallet_config.clone(),
|
||||||
|
passphrase,
|
||||||
|
account,
|
||||||
|
node_api_secret.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let res = controller::owner_single_use(wallet.clone(), |api| {
|
let res = controller::owner_single_use(wallet.clone(), |api| {
|
||||||
match wallet_args.subcommand() {
|
match wallet_args.subcommand() {
|
||||||
("account", Some(acct_args)) => {
|
("account", Some(acct_args)) => {
|
||||||
|
@ -299,17 +291,22 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
|
||||||
})?;
|
})?;
|
||||||
let fluff = send_args.is_present("fluff");
|
let fluff = send_args.is_present("fluff");
|
||||||
let max_outputs = 500;
|
let max_outputs = 500;
|
||||||
if method == "http" {
|
if method == "http" && !dest.starts_with("http://") && !dest.starts_with("https://")
|
||||||
if dest.starts_with("http://") || dest.starts_with("https://") {
|
{
|
||||||
let result = api.issue_send_tx(
|
return Err(ErrorKind::GenericError(format!(
|
||||||
|
"HTTP Destination should start with http://: or https://: {}",
|
||||||
|
dest
|
||||||
|
)).into());
|
||||||
|
}
|
||||||
|
let result = api.initiate_tx(
|
||||||
|
None,
|
||||||
amount,
|
amount,
|
||||||
minimum_confirmations,
|
minimum_confirmations,
|
||||||
dest,
|
|
||||||
max_outputs,
|
max_outputs,
|
||||||
change_outputs,
|
change_outputs,
|
||||||
selection_strategy == "all",
|
selection_strategy == "all",
|
||||||
);
|
);
|
||||||
let slate = match result {
|
let (mut slate, lock_fn) = match result {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
info!(
|
info!(
|
||||||
"Tx created: {} grin to {} (strategy '{}')",
|
"Tx created: {} grin to {} (strategy '{}')",
|
||||||
|
@ -334,88 +331,40 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let adapter = match method {
|
||||||
|
"http" => HTTPWalletCommAdapter::new(),
|
||||||
|
"file" => FileWalletCommAdapter::new(),
|
||||||
|
"self" => NullWalletCommAdapter::new(),
|
||||||
|
_ => NullWalletCommAdapter::new(),
|
||||||
|
};
|
||||||
|
if adapter.supports_sync() {
|
||||||
|
slate = adapter.send_tx_sync(dest, &slate)?;
|
||||||
|
if method == "self" {
|
||||||
|
controller::foreign_single_use(wallet, |api| {
|
||||||
|
api.receive_tx(&mut slate, Some(dest))?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
api.finalize_tx(&mut slate)?;
|
||||||
|
} else {
|
||||||
|
adapter.send_tx_async(dest, &slate)?;
|
||||||
|
}
|
||||||
|
api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
|
if adapter.supports_sync() {
|
||||||
let result = api.post_tx(&slate, fluff);
|
let result = api.post_tx(&slate, fluff);
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!("Tx sent",);
|
info!("Tx sent",);
|
||||||
Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Tx not sent: {}", e);
|
error!("Tx not sent: {}", e);
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(ErrorKind::GenericError(format!(
|
|
||||||
"HTTP Destination should start with http://: or https://: {}",
|
|
||||||
dest
|
|
||||||
)).into());
|
|
||||||
}
|
|
||||||
} else if method == "self" {
|
|
||||||
let result = api.issue_self_tx(
|
|
||||||
amount,
|
|
||||||
minimum_confirmations,
|
|
||||||
max_outputs,
|
|
||||||
change_outputs,
|
|
||||||
selection_strategy == "all",
|
|
||||||
account,
|
|
||||||
dest,
|
|
||||||
);
|
|
||||||
let slate = match result {
|
|
||||||
Ok(s) => {
|
|
||||||
info!(
|
|
||||||
"Tx created: {} grin to self, source acct: {} dest_acct: {} (strategy '{}')",
|
|
||||||
core::amount_to_hr_string(amount, false),
|
|
||||||
account,
|
|
||||||
dest,
|
|
||||||
selection_strategy,
|
|
||||||
);
|
|
||||||
s
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("Tx not created: {}", e);
|
|
||||||
match e.kind() {
|
|
||||||
// user errors, don't backtrace
|
|
||||||
libwallet::ErrorKind::NotEnoughFunds { .. } => {}
|
|
||||||
libwallet::ErrorKind::FeeDispute { .. } => {}
|
|
||||||
libwallet::ErrorKind::FeeExceedsAmount { .. } => {}
|
|
||||||
_ => {
|
|
||||||
// otherwise give full dump
|
|
||||||
error!("Backtrace: {}", e.backtrace().unwrap());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
let result = api.post_tx(&slate, fluff);
|
}
|
||||||
match result {
|
|
||||||
Ok(_) => {
|
|
||||||
info!("Tx sent",);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
|
||||||
error!("Tx not sent: {}", e);
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if method == "file" {
|
|
||||||
api.send_tx(
|
|
||||||
true,
|
|
||||||
amount,
|
|
||||||
minimum_confirmations,
|
|
||||||
dest,
|
|
||||||
max_outputs,
|
|
||||||
change_outputs,
|
|
||||||
selection_strategy == "all",
|
|
||||||
).map_err(|e| ErrorKind::GenericError(format!("Send failed. e={:?}", e)))?;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
return Err(ErrorKind::GenericError(format!(
|
|
||||||
"unsupported payment method: {}",
|
|
||||||
method
|
|
||||||
)).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
("receive", Some(send_args)) => {
|
("receive", Some(send_args)) => {
|
||||||
let mut receive_result: Result<(), grin_wallet::libwallet::Error> = Ok(());
|
let mut receive_result: Result<(), grin_wallet::libwallet::Error> = Ok(());
|
||||||
let tx_file = send_args.value_of("input").ok_or_else(|| {
|
let tx_file = send_args.value_of("input").ok_or_else(|| {
|
||||||
|
@ -426,18 +375,18 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
|
||||||
ErrorKind::GenericError(format!("File {} not found.", tx_file)).into(),
|
ErrorKind::GenericError(format!("File {} not found.", tx_file)).into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let res = controller::foreign_single_use(wallet, |api| {
|
let adapter = FileWalletCommAdapter::new();
|
||||||
receive_result = api.file_receive_tx(tx_file);
|
let mut slate = adapter.receive_tx_async(tx_file)?;
|
||||||
|
controller::foreign_single_use(wallet, |api| {
|
||||||
|
api.receive_tx(&mut slate, Some(account))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
})?;
|
||||||
if res.is_err() {
|
let send_tx = format!("{}.response", tx_file);
|
||||||
return res;
|
adapter.send_tx_async(&send_tx, &slate)?;
|
||||||
} else {
|
|
||||||
info!(
|
info!(
|
||||||
"Response file {}.response generated, sending it back to the transaction originator.",
|
"Response file {}.response generated, sending it back to the transaction originator.",
|
||||||
tx_file,
|
tx_file,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
receive_result
|
receive_result
|
||||||
}
|
}
|
||||||
("finalize", Some(send_args)) => {
|
("finalize", Some(send_args)) => {
|
||||||
|
@ -450,11 +399,8 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) -> i
|
||||||
ErrorKind::GenericError(format!("File {} not found.", tx_file)).into(),
|
ErrorKind::GenericError(format!("File {} not found.", tx_file)).into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let mut pub_tx_f = File::open(tx_file)?;
|
let adapter = FileWalletCommAdapter::new();
|
||||||
let mut content = String::new();
|
let mut slate = adapter.receive_tx_async(tx_file)?;
|
||||||
pub_tx_f.read_to_string(&mut content)?;
|
|
||||||
let mut slate: grin_wallet::libtx::slate::Slate = json::from_str(&content)
|
|
||||||
.map_err(|_| grin_wallet::libwallet::ErrorKind::Format)?;
|
|
||||||
let _ = api.finalize_tx(&mut slate).expect("Finalize failed");
|
let _ = api.finalize_tx(&mut slate).expect("Finalize failed");
|
||||||
|
|
||||||
let result = api.post_tx(&slate, fluff);
|
let result = api.post_tx(&slate, fluff);
|
||||||
|
|
69
wallet/src/adapters/file.rs
Normal file
69
wallet/src/adapters/file.rs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
/// File Output 'plugin' implementation
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
use serde_json as json;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use libtx::slate::Slate;
|
||||||
|
use libwallet::{Error, ErrorKind};
|
||||||
|
use {WalletCommAdapter, WalletConfig};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct FileWalletCommAdapter {}
|
||||||
|
|
||||||
|
impl FileWalletCommAdapter {
|
||||||
|
/// Create
|
||||||
|
pub fn new() -> Box<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)?;
|
||||||
|
pub_tx.write_all(json::to_string(&slate).unwrap().as_bytes())?;
|
||||||
|
pub_tx.sync_all()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive_tx_async(&self, params: &str) -> Result<Slate, Error> {
|
||||||
|
let mut pub_tx_f = File::open(params)?;
|
||||||
|
let mut content = String::new();
|
||||||
|
pub_tx_f.read_to_string(&mut content)?;
|
||||||
|
Ok(json::from_str(&content).map_err(|_| ErrorKind::Format)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn listen(
|
||||||
|
&self,
|
||||||
|
_params: HashMap<String, String>,
|
||||||
|
_config: WalletConfig,
|
||||||
|
_passphrase: &str,
|
||||||
|
_account: &str,
|
||||||
|
_node_api_secret: Option<String>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
86
wallet/src/adapters/http.rs
Normal file
86
wallet/src/adapters/http.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
/// HTTP Wallet 'plugin' implementation
|
||||||
|
use failure::ResultExt;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use api;
|
||||||
|
use controller;
|
||||||
|
use libtx::slate::Slate;
|
||||||
|
use libwallet::{Error, ErrorKind};
|
||||||
|
use {instantiate_wallet, WalletCommAdapter, WalletConfig};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct HTTPWalletCommAdapter {}
|
||||||
|
|
||||||
|
impl HTTPWalletCommAdapter {
|
||||||
|
/// Create
|
||||||
|
pub fn new() -> Box<WalletCommAdapter> {
|
||||||
|
Box::new(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!("{}/v1/wallet/foreign/receive_tx", dest);
|
||||||
|
debug!("Posting transaction slate to {}", url);
|
||||||
|
|
||||||
|
let res = api::client::post(url.as_str(), None, slate)
|
||||||
|
.context(ErrorKind::ClientCallback("Posting transaction slate"))?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_tx_async(&self, _dest: &str, _slate: &Slate) -> Result<(), Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
let wallet =
|
||||||
|
instantiate_wallet(config.clone(), passphrase, account, node_api_secret.clone());
|
||||||
|
let listen_addr = params.get("api_listen_addr").unwrap();
|
||||||
|
let tls_conf = match params.get("certificate") {
|
||||||
|
Some(s) => Some(api::TLSConfig::new(
|
||||||
|
s.to_owned(),
|
||||||
|
params.get("private_key").unwrap().to_owned(),
|
||||||
|
)),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
controller::foreign_listener(wallet.clone(), &listen_addr, tls_conf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
55
wallet/src/adapters/mod.rs
Normal file
55
wallet/src/adapters/mod.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright 2018 The Grin Developers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
mod file;
|
||||||
|
mod http;
|
||||||
|
mod null;
|
||||||
|
|
||||||
|
pub use self::file::FileWalletCommAdapter;
|
||||||
|
pub use self::http::HTTPWalletCommAdapter;
|
||||||
|
pub use self::null::NullWalletCommAdapter;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use libtx::slate::Slate;
|
||||||
|
use libwallet::Error;
|
||||||
|
use WalletConfig;
|
||||||
|
|
||||||
|
/// Encapsulate wallet to wallet communication functions
|
||||||
|
pub trait WalletCommAdapter {
|
||||||
|
/// Whether this adapter supports sync mode
|
||||||
|
fn supports_sync(&self) -> bool;
|
||||||
|
|
||||||
|
/// 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>;
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
account: &str,
|
||||||
|
node_api_secret: Option<String>,
|
||||||
|
) -> Result<(), Error>;
|
||||||
|
}
|
59
wallet/src/adapters/null.rs
Normal file
59
wallet/src/adapters/null.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
/// Null Output 'plugin' implementation
|
||||||
|
use libtx::slate::Slate;
|
||||||
|
use libwallet::Error;
|
||||||
|
use {WalletCommAdapter, WalletConfig};
|
||||||
|
|
||||||
|
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> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_tx_async(&self, _dest: &str, _slate: &Slate) -> Result<(), Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
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,7 +15,9 @@
|
||||||
//! Controller for wallet.. instantiates and handles listeners (or single-run
|
//! Controller for wallet.. instantiates and handles listeners (or single-run
|
||||||
//! invocations) as needed.
|
//! invocations) as needed.
|
||||||
//! Still experimental
|
//! Still experimental
|
||||||
|
use adapters::{FileWalletCommAdapter, HTTPWalletCommAdapter};
|
||||||
use api::{ApiServer, BasicAuthMiddleware, Handler, ResponseFuture, Router, TLSConfig};
|
use api::{ApiServer, BasicAuthMiddleware, Handler, ResponseFuture, Router, TLSConfig};
|
||||||
|
use core::core;
|
||||||
use core::core::Transaction;
|
use core::core::Transaction;
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
use futures::future::{err, ok};
|
use futures::future::{err, ok};
|
||||||
|
@ -25,8 +27,7 @@ use keychain::Keychain;
|
||||||
use libtx::slate::Slate;
|
use libtx::slate::Slate;
|
||||||
use libwallet::api::{APIForeign, APIOwner};
|
use libwallet::api::{APIForeign, APIOwner};
|
||||||
use libwallet::types::{
|
use libwallet::types::{
|
||||||
CbData, OutputData, SendTXArgs, TxLogEntry, WalletBackend, WalletInfo, WalletToNodeClient,
|
CbData, NodeClient, OutputData, SendTXArgs, TxLogEntry, WalletBackend, WalletInfo,
|
||||||
WalletToWalletClient,
|
|
||||||
};
|
};
|
||||||
use libwallet::{Error, ErrorKind};
|
use libwallet::{Error, ErrorKind};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -42,12 +43,11 @@ use util::Mutex;
|
||||||
|
|
||||||
/// Instantiate wallet Owner API for a single-use (command line) call
|
/// Instantiate wallet Owner API for a single-use (command line) call
|
||||||
/// Return a function containing a loaded API context to call
|
/// Return a function containing a loaded API context to call
|
||||||
pub fn owner_single_use<F, T: ?Sized, C, L, K>(wallet: Arc<Mutex<T>>, f: F) -> Result<(), Error>
|
pub fn owner_single_use<F, T: ?Sized, C, K>(wallet: Arc<Mutex<T>>, f: F) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
F: FnOnce(&mut APIOwner<T, C, L, K>) -> Result<(), Error>,
|
F: FnOnce(&mut APIOwner<T, C, K>) -> Result<(), Error>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
f(&mut APIOwner::new(wallet.clone()))?;
|
f(&mut APIOwner::new(wallet.clone()))?;
|
||||||
|
@ -56,12 +56,11 @@ where
|
||||||
|
|
||||||
/// Instantiate wallet Foreign API for a single-use (command line) call
|
/// Instantiate wallet Foreign API for a single-use (command line) call
|
||||||
/// Return a function containing a loaded API context to call
|
/// Return a function containing a loaded API context to call
|
||||||
pub fn foreign_single_use<F, T: ?Sized, C, L, K>(wallet: Arc<Mutex<T>>, f: F) -> Result<(), Error>
|
pub fn foreign_single_use<F, T: ?Sized, C, K>(wallet: Arc<Mutex<T>>, f: F) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
F: FnOnce(&mut APIForeign<T, C, L, K>) -> Result<(), Error>,
|
F: FnOnce(&mut APIForeign<T, C, K>) -> Result<(), Error>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
f(&mut APIForeign::new(wallet.clone()))?;
|
f(&mut APIForeign::new(wallet.clone()))?;
|
||||||
|
@ -70,17 +69,16 @@ where
|
||||||
|
|
||||||
/// Listener version, providing same API but listening for requests on a
|
/// Listener version, providing same API but listening for requests on a
|
||||||
/// port and wrapping the calls
|
/// port and wrapping the calls
|
||||||
pub fn owner_listener<T: ?Sized, C, L, K>(
|
pub fn owner_listener<T: ?Sized, C, K>(
|
||||||
wallet: Arc<Mutex<T>>,
|
wallet: Arc<Mutex<T>>,
|
||||||
addr: &str,
|
addr: &str,
|
||||||
api_secret: Option<String>,
|
api_secret: Option<String>,
|
||||||
tls_config: Option<TLSConfig>,
|
tls_config: Option<TLSConfig>,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
OwnerAPIHandler<T, C, L, K>: Handler,
|
OwnerAPIHandler<T, C, K>: Handler,
|
||||||
C: WalletToNodeClient + 'static,
|
C: NodeClient + 'static,
|
||||||
L: WalletToWalletClient + 'static,
|
|
||||||
K: Keychain + 'static,
|
K: Keychain + 'static,
|
||||||
{
|
{
|
||||||
let api_handler = OwnerAPIHandler::new(wallet);
|
let api_handler = OwnerAPIHandler::new(wallet);
|
||||||
|
@ -112,15 +110,14 @@ where
|
||||||
|
|
||||||
/// Listener version, providing same API but listening for requests on a
|
/// Listener version, providing same API but listening for requests on a
|
||||||
/// port and wrapping the calls
|
/// port and wrapping the calls
|
||||||
pub fn foreign_listener<T: ?Sized, C, L, K>(
|
pub fn foreign_listener<T: ?Sized, C, K>(
|
||||||
wallet: Arc<Mutex<T>>,
|
wallet: Arc<Mutex<T>>,
|
||||||
addr: &str,
|
addr: &str,
|
||||||
tls_config: Option<TLSConfig>,
|
tls_config: Option<TLSConfig>,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
C: WalletToNodeClient + 'static,
|
C: NodeClient + 'static,
|
||||||
L: WalletToWalletClient + 'static,
|
|
||||||
K: Keychain + 'static,
|
K: Keychain + 'static,
|
||||||
{
|
{
|
||||||
let api_handler = ForeignAPIHandler::new(wallet);
|
let api_handler = ForeignAPIHandler::new(wallet);
|
||||||
|
@ -147,41 +144,37 @@ where
|
||||||
type WalletResponseFuture = Box<Future<Item = Response<Body>, Error = Error> + Send>;
|
type WalletResponseFuture = Box<Future<Item = Response<Body>, Error = Error> + Send>;
|
||||||
|
|
||||||
/// API Handler/Wrapper for owner functions
|
/// API Handler/Wrapper for owner functions
|
||||||
pub struct OwnerAPIHandler<T: ?Sized, C, L, K>
|
pub struct OwnerAPIHandler<T: ?Sized, C, K>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
C: WalletToNodeClient + 'static,
|
C: NodeClient + 'static,
|
||||||
L: WalletToWalletClient + 'static,
|
|
||||||
K: Keychain + 'static,
|
K: Keychain + 'static,
|
||||||
{
|
{
|
||||||
/// Wallet instance
|
/// Wallet instance
|
||||||
pub wallet: Arc<Mutex<T>>,
|
pub wallet: Arc<Mutex<T>>,
|
||||||
phantom: PhantomData<K>,
|
phantom: PhantomData<K>,
|
||||||
phantom_c: PhantomData<C>,
|
phantom_c: PhantomData<C>,
|
||||||
phantom_l: PhantomData<L>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized, C, L, K> OwnerAPIHandler<T, C, L, K>
|
impl<T: ?Sized, C, K> OwnerAPIHandler<T, C, K>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
C: WalletToNodeClient + 'static,
|
C: NodeClient + 'static,
|
||||||
L: WalletToWalletClient + 'static,
|
|
||||||
K: Keychain + 'static,
|
K: Keychain + 'static,
|
||||||
{
|
{
|
||||||
/// Create a new owner API handler for GET methods
|
/// Create a new owner API handler for GET methods
|
||||||
pub fn new(wallet: Arc<Mutex<T>>) -> OwnerAPIHandler<T, C, L, K> {
|
pub fn new(wallet: Arc<Mutex<T>>) -> OwnerAPIHandler<T, C, K> {
|
||||||
OwnerAPIHandler {
|
OwnerAPIHandler {
|
||||||
wallet,
|
wallet,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
phantom_c: PhantomData,
|
phantom_c: PhantomData,
|
||||||
phantom_l: PhantomData,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn retrieve_outputs(
|
fn retrieve_outputs(
|
||||||
&self,
|
&self,
|
||||||
req: &Request<Body>,
|
req: &Request<Body>,
|
||||||
api: APIOwner<T, C, L, K>,
|
api: APIOwner<T, C, K>,
|
||||||
) -> Result<(bool, Vec<(OutputData, pedersen::Commitment)>), Error> {
|
) -> Result<(bool, Vec<(OutputData, pedersen::Commitment)>), Error> {
|
||||||
let mut update_from_node = false;
|
let mut update_from_node = false;
|
||||||
let mut id = None;
|
let mut id = None;
|
||||||
|
@ -205,7 +198,7 @@ where
|
||||||
fn retrieve_txs(
|
fn retrieve_txs(
|
||||||
&self,
|
&self,
|
||||||
req: &Request<Body>,
|
req: &Request<Body>,
|
||||||
api: APIOwner<T, C, L, K>,
|
api: APIOwner<T, C, K>,
|
||||||
) -> Result<(bool, Vec<TxLogEntry>), Error> {
|
) -> Result<(bool, Vec<TxLogEntry>), Error> {
|
||||||
let mut tx_id = None;
|
let mut tx_id = None;
|
||||||
let mut tx_slate_id = None;
|
let mut tx_slate_id = None;
|
||||||
|
@ -232,7 +225,7 @@ where
|
||||||
fn dump_stored_tx(
|
fn dump_stored_tx(
|
||||||
&self,
|
&self,
|
||||||
req: &Request<Body>,
|
req: &Request<Body>,
|
||||||
api: APIOwner<T, C, L, K>,
|
api: APIOwner<T, C, K>,
|
||||||
) -> Result<Transaction, Error> {
|
) -> Result<Transaction, Error> {
|
||||||
let params = parse_params(req);
|
let params = parse_params(req);
|
||||||
if let Some(id_string) = params.get("id") {
|
if let Some(id_string) = params.get("id") {
|
||||||
|
@ -261,7 +254,7 @@ where
|
||||||
fn retrieve_summary_info(
|
fn retrieve_summary_info(
|
||||||
&self,
|
&self,
|
||||||
req: &Request<Body>,
|
req: &Request<Body>,
|
||||||
mut api: APIOwner<T, C, L, K>,
|
mut api: APIOwner<T, C, K>,
|
||||||
) -> Result<(bool, WalletInfo), Error> {
|
) -> Result<(bool, WalletInfo), Error> {
|
||||||
let update_from_node = param_exists(req, "refresh");
|
let update_from_node = param_exists(req, "refresh");
|
||||||
api.retrieve_summary_info(update_from_node)
|
api.retrieve_summary_info(update_from_node)
|
||||||
|
@ -270,7 +263,7 @@ where
|
||||||
fn node_height(
|
fn node_height(
|
||||||
&self,
|
&self,
|
||||||
_req: &Request<Body>,
|
_req: &Request<Body>,
|
||||||
mut api: APIOwner<T, C, L, K>,
|
mut api: APIOwner<T, C, K>,
|
||||||
) -> Result<(u64, bool), Error> {
|
) -> Result<(u64, bool), Error> {
|
||||||
api.node_height()
|
api.node_height()
|
||||||
}
|
}
|
||||||
|
@ -298,39 +291,62 @@ where
|
||||||
fn issue_send_tx(
|
fn issue_send_tx(
|
||||||
&self,
|
&self,
|
||||||
req: Request<Body>,
|
req: Request<Body>,
|
||||||
mut api: APIOwner<T, C, L, K>,
|
mut api: APIOwner<T, C, K>,
|
||||||
) -> Box<Future<Item = Slate, Error = Error> + Send> {
|
) -> Box<Future<Item = Slate, Error = Error> + Send> {
|
||||||
Box::new(parse_body(req).and_then(move |args: SendTXArgs| {
|
Box::new(parse_body(req).and_then(move |args: SendTXArgs| {
|
||||||
|
let result = api.initiate_tx(
|
||||||
|
None,
|
||||||
|
args.amount,
|
||||||
|
args.minimum_confirmations,
|
||||||
|
args.max_outputs,
|
||||||
|
args.num_change_outputs,
|
||||||
|
args.selection_strategy_is_use_all,
|
||||||
|
);
|
||||||
|
let (mut slate, lock_fn) = match result {
|
||||||
|
Ok(s) => {
|
||||||
|
info!(
|
||||||
|
"Tx created: {} grin to {} (strategy '{}')",
|
||||||
|
core::amount_to_hr_string(args.amount, false),
|
||||||
|
&args.dest,
|
||||||
|
args.selection_strategy_is_use_all,
|
||||||
|
);
|
||||||
|
s
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Tx not created: {}", e);
|
||||||
|
match e.kind() {
|
||||||
|
// user errors, don't backtrace
|
||||||
|
ErrorKind::NotEnoughFunds { .. } => {}
|
||||||
|
ErrorKind::FeeDispute { .. } => {}
|
||||||
|
ErrorKind::FeeExceedsAmount { .. } => {}
|
||||||
|
_ => {
|
||||||
|
// otherwise give full dump
|
||||||
|
error!("Backtrace: {}", e.backtrace().unwrap());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
if args.method == "http" {
|
if args.method == "http" {
|
||||||
api.issue_send_tx(
|
let adapter = HTTPWalletCommAdapter::new();
|
||||||
args.amount,
|
slate = adapter.send_tx_sync(&args.dest, &slate)?;
|
||||||
args.minimum_confirmations,
|
api.finalize_tx(&mut slate)?;
|
||||||
&args.dest,
|
|
||||||
args.max_outputs,
|
|
||||||
args.num_change_outputs,
|
|
||||||
args.selection_strategy_is_use_all,
|
|
||||||
)
|
|
||||||
} else if args.method == "file" {
|
} else if args.method == "file" {
|
||||||
api.send_tx(
|
let adapter = FileWalletCommAdapter::new();
|
||||||
false,
|
adapter.send_tx_async(&args.dest, &slate)?;
|
||||||
args.amount,
|
|
||||||
args.minimum_confirmations,
|
|
||||||
&args.dest,
|
|
||||||
args.max_outputs,
|
|
||||||
args.num_change_outputs,
|
|
||||||
args.selection_strategy_is_use_all,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
error!("unsupported payment method: {}", args.method);
|
error!("unsupported payment method: {}", args.method);
|
||||||
return Err(ErrorKind::ClientCallback("unsupported payment method"))?;
|
return Err(ErrorKind::ClientCallback("unsupported payment method"))?;
|
||||||
}
|
}
|
||||||
|
api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
|
Ok(slate)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize_tx(
|
fn finalize_tx(
|
||||||
&self,
|
&self,
|
||||||
req: Request<Body>,
|
req: Request<Body>,
|
||||||
mut api: APIOwner<T, C, L, K>,
|
mut api: APIOwner<T, C, K>,
|
||||||
) -> Box<Future<Item = Slate, Error = Error> + Send> {
|
) -> Box<Future<Item = Slate, Error = Error> + Send> {
|
||||||
Box::new(
|
Box::new(
|
||||||
parse_body(req).and_then(move |mut slate| match api.finalize_tx(&mut slate) {
|
parse_body(req).and_then(move |mut slate| match api.finalize_tx(&mut slate) {
|
||||||
|
@ -346,7 +362,7 @@ where
|
||||||
fn cancel_tx(
|
fn cancel_tx(
|
||||||
&self,
|
&self,
|
||||||
req: Request<Body>,
|
req: Request<Body>,
|
||||||
mut api: APIOwner<T, C, L, K>,
|
mut api: APIOwner<T, C, K>,
|
||||||
) -> Box<Future<Item = (), Error = Error> + Send> {
|
) -> Box<Future<Item = (), Error = Error> + Send> {
|
||||||
let params = parse_params(&req);
|
let params = parse_params(&req);
|
||||||
if let Some(id_string) = params.get("id") {
|
if let Some(id_string) = params.get("id") {
|
||||||
|
@ -391,7 +407,7 @@ where
|
||||||
fn post_tx(
|
fn post_tx(
|
||||||
&self,
|
&self,
|
||||||
req: Request<Body>,
|
req: Request<Body>,
|
||||||
api: APIOwner<T, C, L, K>,
|
api: APIOwner<T, C, K>,
|
||||||
) -> Box<Future<Item = (), Error = Error> + Send> {
|
) -> Box<Future<Item = (), Error = Error> + Send> {
|
||||||
let params = match req.uri().query() {
|
let params = match req.uri().query() {
|
||||||
Some(query_string) => form_urlencoded::parse(query_string.as_bytes())
|
Some(query_string) => form_urlencoded::parse(query_string.as_bytes())
|
||||||
|
@ -417,7 +433,7 @@ where
|
||||||
fn issue_burn_tx(
|
fn issue_burn_tx(
|
||||||
&self,
|
&self,
|
||||||
_req: Request<Body>,
|
_req: Request<Body>,
|
||||||
mut api: APIOwner<T, C, L, K>,
|
mut api: APIOwner<T, C, K>,
|
||||||
) -> Box<Future<Item = (), Error = Error> + Send> {
|
) -> Box<Future<Item = (), Error = Error> + Send> {
|
||||||
// TODO: Args
|
// TODO: Args
|
||||||
Box::new(match api.issue_burn_tx(60, 10, 1000) {
|
Box::new(match api.issue_burn_tx(60, 10, 1000) {
|
||||||
|
@ -463,11 +479,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized, C, L, K> Handler for OwnerAPIHandler<T, C, L, K>
|
impl<T: ?Sized, C, K> Handler for OwnerAPIHandler<T, C, K>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
C: WalletToNodeClient + 'static,
|
C: NodeClient + 'static,
|
||||||
L: WalletToWalletClient + 'static,
|
|
||||||
K: Keychain + 'static,
|
K: Keychain + 'static,
|
||||||
{
|
{
|
||||||
fn get(&self, req: Request<Body>) -> ResponseFuture {
|
fn get(&self, req: Request<Body>) -> ResponseFuture {
|
||||||
|
@ -498,41 +513,37 @@ where
|
||||||
|
|
||||||
/// API Handler/Wrapper for foreign functions
|
/// API Handler/Wrapper for foreign functions
|
||||||
|
|
||||||
pub struct ForeignAPIHandler<T: ?Sized, C, L, K>
|
pub struct ForeignAPIHandler<T: ?Sized, C, K>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
C: WalletToNodeClient + 'static,
|
C: NodeClient + 'static,
|
||||||
L: WalletToWalletClient + 'static,
|
|
||||||
K: Keychain + 'static,
|
K: Keychain + 'static,
|
||||||
{
|
{
|
||||||
/// Wallet instance
|
/// Wallet instance
|
||||||
pub wallet: Arc<Mutex<T>>,
|
pub wallet: Arc<Mutex<T>>,
|
||||||
phantom: PhantomData<K>,
|
phantom: PhantomData<K>,
|
||||||
phantom_c: PhantomData<C>,
|
phantom_c: PhantomData<C>,
|
||||||
phantom_l: PhantomData<L>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized, C, L, K> ForeignAPIHandler<T, C, L, K>
|
impl<T: ?Sized, C, K> ForeignAPIHandler<T, C, K>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
C: WalletToNodeClient + 'static,
|
C: NodeClient + 'static,
|
||||||
L: WalletToWalletClient + 'static,
|
|
||||||
K: Keychain + 'static,
|
K: Keychain + 'static,
|
||||||
{
|
{
|
||||||
/// create a new api handler
|
/// create a new api handler
|
||||||
pub fn new(wallet: Arc<Mutex<T>>) -> ForeignAPIHandler<T, C, L, K> {
|
pub fn new(wallet: Arc<Mutex<T>>) -> ForeignAPIHandler<T, C, K> {
|
||||||
ForeignAPIHandler {
|
ForeignAPIHandler {
|
||||||
wallet,
|
wallet,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
phantom_c: PhantomData,
|
phantom_c: PhantomData,
|
||||||
phantom_l: PhantomData,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_coinbase(
|
fn build_coinbase(
|
||||||
&self,
|
&self,
|
||||||
req: Request<Body>,
|
req: Request<Body>,
|
||||||
mut api: APIForeign<T, C, L, K>,
|
mut api: APIForeign<T, C, K>,
|
||||||
) -> Box<Future<Item = CbData, Error = Error> + Send> {
|
) -> Box<Future<Item = CbData, Error = Error> + Send> {
|
||||||
Box::new(parse_body(req).and_then(move |block_fees| api.build_coinbase(&block_fees)))
|
Box::new(parse_body(req).and_then(move |block_fees| api.build_coinbase(&block_fees)))
|
||||||
}
|
}
|
||||||
|
@ -540,17 +551,17 @@ where
|
||||||
fn receive_tx(
|
fn receive_tx(
|
||||||
&self,
|
&self,
|
||||||
req: Request<Body>,
|
req: Request<Body>,
|
||||||
mut api: APIForeign<T, C, L, K>,
|
mut api: APIForeign<T, C, K>,
|
||||||
) -> Box<Future<Item = Slate, Error = Error> + Send> {
|
) -> Box<Future<Item = Slate, Error = Error> + Send> {
|
||||||
Box::new(
|
Box::new(parse_body(req).and_then(
|
||||||
parse_body(req).and_then(move |mut slate| match api.receive_tx(&mut slate) {
|
move |mut slate| match api.receive_tx(&mut slate, None) {
|
||||||
Ok(_) => ok(slate.clone()),
|
Ok(_) => ok(slate.clone()),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("receive_tx: failed with error: {}", e);
|
error!("receive_tx: failed with error: {}", e);
|
||||||
err(e)
|
err(e)
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_request(&self, req: Request<Body>) -> WalletResponseFuture {
|
fn handle_request(&self, req: Request<Body>) -> WalletResponseFuture {
|
||||||
|
@ -575,11 +586,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: ?Sized, C, L, K> Handler for ForeignAPIHandler<T, C, L, K>
|
impl<T: ?Sized, C, K> Handler for ForeignAPIHandler<T, C, K>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
C: WalletToNodeClient + Send + Sync + 'static,
|
C: NodeClient + Send + Sync + 'static,
|
||||||
L: WalletToWalletClient + Send + Sync + 'static,
|
|
||||||
K: Keychain + 'static,
|
K: Keychain + 'static,
|
||||||
{
|
{
|
||||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
|
@ -45,20 +45,48 @@ extern crate grin_keychain as keychain;
|
||||||
extern crate grin_store as store;
|
extern crate grin_store as store;
|
||||||
extern crate grin_util as util;
|
extern crate grin_util as util;
|
||||||
|
|
||||||
mod client;
|
mod adapters;
|
||||||
|
pub mod controller;
|
||||||
pub mod display;
|
pub mod display;
|
||||||
mod error;
|
mod error;
|
||||||
pub mod libtx;
|
pub mod libtx;
|
||||||
pub mod libwallet;
|
pub mod libwallet;
|
||||||
pub mod lmdb_wallet;
|
pub mod lmdb_wallet;
|
||||||
|
mod node_clients;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
pub use client::{create_coinbase, HTTPWalletToNodeClient, HTTPWalletToWalletClient};
|
pub use adapters::{
|
||||||
pub use error::{Error, ErrorKind};
|
FileWalletCommAdapter, HTTPWalletCommAdapter, NullWalletCommAdapter, WalletCommAdapter,
|
||||||
pub use libwallet::controller;
|
|
||||||
pub use libwallet::types::{
|
|
||||||
BlockFees, CbData, WalletBackend, WalletInfo, WalletInst, WalletToNodeClient,
|
|
||||||
WalletToWalletClient,
|
|
||||||
};
|
};
|
||||||
|
pub use error::{Error, ErrorKind};
|
||||||
|
pub use libwallet::types::{BlockFees, CbData, NodeClient, WalletBackend, WalletInfo, WalletInst};
|
||||||
pub use lmdb_wallet::{wallet_db_exists, LMDBBackend};
|
pub use lmdb_wallet::{wallet_db_exists, LMDBBackend};
|
||||||
|
pub use node_clients::{create_coinbase, HTTPNodeClient};
|
||||||
pub use types::{WalletConfig, WalletSeed, SEED_FILE};
|
pub use types::{WalletConfig, WalletSeed, SEED_FILE};
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use util::Mutex;
|
||||||
|
|
||||||
|
/// Helper to create an instance of the LMDB wallet
|
||||||
|
pub fn instantiate_wallet(
|
||||||
|
wallet_config: WalletConfig,
|
||||||
|
passphrase: &str,
|
||||||
|
account: &str,
|
||||||
|
node_api_secret: Option<String>,
|
||||||
|
) -> Arc<Mutex<WalletInst<HTTPNodeClient, keychain::ExtKeychain>>> {
|
||||||
|
let client_n = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, node_api_secret);
|
||||||
|
let mut db_wallet = LMDBBackend::new(wallet_config.clone(), passphrase, client_n)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
panic!(
|
||||||
|
"Error creating DB wallet: {} Config: {:?}",
|
||||||
|
e, wallet_config
|
||||||
|
);
|
||||||
|
});
|
||||||
|
db_wallet
|
||||||
|
.set_parent_key_id_by_name(account)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
panic!("Error starting wallet: {}", e);
|
||||||
|
});
|
||||||
|
info!("Using LMDB Backend for wallet");
|
||||||
|
Arc::new(Mutex::new(db_wallet))
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
//! Still experimental, not sure this is the best way to do this
|
//! Still experimental, not sure this is the best way to do this
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::Write;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use util::Mutex;
|
use util::Mutex;
|
||||||
|
@ -31,10 +31,10 @@ use core::core::Transaction;
|
||||||
use core::ser;
|
use core::ser;
|
||||||
use keychain::{Identifier, Keychain};
|
use keychain::{Identifier, Keychain};
|
||||||
use libtx::slate::Slate;
|
use libtx::slate::Slate;
|
||||||
use libwallet::internal::{keys, selection, tx, updater};
|
use libwallet::internal::{keys, tx, updater};
|
||||||
use libwallet::types::{
|
use libwallet::types::{
|
||||||
AcctPathMapping, BlockFees, CbData, OutputData, TxLogEntry, TxWrapper, WalletBackend,
|
AcctPathMapping, BlockFees, CbData, NodeClient, OutputData, TxLogEntry, TxWrapper,
|
||||||
WalletInfo, WalletToNodeClient, WalletToWalletClient,
|
WalletBackend, WalletInfo,
|
||||||
};
|
};
|
||||||
use libwallet::{Error, ErrorKind};
|
use libwallet::{Error, ErrorKind};
|
||||||
use util;
|
use util;
|
||||||
|
@ -42,11 +42,10 @@ use util::secp::pedersen;
|
||||||
|
|
||||||
/// Wrapper around internal API functions, containing a reference to
|
/// Wrapper around internal API functions, containing a reference to
|
||||||
/// the wallet/keychain that they're acting upon
|
/// the wallet/keychain that they're acting upon
|
||||||
pub struct APIOwner<W: ?Sized, C, L, K>
|
pub struct APIOwner<W: ?Sized, C, K>
|
||||||
where
|
where
|
||||||
W: WalletBackend<C, L, K>,
|
W: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
/// Wallet, contains its keychain (TODO: Split these up into 2 traits
|
/// Wallet, contains its keychain (TODO: Split these up into 2 traits
|
||||||
|
@ -54,14 +53,12 @@ where
|
||||||
pub wallet: Arc<Mutex<W>>,
|
pub wallet: Arc<Mutex<W>>,
|
||||||
phantom: PhantomData<K>,
|
phantom: PhantomData<K>,
|
||||||
phantom_c: PhantomData<C>,
|
phantom_c: PhantomData<C>,
|
||||||
phantom_l: PhantomData<L>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: ?Sized, C, L, K> APIOwner<W, C, L, K>
|
impl<W: ?Sized, C, K> APIOwner<W, C, K>
|
||||||
where
|
where
|
||||||
W: WalletBackend<C, L, K>,
|
W: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
/// Create new API instance
|
/// Create new API instance
|
||||||
|
@ -70,7 +67,6 @@ where
|
||||||
wallet: wallet_in,
|
wallet: wallet_in,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
phantom_c: PhantomData,
|
phantom_c: PhantomData,
|
||||||
phantom_l: PhantomData,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,117 +156,29 @@ where
|
||||||
keys::new_acct_path(&mut *w, label)
|
keys::new_acct_path(&mut *w, label)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Issues a send transaction and sends to recipient
|
/// Creates a new partial transaction for the given amount
|
||||||
pub fn issue_send_tx(
|
pub fn initiate_tx(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
src_acct_name: Option<&str>,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
minimum_confirmations: u64,
|
minimum_confirmations: u64,
|
||||||
dest: &str,
|
|
||||||
max_outputs: usize,
|
max_outputs: usize,
|
||||||
num_change_outputs: usize,
|
num_change_outputs: usize,
|
||||||
selection_strategy_is_use_all: bool,
|
selection_strategy_is_use_all: bool,
|
||||||
) -> Result<Slate, Error> {
|
) -> Result<(Slate, impl FnOnce(&mut W, &str) -> Result<(), Error>), Error> {
|
||||||
let mut w = self.wallet.lock();
|
let mut w = self.wallet.lock();
|
||||||
w.open_with_credentials()?;
|
w.open_with_credentials()?;
|
||||||
let parent_key_id = w.parent_key_id();
|
let parent_key_id = match src_acct_name {
|
||||||
|
Some(d) => {
|
||||||
let client;
|
let pm = w.get_acct_path(d.to_owned())?;
|
||||||
let mut slate_out: Slate;
|
match pm {
|
||||||
let lock_fn_out;
|
Some(p) => p.path,
|
||||||
|
None => w.parent_key_id(),
|
||||||
client = w.w2w_client().clone();
|
|
||||||
let (slate, context, lock_fn) = tx::create_send_tx(
|
|
||||||
&mut *w,
|
|
||||||
amount,
|
|
||||||
minimum_confirmations,
|
|
||||||
max_outputs,
|
|
||||||
num_change_outputs,
|
|
||||||
selection_strategy_is_use_all,
|
|
||||||
&parent_key_id,
|
|
||||||
false,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
lock_fn_out = lock_fn;
|
|
||||||
slate_out = match client.send_tx_slate(dest, &slate) {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(e) => {
|
|
||||||
error!(
|
|
||||||
"Communication with receiver failed on SenderInitiation send. Aborting transaction {:?}",
|
|
||||||
e,
|
|
||||||
);
|
|
||||||
return Err(e)?;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
None => w.parent_key_id(),
|
||||||
};
|
};
|
||||||
|
|
||||||
tx::complete_tx(&mut *w, &mut slate_out, &context)?;
|
|
||||||
let tx_hex = util::to_hex(ser::ser_vec(&slate_out.tx).unwrap());
|
|
||||||
|
|
||||||
// lock our inputs
|
|
||||||
lock_fn_out(&mut *w, &tx_hex)?;
|
|
||||||
w.close()?;
|
|
||||||
Ok(slate_out)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Issues a send transaction to the same wallet, without needing communication
|
|
||||||
/// good for consolidating outputs, or can be extended to split outputs to multiple
|
|
||||||
/// accounts
|
|
||||||
pub fn issue_self_tx(
|
|
||||||
&mut self,
|
|
||||||
amount: u64,
|
|
||||||
minimum_confirmations: u64,
|
|
||||||
max_outputs: usize,
|
|
||||||
num_change_outputs: usize,
|
|
||||||
selection_strategy_is_use_all: bool,
|
|
||||||
src_acct_name: &str,
|
|
||||||
dest_acct_name: &str,
|
|
||||||
) -> Result<Slate, Error> {
|
|
||||||
let mut w = self.wallet.lock();
|
|
||||||
w.open_with_credentials()?;
|
|
||||||
let orig_parent_key_id = w.parent_key_id();
|
|
||||||
w.set_parent_key_id_by_name(src_acct_name)?;
|
|
||||||
let parent_key_id = w.parent_key_id();
|
|
||||||
|
|
||||||
let (mut slate, context, lock_fn) = tx::create_send_tx(
|
|
||||||
&mut *w,
|
|
||||||
amount,
|
|
||||||
minimum_confirmations,
|
|
||||||
max_outputs,
|
|
||||||
num_change_outputs,
|
|
||||||
selection_strategy_is_use_all,
|
|
||||||
&parent_key_id,
|
|
||||||
true,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
w.set_parent_key_id_by_name(dest_acct_name)?;
|
|
||||||
let parent_key_id = w.parent_key_id();
|
|
||||||
tx::receive_tx(&mut *w, &mut slate, &parent_key_id, true)?;
|
|
||||||
|
|
||||||
tx::complete_tx(&mut *w, &mut slate, &context)?;
|
|
||||||
let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap());
|
|
||||||
|
|
||||||
// lock our inputs
|
|
||||||
lock_fn(&mut *w, &tx_hex)?;
|
|
||||||
w.set_parent_key_id(orig_parent_key_id);
|
|
||||||
w.close()?;
|
|
||||||
Ok(slate)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a transaction to send to file so a user can transmit it to the
|
|
||||||
/// receiver in whichever way they see fit (aka carrier pigeon mode).
|
|
||||||
pub fn send_tx(
|
|
||||||
&mut self,
|
|
||||||
write_to_disk: bool,
|
|
||||||
amount: u64,
|
|
||||||
minimum_confirmations: u64,
|
|
||||||
dest: &str,
|
|
||||||
max_outputs: usize,
|
|
||||||
num_change_outputs: usize,
|
|
||||||
selection_strategy_is_use_all: bool,
|
|
||||||
) -> Result<Slate, Error> {
|
|
||||||
let mut w = self.wallet.lock();
|
|
||||||
w.open_with_credentials()?;
|
|
||||||
let parent_key_id = w.parent_key_id();
|
|
||||||
|
|
||||||
let (slate, context, lock_fn) = tx::create_send_tx(
|
let (slate, context, lock_fn) = tx::create_send_tx(
|
||||||
&mut *w,
|
&mut *w,
|
||||||
amount,
|
amount,
|
||||||
|
@ -281,24 +189,30 @@ where
|
||||||
&parent_key_id,
|
&parent_key_id,
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
if write_to_disk {
|
|
||||||
let mut pub_tx = File::create(dest)?;
|
|
||||||
pub_tx.write_all(json::to_string(&slate).unwrap().as_bytes())?;
|
|
||||||
pub_tx.sync_all()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Save the aggsig context in our DB for when we
|
||||||
|
// recieve the transaction back
|
||||||
{
|
{
|
||||||
let mut batch = w.batch()?;
|
let mut batch = w.batch()?;
|
||||||
batch.save_private_context(slate.id.as_bytes(), &context)?;
|
batch.save_private_context(slate.id.as_bytes(), &context)?;
|
||||||
batch.commit()?;
|
batch.commit()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap());
|
|
||||||
|
|
||||||
// lock our inputs
|
|
||||||
lock_fn(&mut *w, &tx_hex)?;
|
|
||||||
w.close()?;
|
w.close()?;
|
||||||
Ok(slate)
|
Ok((slate, lock_fn))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lock outputs associated with a given slate/transaction
|
||||||
|
pub fn tx_lock_outputs(
|
||||||
|
&mut self,
|
||||||
|
slate: &Slate,
|
||||||
|
lock_fn: impl FnOnce(&mut W, &str) -> Result<(), Error>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut w = self.wallet.lock();
|
||||||
|
w.open_with_credentials()?;
|
||||||
|
let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap());
|
||||||
|
lock_fn(&mut *w, &tx_hex)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sender finalization of the transaction. Takes the file returned by the
|
/// Sender finalization of the transaction. Takes the file returned by the
|
||||||
|
@ -308,7 +222,6 @@ where
|
||||||
pub fn finalize_tx(&mut self, slate: &mut Slate) -> Result<(), Error> {
|
pub fn finalize_tx(&mut self, slate: &mut Slate) -> Result<(), Error> {
|
||||||
let mut w = self.wallet.lock();
|
let mut w = self.wallet.lock();
|
||||||
w.open_with_credentials()?;
|
w.open_with_credentials()?;
|
||||||
|
|
||||||
let context = w.get_private_context(slate.id.as_bytes())?;
|
let context = w.get_private_context(slate.id.as_bytes())?;
|
||||||
tx::complete_tx(&mut *w, slate, &context)?;
|
tx::complete_tx(&mut *w, slate, &context)?;
|
||||||
{
|
{
|
||||||
|
@ -513,11 +426,10 @@ where
|
||||||
|
|
||||||
/// Wrapper around external API functions, intended to communicate
|
/// Wrapper around external API functions, intended to communicate
|
||||||
/// with other parties
|
/// with other parties
|
||||||
pub struct APIForeign<W: ?Sized, C, L, K>
|
pub struct APIForeign<W: ?Sized, C, K>
|
||||||
where
|
where
|
||||||
W: WalletBackend<C, L, K>,
|
W: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
/// Wallet, contains its keychain (TODO: Split these up into 2 traits
|
/// Wallet, contains its keychain (TODO: Split these up into 2 traits
|
||||||
|
@ -525,14 +437,12 @@ where
|
||||||
pub wallet: Arc<Mutex<W>>,
|
pub wallet: Arc<Mutex<W>>,
|
||||||
phantom: PhantomData<K>,
|
phantom: PhantomData<K>,
|
||||||
phantom_c: PhantomData<C>,
|
phantom_c: PhantomData<C>,
|
||||||
phantom_l: PhantomData<L>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W: ?Sized, C, L, K> APIForeign<W, C, L, K>
|
impl<'a, W: ?Sized, C, K> APIForeign<W, C, K>
|
||||||
where
|
where
|
||||||
W: WalletBackend<C, L, K>,
|
W: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
/// Create new API instance
|
/// Create new API instance
|
||||||
|
@ -541,7 +451,6 @@ where
|
||||||
wallet: wallet_in,
|
wallet: wallet_in,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
phantom_c: PhantomData,
|
phantom_c: PhantomData,
|
||||||
phantom_l: PhantomData,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,52 +463,24 @@ where
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A sender provided a transaction file with appropriate public keys and
|
|
||||||
/// metadata. Complete the receivers' end of it to generate another file
|
|
||||||
/// to send back.
|
|
||||||
pub fn file_receive_tx(&mut self, source: &str) -> Result<(), Error> {
|
|
||||||
let mut pub_tx_f = File::open(source)?;
|
|
||||||
let mut content = String::new();
|
|
||||||
pub_tx_f.read_to_string(&mut content)?;
|
|
||||||
let mut slate: Slate = json::from_str(&content).map_err(|_| ErrorKind::Format)?;
|
|
||||||
|
|
||||||
let mut wallet = self.wallet.lock();
|
|
||||||
wallet.open_with_credentials()?;
|
|
||||||
let parent_key_id = wallet.parent_key_id();
|
|
||||||
|
|
||||||
// create an output using the amount in the slate
|
|
||||||
let (_, mut context, receiver_create_fn) = selection::build_recipient_output_with_slate(
|
|
||||||
&mut *wallet,
|
|
||||||
&mut slate,
|
|
||||||
parent_key_id,
|
|
||||||
false,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// fill public keys
|
|
||||||
let _ = slate.fill_round_1(
|
|
||||||
wallet.keychain(),
|
|
||||||
&mut context.sec_key,
|
|
||||||
&context.sec_nonce,
|
|
||||||
1,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// perform partial sig
|
|
||||||
let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 1)?;
|
|
||||||
|
|
||||||
// save to file
|
|
||||||
let mut pub_tx = File::create(source.to_owned() + ".response")?;
|
|
||||||
pub_tx.write_all(json::to_string(&slate).unwrap().as_bytes())?;
|
|
||||||
|
|
||||||
// Save output in wallet
|
|
||||||
let _ = receiver_create_fn(&mut wallet);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Receive a transaction from a sender
|
/// Receive a transaction from a sender
|
||||||
pub fn receive_tx(&mut self, slate: &mut Slate) -> Result<(), Error> {
|
pub fn receive_tx(
|
||||||
|
&mut self,
|
||||||
|
slate: &mut Slate,
|
||||||
|
dest_acct_name: Option<&str>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let mut w = self.wallet.lock();
|
let mut w = self.wallet.lock();
|
||||||
w.open_with_credentials()?;
|
w.open_with_credentials()?;
|
||||||
let parent_key_id = w.parent_key_id();
|
let parent_key_id = match dest_acct_name {
|
||||||
|
Some(d) => {
|
||||||
|
let pm = w.get_acct_path(d.to_owned())?;
|
||||||
|
match pm {
|
||||||
|
Some(p) => p.path,
|
||||||
|
None => w.parent_key_id(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => w.parent_key_id(),
|
||||||
|
};
|
||||||
let res = tx::receive_tx(&mut *w, slate, &parent_key_id, false);
|
let res = tx::receive_tx(&mut *w, slate, &parent_key_id, false);
|
||||||
w.close()?;
|
w.close()?;
|
||||||
|
|
||||||
|
|
|
@ -15,14 +15,13 @@
|
||||||
//! Wallet key management functions
|
//! Wallet key management functions
|
||||||
use keychain::{ChildNumber, ExtKeychain, Identifier, Keychain};
|
use keychain::{ChildNumber, ExtKeychain, Identifier, Keychain};
|
||||||
use libwallet::error::{Error, ErrorKind};
|
use libwallet::error::{Error, ErrorKind};
|
||||||
use libwallet::types::{AcctPathMapping, WalletBackend, WalletToNodeClient, WalletToWalletClient};
|
use libwallet::types::{AcctPathMapping, NodeClient, WalletBackend};
|
||||||
|
|
||||||
/// Get next available key in the wallet for a given parent
|
/// Get next available key in the wallet for a given parent
|
||||||
pub fn next_available_key<T: ?Sized, C, L, K>(wallet: &mut T) -> Result<Identifier, Error>
|
pub fn next_available_key<T: ?Sized, C, K>(wallet: &mut T) -> Result<Identifier, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let child = wallet.next_child()?;
|
let child = wallet.next_child()?;
|
||||||
|
@ -30,14 +29,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve an existing key from a wallet
|
/// Retrieve an existing key from a wallet
|
||||||
pub fn retrieve_existing_key<T: ?Sized, C, L, K>(
|
pub fn retrieve_existing_key<T: ?Sized, C, K>(
|
||||||
wallet: &T,
|
wallet: &T,
|
||||||
key_id: Identifier,
|
key_id: Identifier,
|
||||||
) -> Result<(Identifier, u32), Error>
|
) -> Result<(Identifier, u32), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let existing = wallet.get(&key_id)?;
|
let existing = wallet.get(&key_id)?;
|
||||||
|
@ -47,22 +45,20 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a list of account to BIP32 path mappings
|
/// Returns a list of account to BIP32 path mappings
|
||||||
pub fn accounts<T: ?Sized, C, L, K>(wallet: &mut T) -> Result<Vec<AcctPathMapping>, Error>
|
pub fn accounts<T: ?Sized, C, K>(wallet: &mut T) -> Result<Vec<AcctPathMapping>, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
Ok(wallet.acct_path_iter().collect())
|
Ok(wallet.acct_path_iter().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds an new parent account path with a given label
|
/// Adds an new parent account path with a given label
|
||||||
pub fn new_acct_path<T: ?Sized, C, L, K>(wallet: &mut T, label: &str) -> Result<Identifier, Error>
|
pub fn new_acct_path<T: ?Sized, C, K>(wallet: &mut T, label: &str) -> Result<Identifier, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let label = label.to_owned();
|
let label = label.to_owned();
|
||||||
|
@ -100,15 +96,14 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds/sets a particular account path with a given label
|
/// Adds/sets a particular account path with a given label
|
||||||
pub fn set_acct_path<T: ?Sized, C, L, K>(
|
pub fn set_acct_path<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
label: &str,
|
label: &str,
|
||||||
path: &Identifier,
|
path: &Identifier,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let label = label.to_owned();
|
let label = label.to_owned();
|
||||||
|
|
|
@ -42,14 +42,13 @@ struct OutputResult {
|
||||||
pub blinding: SecretKey,
|
pub blinding: SecretKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identify_utxo_outputs<T, C, L, K>(
|
fn identify_utxo_outputs<T, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)>,
|
outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)>,
|
||||||
) -> Result<Vec<OutputResult>, Error>
|
) -> Result<Vec<OutputResult>, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let mut wallet_outputs: Vec<OutputResult> = Vec::new();
|
let mut wallet_outputs: Vec<OutputResult> = Vec::new();
|
||||||
|
@ -99,11 +98,10 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restore a wallet
|
/// Restore a wallet
|
||||||
pub fn restore<T, C, L, K>(wallet: &mut T) -> Result<(), Error>
|
pub fn restore<T, C, K>(wallet: &mut T) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// Don't proceed if wallet_data has anything in it
|
// Don't proceed if wallet_data has anything in it
|
||||||
|
|
|
@ -25,7 +25,7 @@ use libwallet::types::*;
|
||||||
/// and saves the private wallet identifiers of our selected outputs
|
/// and saves the private wallet identifiers of our selected outputs
|
||||||
/// into our transaction context
|
/// into our transaction context
|
||||||
|
|
||||||
pub fn build_send_tx_slate<T: ?Sized, C, L, K>(
|
pub fn build_send_tx_slate<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
num_participants: usize,
|
num_participants: usize,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
|
@ -46,9 +46,8 @@ pub fn build_send_tx_slate<T: ?Sized, C, L, K>(
|
||||||
Error,
|
Error,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let (elems, inputs, change_amounts_derivations, amount, fee) = select_send_tx(
|
let (elems, inputs, change_amounts_derivations, amount, fee) = select_send_tx(
|
||||||
|
@ -145,7 +144,7 @@ where
|
||||||
/// returning the key of the fresh output and a closure
|
/// returning the key of the fresh output and a closure
|
||||||
/// that actually performs the addition of the output to the
|
/// that actually performs the addition of the output to the
|
||||||
/// wallet
|
/// wallet
|
||||||
pub fn build_recipient_output_with_slate<T: ?Sized, C, L, K>(
|
pub fn build_recipient_output_with_slate<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
slate: &mut Slate,
|
slate: &mut Slate,
|
||||||
parent_key_id: Identifier,
|
parent_key_id: Identifier,
|
||||||
|
@ -159,9 +158,8 @@ pub fn build_recipient_output_with_slate<T: ?Sized, C, L, K>(
|
||||||
Error,
|
Error,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// Create a potential output for this transaction
|
// Create a potential output for this transaction
|
||||||
|
@ -219,7 +217,7 @@ where
|
||||||
/// Builds a transaction to send to someone from the HD seed associated with the
|
/// 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,
|
/// wallet and the amount to send. Handles reading through the wallet data file,
|
||||||
/// selecting outputs to spend and building the change.
|
/// selecting outputs to spend and building the change.
|
||||||
pub fn select_send_tx<T: ?Sized, C, L, K>(
|
pub fn select_send_tx<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
current_height: u64,
|
current_height: u64,
|
||||||
|
@ -240,9 +238,8 @@ pub fn select_send_tx<T: ?Sized, C, L, K>(
|
||||||
Error,
|
Error,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// select some spendable coins from the wallet
|
// select some spendable coins from the wallet
|
||||||
|
@ -329,7 +326,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Selects inputs and change for a transaction
|
/// Selects inputs and change for a transaction
|
||||||
pub fn inputs_and_change<T: ?Sized, C, L, K>(
|
pub fn inputs_and_change<T: ?Sized, C, K>(
|
||||||
coins: &Vec<OutputData>,
|
coins: &Vec<OutputData>,
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
|
@ -337,9 +334,8 @@ pub fn inputs_and_change<T: ?Sized, C, L, K>(
|
||||||
num_change_outputs: usize,
|
num_change_outputs: usize,
|
||||||
) -> Result<(Vec<Box<build::Append<K>>>, Vec<(u64, Identifier)>), Error>
|
) -> Result<(Vec<Box<build::Append<K>>>, Vec<(u64, Identifier)>), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let mut parts = vec![];
|
let mut parts = vec![];
|
||||||
|
@ -401,7 +397,7 @@ where
|
||||||
/// we should pass something other than a bool in.
|
/// we should pass something other than a bool in.
|
||||||
/// TODO: Possibly move this into another trait to be owned by a wallet?
|
/// TODO: Possibly move this into another trait to be owned by a wallet?
|
||||||
|
|
||||||
pub fn select_coins<T: ?Sized, C, L, K>(
|
pub fn select_coins<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
current_height: u64,
|
current_height: u64,
|
||||||
|
@ -412,9 +408,8 @@ pub fn select_coins<T: ?Sized, C, L, K>(
|
||||||
) -> (usize, Vec<OutputData>)
|
) -> (usize, Vec<OutputData>)
|
||||||
// max_outputs_available, Outputs
|
// max_outputs_available, Outputs
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// first find all eligible outputs based on number of confirmations
|
// first find all eligible outputs based on number of confirmations
|
||||||
|
|
|
@ -24,23 +24,20 @@ use keychain::{Identifier, Keychain};
|
||||||
use libtx::slate::Slate;
|
use libtx::slate::Slate;
|
||||||
use libtx::{build, tx_fee};
|
use libtx::{build, tx_fee};
|
||||||
use libwallet::internal::{selection, updater};
|
use libwallet::internal::{selection, updater};
|
||||||
use libwallet::types::{
|
use libwallet::types::{Context, NodeClient, TxLogEntryType, WalletBackend};
|
||||||
Context, TxLogEntryType, WalletBackend, WalletToNodeClient, WalletToWalletClient,
|
|
||||||
};
|
|
||||||
use libwallet::{Error, ErrorKind};
|
use libwallet::{Error, ErrorKind};
|
||||||
|
|
||||||
/// Receive a transaction, modifying the slate accordingly (which can then be
|
/// Receive a transaction, modifying the slate accordingly (which can then be
|
||||||
/// sent back to sender for posting)
|
/// sent back to sender for posting)
|
||||||
pub fn receive_tx<T: ?Sized, C, L, K>(
|
pub fn receive_tx<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
slate: &mut Slate,
|
slate: &mut Slate,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
is_self: bool,
|
is_self: bool,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// create an output using the amount in the slate
|
// create an output using the amount in the slate
|
||||||
|
@ -70,7 +67,7 @@ where
|
||||||
|
|
||||||
/// Issue a new transaction to the provided sender by spending some of our
|
/// Issue a new transaction to the provided sender by spending some of our
|
||||||
/// wallet
|
/// wallet
|
||||||
pub fn create_send_tx<T: ?Sized, C, L, K>(
|
pub fn create_send_tx<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
minimum_confirmations: u64,
|
minimum_confirmations: u64,
|
||||||
|
@ -88,9 +85,8 @@ pub fn create_send_tx<T: ?Sized, C, L, K>(
|
||||||
Error,
|
Error,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// Get lock height
|
// Get lock height
|
||||||
|
@ -135,15 +131,14 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete a transaction as the sender
|
/// Complete a transaction as the sender
|
||||||
pub fn complete_tx<T: ?Sized, C, L, K>(
|
pub fn complete_tx<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
slate: &mut Slate,
|
slate: &mut Slate,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 0)?;
|
let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 0)?;
|
||||||
|
@ -156,16 +151,15 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rollback outputs associated with a transaction in the wallet
|
/// Rollback outputs associated with a transaction in the wallet
|
||||||
pub fn cancel_tx<T: ?Sized, C, L, K>(
|
pub fn cancel_tx<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
tx_id: Option<u32>,
|
tx_id: Option<u32>,
|
||||||
tx_slate_id: Option<Uuid>,
|
tx_slate_id: Option<Uuid>,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let mut tx_id_string = String::new();
|
let mut tx_id_string = String::new();
|
||||||
|
@ -194,15 +188,14 @@ where
|
||||||
|
|
||||||
/// Retrieve the associated stored finalised hex Transaction for a given transaction Id
|
/// Retrieve the associated stored finalised hex Transaction for a given transaction Id
|
||||||
/// as well as whether it's been confirmed
|
/// as well as whether it's been confirmed
|
||||||
pub fn retrieve_tx_hex<T: ?Sized, C, L, K>(
|
pub fn retrieve_tx_hex<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
tx_id: u32,
|
tx_id: u32,
|
||||||
) -> Result<(bool, Option<String>), Error>
|
) -> Result<(bool, Option<String>), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let tx_vec = updater::retrieve_txs(wallet, Some(tx_id), None, parent_key_id)?;
|
let tx_vec = updater::retrieve_txs(wallet, Some(tx_id), None, parent_key_id)?;
|
||||||
|
@ -214,7 +207,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Issue a burn tx
|
/// Issue a burn tx
|
||||||
pub fn issue_burn_tx<T: ?Sized, C, L, K>(
|
pub fn issue_burn_tx<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
minimum_confirmations: u64,
|
minimum_confirmations: u64,
|
||||||
|
@ -222,9 +215,8 @@ pub fn issue_burn_tx<T: ?Sized, C, L, K>(
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
) -> Result<Transaction, Error>
|
) -> Result<Transaction, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
|
|
|
@ -28,23 +28,22 @@ use libwallet;
|
||||||
use libwallet::error::{Error, ErrorKind};
|
use libwallet::error::{Error, ErrorKind};
|
||||||
use libwallet::internal::keys;
|
use libwallet::internal::keys;
|
||||||
use libwallet::types::{
|
use libwallet::types::{
|
||||||
BlockFees, CbData, OutputData, OutputStatus, TxLogEntry, TxLogEntryType, WalletBackend,
|
BlockFees, CbData, NodeClient, OutputData, OutputStatus, TxLogEntry, TxLogEntryType,
|
||||||
WalletInfo, WalletToNodeClient, WalletToWalletClient,
|
WalletBackend, WalletInfo,
|
||||||
};
|
};
|
||||||
use util;
|
use util;
|
||||||
use util::secp::pedersen;
|
use util::secp::pedersen;
|
||||||
|
|
||||||
/// Retrieve all of the outputs (doesn't attempt to update from node)
|
/// Retrieve all of the outputs (doesn't attempt to update from node)
|
||||||
pub fn retrieve_outputs<T: ?Sized, C, L, K>(
|
pub fn retrieve_outputs<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
show_spent: bool,
|
show_spent: bool,
|
||||||
tx_id: Option<u32>,
|
tx_id: Option<u32>,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
) -> Result<Vec<(OutputData, pedersen::Commitment)>, Error>
|
) -> Result<Vec<(OutputData, pedersen::Commitment)>, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// just read the wallet here, no need for a write lock
|
// just read the wallet here, no need for a write lock
|
||||||
|
@ -79,16 +78,15 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve all of the transaction entries, or a particular entry
|
/// Retrieve all of the transaction entries, or a particular entry
|
||||||
pub fn retrieve_txs<T: ?Sized, C, L, K>(
|
pub fn retrieve_txs<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
tx_id: Option<u32>,
|
tx_id: Option<u32>,
|
||||||
tx_slate_id: Option<Uuid>,
|
tx_slate_id: Option<Uuid>,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
) -> Result<Vec<TxLogEntry>, Error>
|
) -> Result<Vec<TxLogEntry>, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// just read the wallet here, no need for a write lock
|
// just read the wallet here, no need for a write lock
|
||||||
|
@ -117,14 +115,13 @@ where
|
||||||
}
|
}
|
||||||
/// Refreshes the outputs in a wallet with the latest information
|
/// Refreshes the outputs in a wallet with the latest information
|
||||||
/// from a node
|
/// from a node
|
||||||
pub fn refresh_outputs<T: ?Sized, C, L, K>(
|
pub fn refresh_outputs<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let height = wallet.w2n_client().get_chain_height()?;
|
let height = wallet.w2n_client().get_chain_height()?;
|
||||||
|
@ -134,14 +131,13 @@ where
|
||||||
|
|
||||||
/// build a local map of wallet outputs keyed by commit
|
/// build a local map of wallet outputs keyed by commit
|
||||||
/// and a list of outputs we want to query the node for
|
/// and a list of outputs we want to query the node for
|
||||||
pub fn map_wallet_outputs<T: ?Sized, C, L, K>(
|
pub fn map_wallet_outputs<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
) -> Result<HashMap<pedersen::Commitment, Identifier>, Error>
|
) -> Result<HashMap<pedersen::Commitment, Identifier>, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let mut wallet_outputs: HashMap<pedersen::Commitment, Identifier> = HashMap::new();
|
let mut wallet_outputs: HashMap<pedersen::Commitment, Identifier> = HashMap::new();
|
||||||
|
@ -157,16 +153,15 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cancel transaction and associated outputs
|
/// Cancel transaction and associated outputs
|
||||||
pub fn cancel_tx_and_outputs<T: ?Sized, C, L, K>(
|
pub fn cancel_tx_and_outputs<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
tx: TxLogEntry,
|
tx: TxLogEntry,
|
||||||
outputs: Vec<OutputData>,
|
outputs: Vec<OutputData>,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
) -> Result<(), libwallet::Error>
|
) -> Result<(), libwallet::Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let mut batch = wallet.batch()?;
|
let mut batch = wallet.batch()?;
|
||||||
|
@ -194,7 +189,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply refreshed API output data to the wallet
|
/// Apply refreshed API output data to the wallet
|
||||||
pub fn apply_api_outputs<T: ?Sized, C, L, K>(
|
pub fn apply_api_outputs<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
wallet_outputs: &HashMap<pedersen::Commitment, Identifier>,
|
wallet_outputs: &HashMap<pedersen::Commitment, Identifier>,
|
||||||
api_outputs: &HashMap<pedersen::Commitment, (String, u64)>,
|
api_outputs: &HashMap<pedersen::Commitment, (String, u64)>,
|
||||||
|
@ -202,9 +197,8 @@ pub fn apply_api_outputs<T: ?Sized, C, L, K>(
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
) -> Result<(), libwallet::Error>
|
) -> Result<(), libwallet::Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
// now for each commit, find the output in the wallet and the corresponding
|
// now for each commit, find the output in the wallet and the corresponding
|
||||||
|
@ -275,15 +269,14 @@ where
|
||||||
|
|
||||||
/// Builds a single api query to retrieve the latest output data from the node.
|
/// Builds a single api query to retrieve the latest output data from the node.
|
||||||
/// So we can refresh the local wallet outputs.
|
/// So we can refresh the local wallet outputs.
|
||||||
fn refresh_output_state<T: ?Sized, C, L, K>(
|
fn refresh_output_state<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
height: u64,
|
height: u64,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
debug!("Refreshing wallet outputs");
|
debug!("Refreshing wallet outputs");
|
||||||
|
@ -302,11 +295,10 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_old_unconfirmed<T: ?Sized, C, L, K>(wallet: &mut T, height: u64) -> Result<(), Error>
|
fn clean_old_unconfirmed<T: ?Sized, C, K>(wallet: &mut T, height: u64) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
if height < 500 {
|
if height < 500 {
|
||||||
|
@ -328,14 +320,13 @@ where
|
||||||
|
|
||||||
/// Retrieve summary info about the wallet
|
/// Retrieve summary info about the wallet
|
||||||
/// caller should refresh first if desired
|
/// caller should refresh first if desired
|
||||||
pub fn retrieve_info<T: ?Sized, C, L, K>(
|
pub fn retrieve_info<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
parent_key_id: &Identifier,
|
parent_key_id: &Identifier,
|
||||||
) -> Result<WalletInfo, Error>
|
) -> Result<WalletInfo, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let current_height = wallet.last_confirmed_height()?;
|
let current_height = wallet.last_confirmed_height()?;
|
||||||
|
@ -373,14 +364,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a coinbase output and insert into wallet
|
/// Build a coinbase output and insert into wallet
|
||||||
pub fn build_coinbase<T: ?Sized, C, L, K>(
|
pub fn build_coinbase<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
block_fees: &BlockFees,
|
block_fees: &BlockFees,
|
||||||
) -> Result<CbData, Error>
|
) -> Result<CbData, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let (out, kern, block_fees) = receive_coinbase(wallet, block_fees).context(ErrorKind::Node)?;
|
let (out, kern, block_fees) = receive_coinbase(wallet, block_fees).context(ErrorKind::Node)?;
|
||||||
|
@ -403,14 +393,13 @@ where
|
||||||
|
|
||||||
//TODO: Split up the output creation and the wallet insertion
|
//TODO: Split up the output creation and the wallet insertion
|
||||||
/// Build a coinbase output and the corresponding kernel
|
/// Build a coinbase output and the corresponding kernel
|
||||||
pub fn receive_coinbase<T: ?Sized, C, L, K>(
|
pub fn receive_coinbase<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
block_fees: &BlockFees,
|
block_fees: &BlockFees,
|
||||||
) -> Result<(Output, TxKernel, BlockFees), Error>
|
) -> Result<(Output, TxKernel, BlockFees), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
let height = block_fees.height;
|
let height = block_fees.height;
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod controller;
|
|
||||||
mod error;
|
mod error;
|
||||||
pub mod internal;
|
pub mod internal;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
|
@ -38,18 +38,16 @@ use util::secp::key::{PublicKey, SecretKey};
|
||||||
use util::secp::{self, pedersen, Secp256k1};
|
use util::secp::{self, pedersen, Secp256k1};
|
||||||
|
|
||||||
/// Combined trait to allow dynamic wallet dispatch
|
/// Combined trait to allow dynamic wallet dispatch
|
||||||
pub trait WalletInst<C, L, K>: WalletBackend<C, L, K> + Send + Sync + 'static
|
pub trait WalletInst<C, K>: WalletBackend<C, K> + Send + Sync + 'static
|
||||||
where
|
where
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
impl<T, C, L, K> WalletInst<C, L, K> for T
|
impl<T, C, K> WalletInst<C, K> for T
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, L, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -57,10 +55,9 @@ where
|
||||||
/// Wallets should implement this backend for their storage. All functions
|
/// Wallets should implement this backend for their storage. All functions
|
||||||
/// here expect that the wallet instance has instantiated itself or stored
|
/// here expect that the wallet instance has instantiated itself or stored
|
||||||
/// whatever credentials it needs
|
/// whatever credentials it needs
|
||||||
pub trait WalletBackend<C, L, K>
|
pub trait WalletBackend<C, K>
|
||||||
where
|
where
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
/// Initialize with whatever stored credentials we have
|
/// Initialize with whatever stored credentials we have
|
||||||
|
@ -75,9 +72,6 @@ where
|
||||||
/// Return the client being used to communicate with the node
|
/// Return the client being used to communicate with the node
|
||||||
fn w2n_client(&mut self) -> &mut C;
|
fn w2n_client(&mut self) -> &mut C;
|
||||||
|
|
||||||
/// Return the client being used to communicate with other wallets
|
|
||||||
fn w2w_client(&mut self) -> &mut L;
|
|
||||||
|
|
||||||
/// Set parent key id by stored account name
|
/// Set parent key id by stored account name
|
||||||
fn set_parent_key_id_by_name(&mut self, label: &str) -> Result<(), Error>;
|
fn set_parent_key_id_by_name(&mut self, label: &str) -> Result<(), Error>;
|
||||||
|
|
||||||
|
@ -189,7 +183,7 @@ where
|
||||||
|
|
||||||
/// Encapsulate all wallet-node communication functions. No functions within libwallet
|
/// Encapsulate all wallet-node communication functions. No functions within libwallet
|
||||||
/// should care about communication details
|
/// should care about communication details
|
||||||
pub trait WalletToNodeClient: Sync + Send + Clone {
|
pub trait NodeClient: Sync + Send + Clone {
|
||||||
/// Return the URL of the check node
|
/// Return the URL of the check node
|
||||||
fn node_url(&self) -> &str;
|
fn node_url(&self) -> &str;
|
||||||
|
|
||||||
|
@ -228,16 +222,6 @@ pub trait WalletToNodeClient: Sync + Send + Clone {
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encapsulate wallet to wallet communication functions
|
|
||||||
pub trait WalletToWalletClient: Sync + Send + Clone {
|
|
||||||
/// Call the wallet API to create a coinbase transaction
|
|
||||||
fn create_coinbase(&self, dest: &str, block_fees: &BlockFees) -> Result<CbData, Error>;
|
|
||||||
|
|
||||||
/// Send a transaction slate to another listening wallet and return result
|
|
||||||
/// TODO: Probably need a slate wrapper type
|
|
||||||
fn send_tx_slate(&self, addr: &str, slate: &Slate) -> Result<Slate, Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about an output that's being tracked by the wallet. Must be
|
/// Information about an output that's being tracked by the wallet. Must be
|
||||||
/// enough to reconstruct the commitment associated with the ouput when the
|
/// enough to reconstruct the commitment associated with the ouput when the
|
||||||
/// root private key is known.
|
/// root private key is known.
|
||||||
|
|
|
@ -51,7 +51,7 @@ pub fn wallet_db_exists(config: WalletConfig) -> bool {
|
||||||
db_path.exists()
|
db_path.exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LMDBBackend<C, L, K> {
|
pub struct LMDBBackend<C, K> {
|
||||||
db: store::Store,
|
db: store::Store,
|
||||||
config: WalletConfig,
|
config: WalletConfig,
|
||||||
/// passphrase: TODO better ways of dealing with this other than storing
|
/// passphrase: TODO better ways of dealing with this other than storing
|
||||||
|
@ -62,17 +62,10 @@ pub struct LMDBBackend<C, L, K> {
|
||||||
parent_key_id: Identifier,
|
parent_key_id: Identifier,
|
||||||
/// wallet to node client
|
/// wallet to node client
|
||||||
w2n_client: C,
|
w2n_client: C,
|
||||||
/// w2w client
|
|
||||||
w2w_client: L,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, L, K> LMDBBackend<C, L, K> {
|
impl<C, K> LMDBBackend<C, K> {
|
||||||
pub fn new(
|
pub fn new(config: WalletConfig, passphrase: &str, n_client: C) -> Result<Self, Error> {
|
||||||
config: WalletConfig,
|
|
||||||
passphrase: &str,
|
|
||||||
n_client: C,
|
|
||||||
w_client: L,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let db_path = path::Path::new(&config.data_file_dir).join(DB_DIR);
|
let db_path = path::Path::new(&config.data_file_dir).join(DB_DIR);
|
||||||
fs::create_dir_all(&db_path).expect("Couldn't create wallet backend directory!");
|
fs::create_dir_all(&db_path).expect("Couldn't create wallet backend directory!");
|
||||||
|
|
||||||
|
@ -82,7 +75,7 @@ impl<C, L, K> LMDBBackend<C, L, K> {
|
||||||
// Make sure default wallet derivation path always exists
|
// Make sure default wallet derivation path always exists
|
||||||
let default_account = AcctPathMapping {
|
let default_account = AcctPathMapping {
|
||||||
label: "default".to_owned(),
|
label: "default".to_owned(),
|
||||||
path: LMDBBackend::<C, L, K>::default_path(),
|
path: LMDBBackend::<C, K>::default_path(),
|
||||||
};
|
};
|
||||||
let acct_key = to_key(
|
let acct_key = to_key(
|
||||||
ACCOUNT_PATH_MAPPING_PREFIX,
|
ACCOUNT_PATH_MAPPING_PREFIX,
|
||||||
|
@ -100,9 +93,8 @@ impl<C, L, K> LMDBBackend<C, L, K> {
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
passphrase: String::from(passphrase),
|
passphrase: String::from(passphrase),
|
||||||
keychain: None,
|
keychain: None,
|
||||||
parent_key_id: LMDBBackend::<C, L, K>::default_path(),
|
parent_key_id: LMDBBackend::<C, K>::default_path(),
|
||||||
w2n_client: n_client,
|
w2n_client: n_client,
|
||||||
w2w_client: w_client,
|
|
||||||
};
|
};
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
@ -122,10 +114,9 @@ impl<C, L, K> LMDBBackend<C, L, K> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, L, K> WalletBackend<C, L, K> for LMDBBackend<C, L, K>
|
impl<C, K> WalletBackend<C, K> for LMDBBackend<C, K>
|
||||||
where
|
where
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
/// Initialise with whatever stored credentials we have
|
/// Initialise with whatever stored credentials we have
|
||||||
|
@ -155,11 +146,6 @@ where
|
||||||
&mut self.w2n_client
|
&mut self.w2n_client
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the wallet to wallet client being used
|
|
||||||
fn w2w_client(&mut self) -> &mut L {
|
|
||||||
&mut self.w2w_client
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set parent path by account name
|
/// Set parent path by account name
|
||||||
fn set_parent_key_id_by_name(&mut self, label: &str) -> Result<(), Error> {
|
fn set_parent_key_id_by_name(&mut self, label: &str) -> Result<(), Error> {
|
||||||
let label = label.to_owned();
|
let label = label.to_owned();
|
||||||
|
@ -292,21 +278,21 @@ where
|
||||||
|
|
||||||
/// An atomic batch in which all changes can be committed all at once or
|
/// An atomic batch in which all changes can be committed all at once or
|
||||||
/// discarded on error.
|
/// discarded on error.
|
||||||
pub struct Batch<'a, C: 'a, L: 'a, K: 'a>
|
pub struct Batch<'a, C: 'a, K: 'a>
|
||||||
where
|
where
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
_store: &'a LMDBBackend<C, L, K>,
|
_store: &'a LMDBBackend<C, K>,
|
||||||
db: RefCell<Option<store::Batch<'a>>>,
|
db: RefCell<Option<store::Batch<'a>>>,
|
||||||
/// Keychain
|
/// Keychain
|
||||||
keychain: Option<K>,
|
keychain: Option<K>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
impl<'a, C, L, K> WalletOutputBatch<K> for Batch<'a, C, L, K>
|
impl<'a, C, K> WalletOutputBatch<K> for Batch<'a, C, K>
|
||||||
where
|
where
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
fn keychain(&mut self) -> &mut K {
|
fn keychain(&mut self) -> &mut K {
|
||||||
|
@ -460,6 +446,8 @@ where
|
||||||
self.save(out.clone())
|
self.save(out.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: Keys stored unencrypted in DB.. not good
|
||||||
|
// should store keys as derivation paths instead
|
||||||
fn save_private_context(&mut self, slate_id: &[u8], ctx: &Context) -> Result<(), Error> {
|
fn save_private_context(&mut self, slate_id: &[u8], ctx: &Context) -> Result<(), Error> {
|
||||||
let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec());
|
let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec());
|
||||||
self.db.borrow().as_ref().unwrap().put_ser(&ctx_key, &ctx)?;
|
self.db.borrow().as_ref().unwrap().put_ser(&ctx_key, &ctx)?;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//! Client functions, implementations of the WalletToNodeClient trait
|
//! Client functions, implementations of the NodeClient trait
|
||||||
//! specific to the FileWallet
|
//! specific to the FileWallet
|
||||||
|
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
|
@ -24,28 +24,27 @@ use tokio::runtime::Runtime;
|
||||||
|
|
||||||
use api;
|
use api;
|
||||||
use error::{Error, ErrorKind};
|
use error::{Error, ErrorKind};
|
||||||
use libtx::slate::Slate;
|
|
||||||
use libwallet;
|
use libwallet;
|
||||||
use util;
|
use util;
|
||||||
use util::secp::pedersen;
|
use util::secp::pedersen;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HTTPWalletToNodeClient {
|
pub struct HTTPNodeClient {
|
||||||
node_url: String,
|
node_url: String,
|
||||||
node_api_secret: Option<String>,
|
node_api_secret: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HTTPWalletToNodeClient {
|
impl HTTPNodeClient {
|
||||||
/// Create a new client that will communicate with the given grin node
|
/// Create a new client that will communicate with the given grin node
|
||||||
pub fn new(node_url: &str, node_api_secret: Option<String>) -> HTTPWalletToNodeClient {
|
pub fn new(node_url: &str, node_api_secret: Option<String>) -> HTTPNodeClient {
|
||||||
HTTPWalletToNodeClient {
|
HTTPNodeClient {
|
||||||
node_url: node_url.to_owned(),
|
node_url: node_url.to_owned(),
|
||||||
node_api_secret: node_api_secret,
|
node_api_secret: node_api_secret,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WalletToNodeClient for HTTPWalletToNodeClient {
|
impl NodeClient for HTTPNodeClient {
|
||||||
fn node_url(&self) -> &str {
|
fn node_url(&self) -> &str {
|
||||||
&self.node_url
|
&self.node_url
|
||||||
}
|
}
|
||||||
|
@ -176,62 +175,6 @@ impl WalletToNodeClient for HTTPWalletToNodeClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct HTTPWalletToWalletClient {}
|
|
||||||
|
|
||||||
impl HTTPWalletToWalletClient {
|
|
||||||
/// Create a new client that will communicate other wallets
|
|
||||||
pub fn new() -> HTTPWalletToWalletClient {
|
|
||||||
HTTPWalletToWalletClient {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WalletToWalletClient for HTTPWalletToWalletClient {
|
|
||||||
/// Call the wallet API to create a coinbase output for the given
|
|
||||||
/// block_fees. Will retry based on default "retry forever with backoff"
|
|
||||||
/// behavior.
|
|
||||||
fn create_coinbase(
|
|
||||||
&self,
|
|
||||||
dest: &str,
|
|
||||||
block_fees: &BlockFees,
|
|
||||||
) -> Result<CbData, libwallet::Error> {
|
|
||||||
let url = format!("{}/v1/wallet/foreign/build_coinbase", dest);
|
|
||||||
match single_create_coinbase(&url, &block_fees) {
|
|
||||||
Err(e) => {
|
|
||||||
error!(
|
|
||||||
"Failed to get coinbase from {}. Run grin wallet listen?",
|
|
||||||
url
|
|
||||||
);
|
|
||||||
error!("Underlying Error: {}", e.cause().unwrap());
|
|
||||||
error!("Backtrace: {}", e.backtrace().unwrap());
|
|
||||||
Err(libwallet::ErrorKind::ClientCallback(
|
|
||||||
"Failed to get coinbase",
|
|
||||||
))?
|
|
||||||
}
|
|
||||||
Ok(res) => Ok(res),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send the slate to a listening wallet instance
|
|
||||||
fn send_tx_slate(&self, dest: &str, slate: &Slate) -> Result<Slate, libwallet::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(libwallet::ErrorKind::Uri)?
|
|
||||||
}
|
|
||||||
let url = format!("{}/v1/wallet/foreign/receive_tx", dest);
|
|
||||||
debug!("Posting transaction slate to {}", url);
|
|
||||||
|
|
||||||
let res = api::client::post(url.as_str(), None, slate).context(
|
|
||||||
libwallet::ErrorKind::ClientCallback("Posting transaction slate"),
|
|
||||||
)?;
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Call the wallet API to create a coinbase output for the given block_fees.
|
/// Call the wallet API to create a coinbase output for the given block_fees.
|
||||||
/// Will retry based on default "retry forever with backoff" behavior.
|
/// Will retry based on default "retry forever with backoff" behavior.
|
||||||
pub fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
|
pub fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
|
17
wallet/src/node_clients/mod.rs
Normal file
17
wallet/src/node_clients/mod.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2018 The Grin Developers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
mod http;
|
||||||
|
|
||||||
|
pub use self::http::{create_coinbase, HTTPNodeClient};
|
|
@ -51,28 +51,19 @@ fn setup(test_dir: &str) {
|
||||||
fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
setup(test_dir);
|
setup(test_dir);
|
||||||
// Create a new proxy to simulate server and wallet responses
|
// Create a new proxy to simulate server and wallet responses
|
||||||
let mut wallet_proxy: WalletProxy<LocalWalletClient, LocalWalletClient, ExtKeychain> =
|
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
|
||||||
WalletProxy::new(test_dir);
|
|
||||||
let chain = wallet_proxy.chain.clone();
|
let chain = wallet_proxy.chain.clone();
|
||||||
|
|
||||||
// Create a new wallet test client, and set its queues to communicate with the
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
// proxy
|
// proxy
|
||||||
let client = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = common::create_wallet(
|
let wallet1 = common::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
||||||
&format!("{}/wallet1", test_dir),
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
client.clone(),
|
|
||||||
client.clone(),
|
|
||||||
);
|
|
||||||
wallet_proxy.add_wallet("wallet1", client.get_send_instance(), wallet1.clone());
|
|
||||||
|
|
||||||
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
// define recipient wallet, add to proxy
|
// define recipient wallet, add to proxy
|
||||||
let wallet2 = common::create_wallet(
|
let wallet2 = common::create_wallet(&format!("{}/wallet2", test_dir), client2.clone());
|
||||||
&format!("{}/wallet2", test_dir),
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
client.clone(),
|
|
||||||
client.clone(),
|
|
||||||
);
|
|
||||||
let client = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
|
||||||
wallet_proxy.add_wallet("wallet2", client.get_send_instance(), wallet2.clone());
|
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
@ -193,14 +184,16 @@ fn accounts_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
let slate = api.issue_send_tx(
|
let (mut slate, lock_fn) = api.initiate_tx(
|
||||||
reward, // amount
|
None, reward, // amount
|
||||||
2, // minimum confirmations
|
2, // minimum confirmations
|
||||||
"wallet2", // dest
|
|
||||||
500, // max outputs
|
500, // max outputs
|
||||||
1, // num change outputs
|
1, // num change outputs
|
||||||
true, // select all outputs
|
true, // select all outputs
|
||||||
)?;
|
)?;
|
||||||
|
slate = client1.send_tx_slate_direct("wallet2", &slate)?;
|
||||||
|
api.finalize_tx(&mut slate)?;
|
||||||
|
api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
api.post_tx(&slate, false)?;
|
api.post_tx(&slate, false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -28,11 +28,9 @@ use util::Mutex;
|
||||||
use chain::Chain;
|
use chain::Chain;
|
||||||
use core::core::{OutputFeatures, OutputIdentifier, Transaction};
|
use core::core::{OutputFeatures, OutputIdentifier, Transaction};
|
||||||
use core::{consensus, global, pow, ser};
|
use core::{consensus, global, pow, ser};
|
||||||
use wallet::libwallet;
|
use wallet::libwallet::types::{BlockFees, CbData, NodeClient, WalletInst};
|
||||||
use wallet::libwallet::types::{
|
|
||||||
BlockFees, CbData, WalletInst, WalletToNodeClient, WalletToWalletClient,
|
|
||||||
};
|
|
||||||
use wallet::lmdb_wallet::LMDBBackend;
|
use wallet::lmdb_wallet::LMDBBackend;
|
||||||
|
use wallet::{controller, libwallet};
|
||||||
use wallet::{WalletBackend, WalletConfig};
|
use wallet::{WalletBackend, WalletConfig};
|
||||||
|
|
||||||
use util;
|
use util;
|
||||||
|
@ -115,14 +113,13 @@ 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
|
/// 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.
|
/// the block and adds it to the chain, with option transactions included.
|
||||||
/// Helpful for building up precise wallet balances for testing.
|
/// Helpful for building up precise wallet balances for testing.
|
||||||
pub fn award_block_to_wallet<C, L, K>(
|
pub fn award_block_to_wallet<C, K>(
|
||||||
chain: &Chain,
|
chain: &Chain,
|
||||||
txs: Vec<&Transaction>,
|
txs: Vec<&Transaction>,
|
||||||
wallet: Arc<Mutex<WalletInst<C, L, K>>>,
|
wallet: Arc<Mutex<WalletInst<C, K>>>,
|
||||||
) -> Result<(), libwallet::Error>
|
) -> Result<(), libwallet::Error>
|
||||||
where
|
where
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: keychain::Keychain,
|
K: keychain::Keychain,
|
||||||
{
|
{
|
||||||
// build block fees
|
// build block fees
|
||||||
|
@ -134,7 +131,7 @@ where
|
||||||
height: prev.height + 1,
|
height: prev.height + 1,
|
||||||
};
|
};
|
||||||
// build coinbase (via api) and add block
|
// build coinbase (via api) and add block
|
||||||
libwallet::controller::foreign_single_use(wallet.clone(), |api| {
|
controller::foreign_single_use(wallet.clone(), |api| {
|
||||||
let coinbase_tx = api.build_coinbase(&block_fees)?;
|
let coinbase_tx = api.build_coinbase(&block_fees)?;
|
||||||
add_block_with_reward(chain, txs, coinbase_tx.clone());
|
add_block_with_reward(chain, txs, coinbase_tx.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -143,14 +140,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Award a blocks to a wallet directly
|
/// Award a blocks to a wallet directly
|
||||||
pub fn award_blocks_to_wallet<C, L, K>(
|
pub fn award_blocks_to_wallet<C, K>(
|
||||||
chain: &Chain,
|
chain: &Chain,
|
||||||
wallet: Arc<Mutex<WalletInst<C, L, K>>>,
|
wallet: Arc<Mutex<WalletInst<C, K>>>,
|
||||||
number: usize,
|
number: usize,
|
||||||
) -> Result<(), libwallet::Error>
|
) -> Result<(), libwallet::Error>
|
||||||
where
|
where
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: keychain::Keychain,
|
K: keychain::Keychain,
|
||||||
{
|
{
|
||||||
for _ in 0..number {
|
for _ in 0..number {
|
||||||
|
@ -160,20 +156,15 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// dispatch a db wallet
|
/// dispatch a db wallet
|
||||||
pub fn create_wallet<C, L, K>(
|
pub fn create_wallet<C, K>(dir: &str, n_client: C) -> Arc<Mutex<WalletInst<C, K>>>
|
||||||
dir: &str,
|
|
||||||
n_client: C,
|
|
||||||
w_client: L,
|
|
||||||
) -> Arc<Mutex<WalletInst<C, L, K>>>
|
|
||||||
where
|
where
|
||||||
C: WalletToNodeClient + 'static,
|
C: NodeClient + 'static,
|
||||||
L: WalletToWalletClient + 'static,
|
|
||||||
K: keychain::Keychain + 'static,
|
K: keychain::Keychain + 'static,
|
||||||
{
|
{
|
||||||
let mut wallet_config = WalletConfig::default();
|
let mut wallet_config = WalletConfig::default();
|
||||||
wallet_config.data_file_dir = String::from(dir);
|
wallet_config.data_file_dir = String::from(dir);
|
||||||
let _ = wallet::WalletSeed::init_file(&wallet_config);
|
let _ = wallet::WalletSeed::init_file(&wallet_config);
|
||||||
let mut wallet = LMDBBackend::new(wallet_config.clone(), "", n_client, w_client)
|
let mut wallet = LMDBBackend::new(wallet_config.clone(), "", n_client)
|
||||||
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config));
|
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config));
|
||||||
wallet.open_with_credentials().unwrap_or_else(|e| {
|
wallet.open_with_credentials().unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
|
|
|
@ -43,8 +43,8 @@ use keychain::Keychain;
|
||||||
|
|
||||||
use util::secp::pedersen;
|
use util::secp::pedersen;
|
||||||
use wallet::libtx::slate::Slate;
|
use wallet::libtx::slate::Slate;
|
||||||
use wallet::libwallet;
|
|
||||||
use wallet::libwallet::types::*;
|
use wallet::libwallet::types::*;
|
||||||
|
use wallet::{controller, libwallet, WalletCommAdapter, WalletConfig};
|
||||||
|
|
||||||
use common;
|
use common;
|
||||||
|
|
||||||
|
@ -63,10 +63,9 @@ pub struct WalletProxyMessage {
|
||||||
|
|
||||||
/// communicates with a chain instance or other wallet
|
/// communicates with a chain instance or other wallet
|
||||||
/// listener APIs via message queues
|
/// listener APIs via message queues
|
||||||
pub struct WalletProxy<C, L, K>
|
pub struct WalletProxy<C, K>
|
||||||
where
|
where
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
/// directory to create the chain in
|
/// directory to create the chain in
|
||||||
|
@ -78,7 +77,7 @@ where
|
||||||
String,
|
String,
|
||||||
(
|
(
|
||||||
Sender<WalletProxyMessage>,
|
Sender<WalletProxyMessage>,
|
||||||
Arc<Mutex<WalletInst<LocalWalletClient, LocalWalletClient, K>>>,
|
Arc<Mutex<WalletInst<LocalWalletClient, K>>>,
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
/// simulate json send to another client
|
/// simulate json send to another client
|
||||||
|
@ -92,14 +91,11 @@ where
|
||||||
phantom_c: PhantomData<C>,
|
phantom_c: PhantomData<C>,
|
||||||
/// Phantom
|
/// Phantom
|
||||||
phantom_k: PhantomData<K>,
|
phantom_k: PhantomData<K>,
|
||||||
/// Phantom
|
|
||||||
phantom_l: PhantomData<L>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, L, K> WalletProxy<C, L, K>
|
impl<C, K> WalletProxy<C, K>
|
||||||
where
|
where
|
||||||
C: WalletToNodeClient,
|
C: NodeClient,
|
||||||
L: WalletToWalletClient,
|
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
/// Create a new client that will communicate with the given grin node
|
/// Create a new client that will communicate with the given grin node
|
||||||
|
@ -128,7 +124,6 @@ where
|
||||||
running: Arc::new(AtomicBool::new(false)),
|
running: Arc::new(AtomicBool::new(false)),
|
||||||
phantom_c: PhantomData,
|
phantom_c: PhantomData,
|
||||||
phantom_k: PhantomData,
|
phantom_k: PhantomData,
|
||||||
phantom_l: PhantomData,
|
|
||||||
};
|
};
|
||||||
retval
|
retval
|
||||||
}
|
}
|
||||||
|
@ -138,7 +133,7 @@ where
|
||||||
&mut self,
|
&mut self,
|
||||||
addr: &str,
|
addr: &str,
|
||||||
tx: Sender<WalletProxyMessage>,
|
tx: Sender<WalletProxyMessage>,
|
||||||
wallet: Arc<Mutex<WalletInst<LocalWalletClient, LocalWalletClient, K>>>,
|
wallet: Arc<Mutex<WalletInst<LocalWalletClient, K>>>,
|
||||||
) {
|
) {
|
||||||
self.wallets.insert(addr.to_owned(), (tx, wallet));
|
self.wallets.insert(addr.to_owned(), (tx, wallet));
|
||||||
}
|
}
|
||||||
|
@ -215,8 +210,8 @@ where
|
||||||
}
|
}
|
||||||
let w = dest_wallet.unwrap().1.clone();
|
let w = dest_wallet.unwrap().1.clone();
|
||||||
let mut slate = serde_json::from_str(&m.body).unwrap();
|
let mut slate = serde_json::from_str(&m.body).unwrap();
|
||||||
libwallet::controller::foreign_single_use(w.clone(), |listener_api| {
|
controller::foreign_single_use(w.clone(), |listener_api| {
|
||||||
listener_api.receive_tx(&mut slate)?;
|
listener_api.receive_tx(&mut slate, None)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
Ok(WalletProxyMessage {
|
Ok(WalletProxyMessage {
|
||||||
|
@ -314,22 +309,13 @@ impl LocalWalletClient {
|
||||||
pub fn get_send_instance(&self) -> Sender<WalletProxyMessage> {
|
pub fn get_send_instance(&self) -> Sender<WalletProxyMessage> {
|
||||||
self.tx.lock().clone()
|
self.tx.lock().clone()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl WalletToWalletClient for LocalWalletClient {
|
|
||||||
/// Call the wallet API to create a coinbase output for the given
|
|
||||||
/// block_fees. Will retry based on default "retry forever with backoff"
|
|
||||||
/// behavior.
|
|
||||||
fn create_coinbase(
|
|
||||||
&self,
|
|
||||||
_dest: &str,
|
|
||||||
_block_fees: &BlockFees,
|
|
||||||
) -> Result<CbData, libwallet::Error> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send the slate to a listening wallet instance
|
/// Send the slate to a listening wallet instance
|
||||||
fn send_tx_slate(&self, dest: &str, slate: &Slate) -> Result<Slate, libwallet::Error> {
|
pub fn send_tx_slate_direct(
|
||||||
|
&self,
|
||||||
|
dest: &str,
|
||||||
|
slate: &Slate,
|
||||||
|
) -> Result<Slate, libwallet::Error> {
|
||||||
let m = WalletProxyMessage {
|
let m = WalletProxyMessage {
|
||||||
sender_id: self.id.clone(),
|
sender_id: self.id.clone(),
|
||||||
dest: dest.to_owned(),
|
dest: dest.to_owned(),
|
||||||
|
@ -352,7 +338,55 @@ impl WalletToWalletClient for LocalWalletClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WalletToNodeClient for 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"))?;
|
||||||
|
}
|
||||||
|
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",
|
||||||
|
))?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
fn node_url(&self) -> &str {
|
||||||
"node"
|
"node"
|
||||||
}
|
}
|
||||||
|
|
181
wallet/tests/file.rs
Normal file
181
wallet/tests/file.rs
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Test a wallet file send/recieve
|
||||||
|
extern crate grin_chain as chain;
|
||||||
|
extern crate grin_core as core;
|
||||||
|
extern crate grin_keychain as keychain;
|
||||||
|
extern crate grin_store as store;
|
||||||
|
extern crate grin_util as util;
|
||||||
|
extern crate grin_wallet as wallet;
|
||||||
|
extern crate rand;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
extern crate chrono;
|
||||||
|
extern crate serde;
|
||||||
|
extern crate uuid;
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
use common::testclient::{LocalWalletClient, WalletProxy};
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use core::global;
|
||||||
|
use core::global::ChainTypes;
|
||||||
|
use keychain::ExtKeychain;
|
||||||
|
use wallet::{libwallet, FileWalletCommAdapter};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// self send impl
|
||||||
|
fn file_exchange_test_impl(test_dir: &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 chain = wallet_proxy.chain.clone();
|
||||||
|
|
||||||
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
|
let wallet1 = common::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
||||||
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
|
|
||||||
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
|
let wallet2 = common::create_wallet(&format!("{}/wallet2", test_dir), client2.clone());
|
||||||
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
|
|
||||||
|
// Set the wallet proxy listener running
|
||||||
|
thread::spawn(move || {
|
||||||
|
if let Err(e) = wallet_proxy.run() {
|
||||||
|
error!("Wallet Proxy error: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// few values to keep things shorter
|
||||||
|
let reward = core::consensus::REWARD;
|
||||||
|
|
||||||
|
// add some accounts
|
||||||
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
|
api.new_account_path("mining")?;
|
||||||
|
api.new_account_path("listener")?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// add some accounts
|
||||||
|
wallet::controller::owner_single_use(wallet2.clone(), |api| {
|
||||||
|
api.new_account_path("account1")?;
|
||||||
|
api.new_account_path("account2")?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Get some mining done
|
||||||
|
{
|
||||||
|
let mut w = wallet1.lock();
|
||||||
|
w.set_parent_key_id_by_name("mining")?;
|
||||||
|
}
|
||||||
|
let mut bh = 10u64;
|
||||||
|
let _ = common::award_blocks_to_wallet(&chain, wallet1.clone(), bh as usize);
|
||||||
|
|
||||||
|
let send_file = format!("{}/part_tx_1.tx", test_dir);
|
||||||
|
let receive_file = format!("{}/part_tx_2.tx", test_dir);
|
||||||
|
|
||||||
|
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||||
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
|
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true)?;
|
||||||
|
assert!(wallet1_refreshed);
|
||||||
|
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||||
|
assert_eq!(wallet1_info.total, bh * reward);
|
||||||
|
// send to send
|
||||||
|
let (mut slate, lock_fn) = api.initiate_tx(
|
||||||
|
Some("mining"),
|
||||||
|
reward * 2, // amount
|
||||||
|
2, // minimum confirmations
|
||||||
|
500, // max outputs
|
||||||
|
1, // num change outputs
|
||||||
|
true, // select all outputs
|
||||||
|
//"mining",
|
||||||
|
//"listener",
|
||||||
|
)?;
|
||||||
|
/// output tx file
|
||||||
|
let file_adapter = FileWalletCommAdapter::new();
|
||||||
|
file_adapter.send_tx_async(&send_file, &mut slate)?;
|
||||||
|
api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Get some mining done
|
||||||
|
{
|
||||||
|
let mut w = wallet2.lock();
|
||||||
|
w.set_parent_key_id_by_name("account1")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wallet 2 receives file, completes, sends file back
|
||||||
|
wallet::controller::foreign_single_use(wallet2.clone(), |api| {
|
||||||
|
let adapter = FileWalletCommAdapter::new();
|
||||||
|
let mut slate = adapter.receive_tx_async(&send_file)?;
|
||||||
|
api.receive_tx(&mut slate, None)?;
|
||||||
|
adapter.send_tx_async(&receive_file, &mut 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)?;
|
||||||
|
api.finalize_tx(&mut slate)?;
|
||||||
|
api.post_tx(&slate, false);
|
||||||
|
bh += 1;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let _ = common::award_blocks_to_wallet(&chain, wallet1.clone(), 3);
|
||||||
|
bh += 3;
|
||||||
|
|
||||||
|
// Check total in mining account
|
||||||
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
|
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(true)?;
|
||||||
|
assert!(wallet1_refreshed);
|
||||||
|
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||||
|
assert_eq!(wallet1_info.total, bh * reward - reward * 2);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Check total in 'wallet 2' account
|
||||||
|
wallet::controller::owner_single_use(wallet2.clone(), |api| {
|
||||||
|
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(true)?;
|
||||||
|
assert!(wallet2_refreshed);
|
||||||
|
assert_eq!(wallet2_info.last_confirmed_height, bh);
|
||||||
|
assert_eq!(wallet2_info.total, 2 * reward);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// let logging finish
|
||||||
|
thread::sleep(Duration::from_millis(200));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wallet_file_exchange() {
|
||||||
|
let test_dir = "test_output/file_exchange";
|
||||||
|
if let Err(e) = file_exchange_test_impl(test_dir) {
|
||||||
|
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,11 +56,10 @@ fn restore_wallet(base_dir: &str, wallet_dir: &str) -> Result<(), libwallet::Err
|
||||||
let dest_seed = format!("{}/wallet.seed", dest_dir);
|
let dest_seed = format!("{}/wallet.seed", dest_dir);
|
||||||
fs::copy(source_seed, dest_seed)?;
|
fs::copy(source_seed, dest_seed)?;
|
||||||
|
|
||||||
let mut wallet_proxy: WalletProxy<LocalWalletClient, LocalWalletClient, ExtKeychain> =
|
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(base_dir);
|
||||||
WalletProxy::new(base_dir);
|
|
||||||
let client = LocalWalletClient::new(wallet_dir, wallet_proxy.tx.clone());
|
let client = LocalWalletClient::new(wallet_dir, wallet_proxy.tx.clone());
|
||||||
|
|
||||||
let wallet = common::create_wallet(&dest_dir, client.clone(), client.clone());
|
let wallet = common::create_wallet(&dest_dir, client.clone());
|
||||||
|
|
||||||
wallet_proxy.add_wallet(wallet_dir, client.get_send_instance(), wallet.clone());
|
wallet_proxy.add_wallet(wallet_dir, client.get_send_instance(), wallet.clone());
|
||||||
|
|
||||||
|
@ -90,11 +89,10 @@ fn compare_wallet_restore(
|
||||||
let source_dir = format!("{}/{}", base_dir, wallet_dir);
|
let source_dir = format!("{}/{}", base_dir, wallet_dir);
|
||||||
let dest_dir = format!("{}/{}", base_dir, restore_name);
|
let dest_dir = format!("{}/{}", base_dir, restore_name);
|
||||||
|
|
||||||
let mut wallet_proxy: WalletProxy<LocalWalletClient, LocalWalletClient, ExtKeychain> =
|
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(base_dir);
|
||||||
WalletProxy::new(base_dir);
|
|
||||||
|
|
||||||
let client = LocalWalletClient::new(wallet_dir, wallet_proxy.tx.clone());
|
let client = LocalWalletClient::new(wallet_dir, wallet_proxy.tx.clone());
|
||||||
let wallet_source = common::create_wallet(&source_dir, client.clone(), client.clone());
|
let wallet_source = common::create_wallet(&source_dir, client.clone());
|
||||||
wallet_proxy.add_wallet(
|
wallet_proxy.add_wallet(
|
||||||
&wallet_dir,
|
&wallet_dir,
|
||||||
client.get_send_instance(),
|
client.get_send_instance(),
|
||||||
|
@ -102,7 +100,7 @@ fn compare_wallet_restore(
|
||||||
);
|
);
|
||||||
|
|
||||||
let client = LocalWalletClient::new(&restore_name, wallet_proxy.tx.clone());
|
let client = LocalWalletClient::new(&restore_name, wallet_proxy.tx.clone());
|
||||||
let wallet_dest = common::create_wallet(&dest_dir, client.clone(), client.clone());
|
let wallet_dest = common::create_wallet(&dest_dir, client.clone());
|
||||||
wallet_proxy.add_wallet(
|
wallet_proxy.add_wallet(
|
||||||
&restore_name,
|
&restore_name,
|
||||||
client.get_send_instance(),
|
client.get_send_instance(),
|
||||||
|
@ -184,28 +182,19 @@ fn compare_wallet_restore(
|
||||||
fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
|
fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
setup(test_dir);
|
setup(test_dir);
|
||||||
// Create a new proxy to simulate server and wallet responses
|
// Create a new proxy to simulate server and wallet responses
|
||||||
let mut wallet_proxy: WalletProxy<LocalWalletClient, LocalWalletClient, ExtKeychain> =
|
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
|
||||||
WalletProxy::new(test_dir);
|
|
||||||
let chain = wallet_proxy.chain.clone();
|
let chain = wallet_proxy.chain.clone();
|
||||||
|
|
||||||
// Create a new wallet test client, and set its queues to communicate with the
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
// proxy
|
// proxy
|
||||||
let client = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = common::create_wallet(
|
let wallet1 = common::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
||||||
&format!("{}/wallet1", test_dir),
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
client.clone(),
|
|
||||||
client.clone(),
|
|
||||||
);
|
|
||||||
wallet_proxy.add_wallet("wallet1", client.get_send_instance(), wallet1.clone());
|
|
||||||
|
|
||||||
// define recipient wallet, add to proxy
|
// define recipient wallet, add to proxy
|
||||||
let client = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
let wallet2 = common::create_wallet(
|
let wallet2 = common::create_wallet(&format!("{}/wallet2", test_dir), client2.clone());
|
||||||
&format!("{}/wallet2", test_dir),
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
client.clone(),
|
|
||||||
client.clone(),
|
|
||||||
);
|
|
||||||
wallet_proxy.add_wallet("wallet2", client.get_send_instance(), wallet2.clone());
|
|
||||||
|
|
||||||
// wallet 2 will use another account
|
// wallet 2 will use another account
|
||||||
wallet::controller::owner_single_use(wallet2.clone(), |api| {
|
wallet::controller::owner_single_use(wallet2.clone(), |api| {
|
||||||
|
@ -221,13 +210,9 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Another wallet
|
// Another wallet
|
||||||
let client = LocalWalletClient::new("wallet3", wallet_proxy.tx.clone());
|
let client3 = LocalWalletClient::new("wallet3", wallet_proxy.tx.clone());
|
||||||
let wallet3 = common::create_wallet(
|
let wallet3 = common::create_wallet(&format!("{}/wallet3", test_dir), client3.clone());
|
||||||
&format!("{}/wallet3", test_dir),
|
wallet_proxy.add_wallet("wallet3", client3.get_send_instance(), wallet3.clone());
|
||||||
client.clone(),
|
|
||||||
client.clone(),
|
|
||||||
);
|
|
||||||
wallet_proxy.add_wallet("wallet3", client.get_send_instance(), wallet3.clone());
|
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
@ -245,14 +230,16 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
let mut slate = Slate::blank(1);
|
let mut slate = Slate::blank(1);
|
||||||
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
|
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
|
||||||
// note this will increment the block count as part of the transaction "Posting"
|
// note this will increment the block count as part of the transaction "Posting"
|
||||||
slate = sender_api.issue_send_tx(
|
let (slate_i, lock_fn) = sender_api.initiate_tx(
|
||||||
amount, // amount
|
None, amount, // amount
|
||||||
2, // minimum confirmations
|
2, // minimum confirmations
|
||||||
"wallet2", // dest
|
|
||||||
500, // max outputs
|
500, // max outputs
|
||||||
1, // num change outputs
|
1, // num change outputs
|
||||||
true, // select all outputs
|
true, // select all outputs
|
||||||
)?;
|
)?;
|
||||||
|
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||||
|
sender_api.finalize_tx(&mut slate)?;
|
||||||
|
sender_api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
sender_api.post_tx(&slate, false)?;
|
sender_api.post_tx(&slate, false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
@ -263,14 +250,17 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
// Send some to wallet 3
|
// Send some to wallet 3
|
||||||
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
|
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
|
||||||
// note this will increment the block count as part of the transaction "Posting"
|
// note this will increment the block count as part of the transaction "Posting"
|
||||||
slate = sender_api.issue_send_tx(
|
let (slate_i, lock_fn) = sender_api.initiate_tx(
|
||||||
|
None,
|
||||||
amount * 2, // amount
|
amount * 2, // amount
|
||||||
2, // minimum confirmations
|
2, // minimum confirmations
|
||||||
"wallet3", // dest
|
|
||||||
500, // max outputs
|
500, // max outputs
|
||||||
1, // num change outputs
|
1, // num change outputs
|
||||||
true, // select all outputs
|
true, // select all outputs
|
||||||
)?;
|
)?;
|
||||||
|
slate = client1.send_tx_slate_direct("wallet3", &slate_i)?;
|
||||||
|
sender_api.finalize_tx(&mut slate)?;
|
||||||
|
sender_api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
sender_api.post_tx(&slate, false)?;
|
sender_api.post_tx(&slate, false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
@ -281,14 +271,17 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
// Wallet3 to wallet 2
|
// Wallet3 to wallet 2
|
||||||
wallet::controller::owner_single_use(wallet3.clone(), |sender_api| {
|
wallet::controller::owner_single_use(wallet3.clone(), |sender_api| {
|
||||||
// note this will increment the block count as part of the transaction "Posting"
|
// note this will increment the block count as part of the transaction "Posting"
|
||||||
slate = sender_api.issue_send_tx(
|
let (slate_i, lock_fn) = sender_api.initiate_tx(
|
||||||
|
None,
|
||||||
amount * 3, // amount
|
amount * 3, // amount
|
||||||
2, // minimum confirmations
|
2, // minimum confirmations
|
||||||
"wallet2", // dest
|
|
||||||
500, // max outputs
|
500, // max outputs
|
||||||
1, // num change outputs
|
1, // num change outputs
|
||||||
true, // select all outputs
|
true, // select all outputs
|
||||||
)?;
|
)?;
|
||||||
|
slate = client3.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||||
|
sender_api.finalize_tx(&mut slate)?;
|
||||||
|
sender_api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
sender_api.post_tx(&slate, false)?;
|
sender_api.post_tx(&slate, false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
@ -305,14 +298,17 @@ fn setup_restore(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
// Wallet3 to wallet 2 again (to another account)
|
// Wallet3 to wallet 2 again (to another account)
|
||||||
wallet::controller::owner_single_use(wallet3.clone(), |sender_api| {
|
wallet::controller::owner_single_use(wallet3.clone(), |sender_api| {
|
||||||
// note this will increment the block count as part of the transaction "Posting"
|
// note this will increment the block count as part of the transaction "Posting"
|
||||||
slate = sender_api.issue_send_tx(
|
let (slate_i, lock_fn) = sender_api.initiate_tx(
|
||||||
|
None,
|
||||||
amount * 3, // amount
|
amount * 3, // amount
|
||||||
2, // minimum confirmations
|
2, // minimum confirmations
|
||||||
"wallet2", // dest
|
|
||||||
500, // max outputs
|
500, // max outputs
|
||||||
1, // num change outputs
|
1, // num change outputs
|
||||||
true, // select all outputs
|
true, // select all outputs
|
||||||
)?;
|
)?;
|
||||||
|
slate = client3.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||||
|
sender_api.finalize_tx(&mut slate)?;
|
||||||
|
sender_api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
sender_api.post_tx(&slate, false)?;
|
sender_api.post_tx(&slate, false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -51,28 +51,14 @@ fn setup(test_dir: &str) {
|
||||||
fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
setup(test_dir);
|
setup(test_dir);
|
||||||
// Create a new proxy to simulate server and wallet responses
|
// Create a new proxy to simulate server and wallet responses
|
||||||
let mut wallet_proxy: WalletProxy<LocalWalletClient, LocalWalletClient, ExtKeychain> =
|
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
|
||||||
WalletProxy::new(test_dir);
|
|
||||||
let chain = wallet_proxy.chain.clone();
|
let chain = wallet_proxy.chain.clone();
|
||||||
|
|
||||||
// Create a new wallet test client, and set its queues to communicate with the
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
// proxy
|
// proxy
|
||||||
let client = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = common::create_wallet(
|
let wallet1 = common::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
||||||
&format!("{}/wallet1", test_dir),
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
client.clone(),
|
|
||||||
client.clone(),
|
|
||||||
);
|
|
||||||
wallet_proxy.add_wallet("wallet1", client.get_send_instance(), wallet1.clone());
|
|
||||||
|
|
||||||
// define recipient wallet, add to proxy
|
|
||||||
let wallet2 = common::create_wallet(
|
|
||||||
&format!("{}/wallet2", test_dir),
|
|
||||||
client.clone(),
|
|
||||||
client.clone(),
|
|
||||||
);
|
|
||||||
let client = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
|
||||||
wallet_proxy.add_wallet("wallet2", client.get_send_instance(), wallet2.clone());
|
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
@ -91,18 +77,6 @@ fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// add account to wallet 2
|
|
||||||
wallet::controller::owner_single_use(wallet2.clone(), |api| {
|
|
||||||
api.new_account_path("listener")?;
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Default wallet 2 to listen on that account
|
|
||||||
{
|
|
||||||
let mut w = wallet2.lock();
|
|
||||||
w.set_parent_key_id_by_name("listener")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get some mining done
|
// Get some mining done
|
||||||
{
|
{
|
||||||
let mut w = wallet1.lock();
|
let mut w = wallet1.lock();
|
||||||
|
@ -118,15 +92,23 @@ fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
assert_eq!(wallet1_info.last_confirmed_height, bh);
|
||||||
assert_eq!(wallet1_info.total, bh * reward);
|
assert_eq!(wallet1_info.total, bh * reward);
|
||||||
// send to send
|
// send to send
|
||||||
let slate = api.issue_self_tx(
|
let (mut slate, lock_fn) = api.initiate_tx(
|
||||||
|
Some("mining"),
|
||||||
reward * 2, // amount
|
reward * 2, // amount
|
||||||
2, // minimum confirmations
|
2, // minimum confirmations
|
||||||
500, // max outputs
|
500, // max outputs
|
||||||
1, // num change outputs
|
1, // num change outputs
|
||||||
true, // select all outputs
|
true, // select all outputs
|
||||||
"mining",
|
//"mining",
|
||||||
"listener",
|
//"listener",
|
||||||
)?;
|
)?;
|
||||||
|
// Send directly to self
|
||||||
|
wallet::controller::foreign_single_use(wallet1.clone(), |api| {
|
||||||
|
api.receive_tx(&mut slate, Some("listener"))?;
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
api.finalize_tx(&mut slate)?;
|
||||||
|
api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
api.post_tx(&slate, false)?; // mines a block
|
api.post_tx(&slate, false)?; // mines a block
|
||||||
bh += 1;
|
bh += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -163,7 +145,7 @@ fn self_send_test_impl(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wallet_stress() {
|
fn wallet_self_send() {
|
||||||
let test_dir = "test_output/self_send";
|
let test_dir = "test_output/self_send";
|
||||||
if let Err(e) = self_send_test_impl(test_dir) {
|
if let Err(e) = self_send_test_impl(test_dir) {
|
||||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||||
|
|
|
@ -49,34 +49,25 @@ fn setup(test_dir: &str) {
|
||||||
global::set_mining_mode(ChainTypes::AutomatedTesting);
|
global::set_mining_mode(ChainTypes::AutomatedTesting);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Exercises the Transaction API fully with a test WalletToNodeClient operating
|
/// Exercises the Transaction API fully with a test NodeClient operating
|
||||||
/// directly on a chain instance
|
/// directly on a chain instance
|
||||||
/// Callable with any type of wallet
|
/// Callable with any type of wallet
|
||||||
fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
|
fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
setup(test_dir);
|
setup(test_dir);
|
||||||
// Create a new proxy to simulate server and wallet responses
|
// Create a new proxy to simulate server and wallet responses
|
||||||
let mut wallet_proxy: WalletProxy<LocalWalletClient, LocalWalletClient, ExtKeychain> =
|
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
|
||||||
WalletProxy::new(test_dir);
|
|
||||||
let chain = wallet_proxy.chain.clone();
|
let chain = wallet_proxy.chain.clone();
|
||||||
|
|
||||||
// Create a new wallet test client, and set its queues to communicate with the
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
// proxy
|
// proxy
|
||||||
let client = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = common::create_wallet(
|
let wallet1 = common::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
||||||
&format!("{}/wallet1", test_dir),
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
client.clone(),
|
|
||||||
client.clone(),
|
|
||||||
);
|
|
||||||
wallet_proxy.add_wallet("wallet1", client.get_send_instance(), wallet1.clone());
|
|
||||||
|
|
||||||
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
// define recipient wallet, add to proxy
|
// define recipient wallet, add to proxy
|
||||||
let wallet2 = common::create_wallet(
|
let wallet2 = common::create_wallet(&format!("{}/wallet2", test_dir), client2.clone());
|
||||||
&format!("{}/wallet2", test_dir),
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
client.clone(),
|
|
||||||
client.clone(),
|
|
||||||
);
|
|
||||||
let client = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
|
||||||
wallet_proxy.add_wallet("wallet2", client.get_send_instance(), wallet2.clone());
|
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
@ -113,14 +104,16 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
let mut slate = Slate::blank(1);
|
let mut slate = Slate::blank(1);
|
||||||
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
|
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
|
||||||
// note this will increment the block count as part of the transaction "Posting"
|
// note this will increment the block count as part of the transaction "Posting"
|
||||||
slate = sender_api.issue_send_tx(
|
let (slate_i, lock_fn) = sender_api.initiate_tx(
|
||||||
amount, // amount
|
None, amount, // amount
|
||||||
2, // minimum confirmations
|
2, // minimum confirmations
|
||||||
"wallet2", // dest
|
|
||||||
500, // max outputs
|
500, // max outputs
|
||||||
1, // num change outputs
|
1, // num change outputs
|
||||||
true, // select all outputs
|
true, // select all outputs
|
||||||
)?;
|
)?;
|
||||||
|
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||||
|
sender_api.finalize_tx(&mut slate)?;
|
||||||
|
sender_api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -244,14 +237,17 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
// the stored transaction instead
|
// the stored transaction instead
|
||||||
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
|
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
|
||||||
// note this will increment the block count as part of the transaction "Posting"
|
// note this will increment the block count as part of the transaction "Posting"
|
||||||
slate = sender_api.issue_send_tx(
|
let (slate_i, lock_fn) = sender_api.initiate_tx(
|
||||||
|
None,
|
||||||
amount * 2, // amount
|
amount * 2, // amount
|
||||||
2, // minimum confirmations
|
2, // minimum confirmations
|
||||||
"wallet2", // dest
|
|
||||||
500, // max outputs
|
500, // max outputs
|
||||||
1, // num change outputs
|
1, // num change outputs
|
||||||
true, // select all outputs
|
true, // select all outputs
|
||||||
)?;
|
)?;
|
||||||
|
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||||
|
sender_api.finalize_tx(&mut slate)?;
|
||||||
|
sender_api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -305,28 +301,19 @@ fn basic_transaction_api(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
fn tx_rollback(test_dir: &str) -> Result<(), libwallet::Error> {
|
fn tx_rollback(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
setup(test_dir);
|
setup(test_dir);
|
||||||
// Create a new proxy to simulate server and wallet responses
|
// Create a new proxy to simulate server and wallet responses
|
||||||
let mut wallet_proxy: WalletProxy<LocalWalletClient, LocalWalletClient, ExtKeychain> =
|
let mut wallet_proxy: WalletProxy<LocalWalletClient, ExtKeychain> = WalletProxy::new(test_dir);
|
||||||
WalletProxy::new(test_dir);
|
|
||||||
let chain = wallet_proxy.chain.clone();
|
let chain = wallet_proxy.chain.clone();
|
||||||
|
|
||||||
// Create a new wallet test client, and set its queues to communicate with the
|
// Create a new wallet test client, and set its queues to communicate with the
|
||||||
// proxy
|
// proxy
|
||||||
let client = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||||
let wallet1 = common::create_wallet(
|
let wallet1 = common::create_wallet(&format!("{}/wallet1", test_dir), client1.clone());
|
||||||
&format!("{}/wallet1", test_dir),
|
wallet_proxy.add_wallet("wallet1", client1.get_send_instance(), wallet1.clone());
|
||||||
client.clone(),
|
|
||||||
client.clone(),
|
|
||||||
);
|
|
||||||
wallet_proxy.add_wallet("wallet1", client.get_send_instance(), wallet1.clone());
|
|
||||||
|
|
||||||
// define recipient wallet, add to proxy
|
// define recipient wallet, add to proxy
|
||||||
let client = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||||
let wallet2 = common::create_wallet(
|
let wallet2 = common::create_wallet(&format!("{}/wallet2", test_dir), client2.clone());
|
||||||
&format!("{}/wallet2", test_dir),
|
wallet_proxy.add_wallet("wallet2", client2.get_send_instance(), wallet2.clone());
|
||||||
client.clone(),
|
|
||||||
client.clone(),
|
|
||||||
);
|
|
||||||
wallet_proxy.add_wallet("wallet2", client.get_send_instance(), wallet2.clone());
|
|
||||||
|
|
||||||
// Set the wallet proxy listener running
|
// Set the wallet proxy listener running
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
@ -345,14 +332,16 @@ fn tx_rollback(test_dir: &str) -> Result<(), libwallet::Error> {
|
||||||
let mut slate = Slate::blank(1);
|
let mut slate = Slate::blank(1);
|
||||||
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
|
wallet::controller::owner_single_use(wallet1.clone(), |sender_api| {
|
||||||
// note this will increment the block count as part of the transaction "Posting"
|
// note this will increment the block count as part of the transaction "Posting"
|
||||||
slate = sender_api.issue_send_tx(
|
let (slate_i, lock_fn) = sender_api.initiate_tx(
|
||||||
amount, // amount
|
None, amount, // amount
|
||||||
2, // minimum confirmations
|
2, // minimum confirmations
|
||||||
"wallet2", // dest
|
|
||||||
500, // max outputs
|
500, // max outputs
|
||||||
1, // num change outputs
|
1, // num change outputs
|
||||||
true, // select all outputs
|
true, // select all outputs
|
||||||
)?;
|
)?;
|
||||||
|
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||||
|
sender_api.finalize_tx(&mut slate)?;
|
||||||
|
sender_api.tx_lock_outputs(&slate, lock_fn)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue