WalletClient trait refactor (#1238)

* refactor WalletClient

* revert chain changes

* missing files
This commit is contained in:
Yeastplume 2018-07-10 09:18:24 +01:00 committed by GitHub
parent c7d78446f3
commit 49cbab90f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 432 additions and 433 deletions

View file

@ -27,7 +27,7 @@ use std::default::Default;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::{fs, thread, time}; use std::{fs, thread, time};
use wallet::{FileWallet, WalletConfig}; use wallet::{HTTPWalletClient, FileWallet, 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) {
@ -262,12 +262,14 @@ 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 = HTTPWalletClient::new(&self.wallet_config.check_node_api_http_addr);
if let Err(e) = r { if let Err(e) = r {
//panic!("Error initting wallet seed: {}", e); //panic!("Error initting wallet seed: {}", e);
} }
let wallet: FileWallet<keychain::ExtKeychain> = let wallet: FileWallet<HTTPWalletClient, keychain::ExtKeychain> =
FileWallet::new(self.wallet_config.clone(), "").unwrap_or_else(|e| { FileWallet::new(self.wallet_config.clone(), "", client).unwrap_or_else(|e| {
panic!( panic!(
"Error creating wallet: {:?} Config: {:?}", "Error creating wallet: {:?} Config: {:?}",
e, self.wallet_config e, self.wallet_config
@ -300,7 +302,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 mut wallet = FileWallet::new(config.clone(), "") let client = HTTPWalletClient::new(&config.check_node_api_http_addr);
let mut wallet = FileWallet::new(config.clone(), "", client)
.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::libwallet::internal::updater::refresh_outputs(&mut wallet); let _ = wallet::libwallet::internal::updater::refresh_outputs(&mut wallet);
@ -324,9 +327,11 @@ 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 = HTTPWalletClient::new(&config.check_node_api_http_addr);
let max_outputs = 500; let max_outputs = 500;
let mut wallet = FileWallet::new(config.clone(), "") let mut wallet = FileWallet::new(config.clone(), "", client)
.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 _ = let _ =

View file

@ -53,7 +53,8 @@ use core::core::amount_to_hr_string;
use core::global; use core::global;
use tui::ui; use tui::ui;
use util::{init_logger, LoggingConfig, LOGGER}; use util::{init_logger, LoggingConfig, LOGGER};
use wallet::{libwallet, wallet_db_exists, FileWallet, LMDBBackend, WalletConfig, WalletInst}; use wallet::{libwallet, wallet_db_exists, FileWallet,
HTTPWalletClient, LMDBBackend, WalletConfig, WalletInst};
// include build information // include build information
pub mod built_info { pub mod built_info {
@ -553,9 +554,10 @@ fn instantiate_wallet(
wallet_config: WalletConfig, wallet_config: WalletConfig,
passphrase: &str, passphrase: &str,
use_db: bool, use_db: bool,
) -> Box<WalletInst<keychain::ExtKeychain>> { ) -> Box<WalletInst<HTTPWalletClient, keychain::ExtKeychain>> {
let client = HTTPWalletClient::new(&wallet_config.check_node_api_http_addr);
if use_db { if use_db {
let db_wallet = LMDBBackend::new(wallet_config.clone(), "").unwrap_or_else(|e| { let db_wallet = LMDBBackend::new(wallet_config.clone(), "", client).unwrap_or_else(|e| {
panic!( panic!(
"Error creating DB wallet: {} Config: {:?}", "Error creating DB wallet: {} Config: {:?}",
e, wallet_config e, wallet_config
@ -564,7 +566,7 @@ fn instantiate_wallet(
info!(LOGGER, "Using LMDB Backend for wallet"); info!(LOGGER, "Using LMDB Backend for wallet");
Box::new(db_wallet) Box::new(db_wallet)
} else { } else {
let file_wallet = FileWallet::new(wallet_config.clone(), passphrase) let file_wallet = FileWallet::new(wallet_config.clone(), passphrase, 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));
info!(LOGGER, "Using File Backend for wallet"); info!(LOGGER, "Using File Backend for wallet");
Box::new(file_wallet) Box::new(file_wallet)
@ -603,7 +605,8 @@ fn wallet_command(wallet_args: &ArgMatches, global_config: GlobalConfig) {
wallet::WalletSeed::init_file(&wallet_config).expect("Failed to init wallet seed file."); wallet::WalletSeed::init_file(&wallet_config).expect("Failed to init wallet seed file.");
info!(LOGGER, "Wallet seed file created"); info!(LOGGER, "Wallet seed file created");
if use_db { if use_db {
let _: LMDBBackend<keychain::ExtKeychain> = LMDBBackend::new(wallet_config.clone(), "") let client = HTTPWalletClient::new(&wallet_config.check_node_api_http_addr);
let _: LMDBBackend<HTTPWalletClient, keychain::ExtKeychain> = LMDBBackend::new(wallet_config.clone(), "", client)
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
panic!( panic!(
"Error creating DB wallet: {} Config: {:?}", "Error creating DB wallet: {} Config: {:?}",

View file

@ -28,10 +28,188 @@ use tokio_core::reactor;
use api; use api;
use error::{Error, ErrorKind}; use error::{Error, ErrorKind};
use libwallet;
use libtx::slate::Slate; use libtx::slate::Slate;
use util::secp::pedersen; use util::secp::pedersen;
use util::{self, LOGGER}; use util::{self, LOGGER};
#[derive(Clone)]
pub struct HTTPWalletClient {
node_url: String,
}
impl HTTPWalletClient {
/// Create a new client that will communicate with the given grin node
pub fn new(node_url:&str) -> HTTPWalletClient {
HTTPWalletClient {
node_url: node_url.to_owned(),
}
}
}
impl WalletClient for HTTPWalletClient {
fn node_url(&self) -> &str {
&self.node_url
}
/// 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!(
LOGGER,
"Failed to get coinbase from {}. Run grin wallet listen?", url
);
error!(LOGGER, "Underlying Error: {}", e.cause().unwrap());
error!(LOGGER, "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!(LOGGER, "{}", err_str,);
Err(libwallet::ErrorKind::Uri)?
}
let url = format!("{}/v1/wallet/foreign/receive_tx", dest);
debug!(LOGGER, "Posting transaction slate to {}", url);
let mut core = reactor::Core::new()
.context(libwallet::ErrorKind::ClientCallback("Sending transaction: Initialise API"))?;
let client = hyper::Client::new(&core.handle());
let url_pool = url.to_owned();
let mut req = Request::new(
Method::Post,
url_pool.parse::<hyper::Uri>()
.context(libwallet::ErrorKind::ClientCallback("Sending transaction: parsing URL"))?
);
req.headers_mut().set(ContentType::json());
let json = serde_json::to_string(&slate)
.context(libwallet::ErrorKind::ClientCallback("Sending transaction: parsing response"))?;
req.set_body(json);
let work = client.request(req).and_then(|res| {
res.body().concat2().and_then(move |body| {
let slate: Slate =
serde_json::from_slice(&body).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(slate)
})
});
let res = core.run(work)
.context(libwallet::ErrorKind::ClientCallback("Sending transaction: posting request"))?;
Ok(res)
}
/// Posts a transaction to a grin node
fn post_tx(&self, tx: &TxWrapper, fluff: bool) -> Result<(), libwallet::Error> {
let url;
let dest = self.node_url();
if fluff {
url = format!("{}/v1/pool/push?fluff", dest);
} else {
url = format!("{}/v1/pool/push", dest);
}
api::client::post(url.as_str(), tx)
.context(libwallet::ErrorKind::ClientCallback("Posting transaction to node"))?;
Ok(())
}
/// Return the chain tip from a given node
fn get_chain_height(&self) -> Result<u64, libwallet::Error> {
let addr = self.node_url();
let url = format!("{}/v1/chain", addr);
let res = api::client::get::<api::Tip>(url.as_str())
.context(libwallet::ErrorKind::ClientCallback("Getting chain height from node"))?;
Ok(res.height)
}
/// Retrieve outputs from node
fn get_outputs_from_node(
&self,
wallet_outputs: Vec<pedersen::Commitment>,
) -> Result<HashMap<pedersen::Commitment, String>, libwallet::Error> {
let addr = self.node_url();
// build the necessary query params -
// ?id=xxx&id=yyy&id=zzz
let query_params: Vec<String> = wallet_outputs
.iter()
.map(|commit| format!("id={}", util::to_hex(commit.as_ref().to_vec())))
.collect();
// build a map of api outputs by commit so we can look them up efficiently
let mut api_outputs: HashMap<pedersen::Commitment, String> = HashMap::new();
for query_chunk in query_params.chunks(1000) {
let url = format!("{}/v1/chain/outputs/byids?{}", addr, query_chunk.join("&"),);
match api::client::get::<Vec<api::Output>>(url.as_str()) {
Ok(outputs) => for out in outputs {
api_outputs.insert(out.commit.commit(), util::to_hex(out.commit.to_vec()));
},
Err(e) => {
// if we got anything other than 200 back from server, don't attempt to refresh
// the wallet data after
return Err(libwallet::ErrorKind::ClientCallback("Error from server"))?;
}
}
}
Ok(api_outputs)
}
fn get_outputs_by_pmmr_index(
&self,
start_height: u64,
max_outputs: u64,
) -> Result<
(
u64,
u64,
Vec<(pedersen::Commitment, pedersen::RangeProof, bool)>,
),
libwallet::Error,
> {
let addr = self.node_url();
let query_param = format!("start_index={}&max={}", start_height, max_outputs);
let url = format!("{}/v1/txhashset/outputs?{}", addr, query_param,);
let mut api_outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool)> = Vec::new();
match api::client::get::<api::OutputListing>(url.as_str()) {
Ok(o) => {
for out in o.outputs {
let is_coinbase = match out.output_type {
api::OutputType::Coinbase => true,
api::OutputType::Transaction => false,
};
api_outputs.push((out.commit, out.range_proof().unwrap(), is_coinbase));
}
Ok((o.highest_index, o.last_retrieved_index, api_outputs))
}
Err(e) => {
// if we got anything other than 200 back from server, bye
error!(
LOGGER,
"get_outputs_by_pmmr_index: unable to contact API {}. Error: {}", addr, e
);
Err(libwallet::ErrorKind::ClientCallback("unable to contact api"))?
}
}
}
}
/// 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> {
@ -44,49 +222,12 @@ pub fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result<CbData, Err
); );
error!(LOGGER, "Underlying Error: {}", e.cause().unwrap()); error!(LOGGER, "Underlying Error: {}", e.cause().unwrap());
error!(LOGGER, "Backtrace: {}", e.backtrace().unwrap()); error!(LOGGER, "Backtrace: {}", e.backtrace().unwrap());
Err(e) Err(e)?
} }
Ok(res) => Ok(res), Ok(res) => Ok(res),
} }
} }
/// Send the slate to a listening wallet instance
pub fn send_tx_slate(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!(LOGGER, "{}", err_str,);
Err(ErrorKind::Uri)?
}
let url = format!("{}/v1/wallet/foreign/receive_tx", dest);
debug!(LOGGER, "Posting transaction slate to {}", url);
let mut core = reactor::Core::new().context(ErrorKind::Hyper)?;
let client = hyper::Client::new(&core.handle());
let url_pool = url.to_owned();
let mut req = Request::new(
Method::Post,
url_pool.parse::<hyper::Uri>().context(ErrorKind::Hyper)?,
);
req.headers_mut().set(ContentType::json());
let json = serde_json::to_string(&slate).context(ErrorKind::Hyper)?;
req.set_body(json);
let work = client.request(req).and_then(|res| {
res.body().concat2().and_then(move |body| {
let slate: Slate =
serde_json::from_slice(&body).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(slate)
})
});
let res = core.run(work).context(ErrorKind::Hyper)?;
Ok(res)
}
/// Makes a single request to the wallet API to create a new coinbase output. /// Makes a single request to the wallet API to create a new coinbase output.
fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, Error> { fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
let mut core = let mut core =
@ -116,94 +257,3 @@ fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, E
Ok(res) Ok(res)
} }
/// Posts a transaction to a grin node
pub fn post_tx(dest: &str, tx: &TxWrapper, fluff: bool) -> Result<(), Error> {
let url;
if fluff {
url = format!("{}/v1/pool/push?fluff", dest);
} else {
url = format!("{}/v1/pool/push", dest);
}
api::client::post(url.as_str(), tx)?;
Ok(())
}
/// Return the chain tip from a given node
pub fn get_chain_height(addr: &str) -> Result<u64, Error> {
let url = format!("{}/v1/chain", addr);
let res = api::client::get::<api::Tip>(url.as_str())?;
Ok(res.height)
}
/// Retrieve outputs from node
pub fn get_outputs_from_node(
addr: &str,
wallet_outputs: Vec<pedersen::Commitment>,
) -> Result<HashMap<pedersen::Commitment, String>, Error> {
// build the necessary query params -
// ?id=xxx&id=yyy&id=zzz
let query_params: Vec<String> = wallet_outputs
.iter()
.map(|commit| format!("id={}", util::to_hex(commit.as_ref().to_vec())))
.collect();
// build a map of api outputs by commit so we can look them up efficiently
let mut api_outputs: HashMap<pedersen::Commitment, String> = HashMap::new();
for query_chunk in query_params.chunks(1000) {
let url = format!("{}/v1/chain/outputs/byids?{}", addr, query_chunk.join("&"),);
match api::client::get::<Vec<api::Output>>(url.as_str()) {
Ok(outputs) => for out in outputs {
api_outputs.insert(out.commit.commit(), util::to_hex(out.commit.to_vec()));
},
Err(e) => {
// if we got anything other than 200 back from server, don't attempt to refresh
// the wallet data after
return Err(e)?;
}
}
}
Ok(api_outputs)
}
pub fn get_outputs_by_pmmr_index(
addr: &str,
start_height: u64,
max_outputs: u64,
) -> Result<
(
u64,
u64,
Vec<(pedersen::Commitment, pedersen::RangeProof, bool)>,
),
Error,
> {
let query_param = format!("start_index={}&max={}", start_height, max_outputs);
let url = format!("{}/v1/txhashset/outputs?{}", addr, query_param,);
let mut api_outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool)> = Vec::new();
match api::client::get::<api::OutputListing>(url.as_str()) {
Ok(o) => {
for out in o.outputs {
let is_coinbase = match out.output_type {
api::OutputType::Coinbase => true,
api::OutputType::Transaction => false,
};
api_outputs.push((out.commit, out.range_proof().unwrap(), is_coinbase));
}
Ok((o.highest_index, o.last_retrieved_index, api_outputs))
}
Err(e) => {
// if we got anything other than 200 back from server, bye
error!(
LOGGER,
"get_outputs_by_pmmr_index: unable to contact API {}. Error: {}", addr, e
);
Err(e)?
}
}
}

View file

@ -139,9 +139,11 @@ impl<'a> Drop for FileBatch<'a> {
/// Wallet information tracking all our outputs. Based on HD derivation and /// Wallet information tracking all our outputs. Based on HD derivation and
/// avoids storing any key data, only storing output amounts and child index. /// avoids storing any key data, only storing output amounts and child index.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FileWallet<K> { pub struct FileWallet<C, K> {
/// Keychain /// Keychain
pub keychain: Option<K>, pub keychain: Option<K>,
/// Client implementation
pub client: C,
/// Configuration /// Configuration
pub config: WalletConfig, pub config: WalletConfig,
/// passphrase: TODO better ways of dealing with this other than storing /// passphrase: TODO better ways of dealing with this other than storing
@ -162,8 +164,9 @@ pub struct FileWallet<K> {
pub details_bak_path: String, pub details_bak_path: String,
} }
impl<K> WalletBackend<K> for FileWallet<K> impl<C, K> WalletBackend<C, K> for FileWallet<C, K>
where where
C: WalletClient,
K: Keychain, K: Keychain,
{ {
/// Initialize with whatever stored credentials we have /// Initialize with whatever stored credentials we have
@ -190,6 +193,11 @@ where
self.keychain.as_mut().unwrap() self.keychain.as_mut().unwrap()
} }
/// Return the client being used
fn client(&mut self) -> &mut C {
&mut self.client
}
fn iter<'a>(&'a self) -> Box<Iterator<Item = OutputData> + 'a> { fn iter<'a>(&'a self) -> Box<Iterator<Item = OutputData> + 'a> {
Box::new(self.outputs.values().cloned()) Box::new(self.outputs.values().cloned())
} }
@ -320,98 +328,16 @@ where
} }
} }
impl<K> WalletClient for FileWallet<K> { impl<C, K> FileWallet<C, K>
/// Return URL for check node
fn node_url(&self) -> &str {
&self.config.check_node_api_http_addr
}
/// Call the wallet API to create a coinbase transaction
fn create_coinbase(&self, block_fees: &BlockFees) -> Result<CbData, libwallet::Error> {
let res = client::create_coinbase(self.node_url(), block_fees);
match res {
Ok(r) => Ok(r),
Err(e) => {
let message = format!("{}", e.cause().unwrap());
error!(
LOGGER,
"Create Coinbase: Communication error: {},{}",
e.cause().unwrap(),
e.backtrace().unwrap()
);
Err(libwallet::ErrorKind::WalletComms(message))?
}
}
}
/// Send a transaction slate to another listening wallet and return result
fn send_tx_slate(&self, addr: &str, slate: &Slate) -> Result<Slate, libwallet::Error> {
let res = client::send_tx_slate(addr, slate);
match res {
Ok(r) => Ok(r),
Err(e) => {
let message = format!("{}", e.cause().unwrap());
error!(
LOGGER,
"Send TX Slate: Communication error: {},{}",
e.cause().unwrap(),
e.backtrace().unwrap()
);
Err(libwallet::ErrorKind::WalletComms(message))?
}
}
}
/// Posts a transaction to a grin node
fn post_tx(&self, tx: &TxWrapper, fluff: bool) -> Result<(), libwallet::Error> {
let res = client::post_tx(self.node_url(), tx, fluff).context(libwallet::ErrorKind::Node)?;
Ok(res)
}
/// retrieves the current tip from the specified grin node
fn get_chain_height(&self) -> Result<u64, libwallet::Error> {
let res = client::get_chain_height(self.node_url()).context(libwallet::ErrorKind::Node)?;
Ok(res)
}
/// retrieve a list of outputs from the specified grin node
/// need "by_height" and "by_id" variants
fn get_outputs_from_node(
&self,
wallet_outputs: Vec<pedersen::Commitment>,
) -> Result<HashMap<pedersen::Commitment, String>, libwallet::Error> {
let res = client::get_outputs_from_node(self.node_url(), wallet_outputs)
.context(libwallet::ErrorKind::Node)?;
Ok(res)
}
/// Outputs by PMMR index
fn get_outputs_by_pmmr_index(
&self,
start_height: u64,
max_outputs: u64,
) -> Result<
(
u64,
u64,
Vec<(pedersen::Commitment, pedersen::RangeProof, bool)>,
),
libwallet::Error,
> {
let res = client::get_outputs_by_pmmr_index(self.node_url(), start_height, max_outputs)
.context(libwallet::ErrorKind::Node)?;
Ok(res)
}
}
impl<K> FileWallet<K>
where where
C: WalletClient,
K: Keychain, K: Keychain,
{ {
/// Create a new FileWallet instance /// Create a new FileWallet instance
pub fn new(config: WalletConfig, passphrase: &str) -> Result<Self, Error> { pub fn new(config: WalletConfig, passphrase: &str, client: C) -> Result<Self, Error> {
let mut retval = FileWallet { let mut retval = FileWallet {
keychain: None, keychain: None,
client: client,
config: config.clone(), config: config.clone(),
passphrase: String::from(passphrase), passphrase: String::from(passphrase),
outputs: HashMap::new(), outputs: HashMap::new(),

View file

@ -56,10 +56,10 @@ pub mod libwallet;
pub mod lmdb_wallet; pub mod lmdb_wallet;
mod types; mod types;
pub use client::create_coinbase;
pub use error::{Error, ErrorKind}; pub use error::{Error, ErrorKind};
pub use file_wallet::FileWallet; pub use file_wallet::FileWallet;
pub use client::{create_coinbase, HTTPWalletClient};
pub use libwallet::controller; pub use libwallet::controller;
pub use libwallet::types::{BlockFees, CbData, WalletInfo, WalletInst}; pub use libwallet::types::{BlockFees, CbData, WalletBackend, WalletClient, WalletInfo, WalletInst};
pub use lmdb_wallet::{wallet_db_exists, LMDBBackend}; pub use lmdb_wallet::{wallet_db_exists, LMDBBackend};
pub use types::{WalletConfig, WalletSeed}; pub use types::{WalletConfig, WalletSeed};

View file

@ -31,27 +31,31 @@ use util::{self, LOGGER};
/// 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<'a, W: ?Sized, K> pub struct APIOwner<'a, W: ?Sized, C, K>
where where
W: 'a + WalletBackend<K> + WalletClient, W: 'a + WalletBackend<C, K>,
C: WalletClient,
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
/// perhaps) /// perhaps)
pub wallet: &'a mut Box<W>, pub wallet: &'a mut Box<W>,
phantom: PhantomData<K>, phantom: PhantomData<K>,
phantom_c: PhantomData<C>,
} }
impl<'a, W: ?Sized, K> APIOwner<'a, W, K> impl<'a, W: ?Sized, C, K> APIOwner<'a, W, C, K>
where where
W: 'a + WalletBackend<K> + WalletClient, W: 'a + WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
/// Create new API instance /// Create new API instance
pub fn new(wallet_in: &'a mut Box<W>) -> APIOwner<'a, W, K> { pub fn new(wallet_in: &'a mut Box<W>) -> APIOwner<'a, W, C, K> {
APIOwner { APIOwner {
wallet: wallet_in, wallet: wallet_in,
phantom: PhantomData, phantom: PhantomData,
phantom_c: PhantomData,
} }
} }
@ -103,7 +107,7 @@ where
selection_strategy_is_use_all, selection_strategy_is_use_all,
)?; )?;
let mut slate = match self.wallet.send_tx_slate(dest, &slate) { let mut slate = match self.wallet.client().send_tx_slate(dest, &slate) {
Ok(s) => s, Ok(s) => s,
Err(e) => { Err(e) => {
error!( error!(
@ -119,7 +123,7 @@ where
// All good here, so let's post it // All good here, so let's post it
let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap()); let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap());
self.wallet.post_tx(&TxWrapper { tx_hex: tx_hex }, fluff)?; self.wallet.client().post_tx(&TxWrapper { tx_hex: tx_hex }, fluff)?;
// All good here, lock our inputs // All good here, lock our inputs
lock_fn(self.wallet)?; lock_fn(self.wallet)?;
@ -140,7 +144,7 @@ where
max_outputs, max_outputs,
)?; )?;
let tx_hex = util::to_hex(ser::ser_vec(&tx_burn).unwrap()); let tx_hex = util::to_hex(ser::ser_vec(&tx_burn).unwrap());
self.wallet.post_tx(&TxWrapper { tx_hex: tx_hex }, false)?; self.wallet.client().post_tx(&TxWrapper { tx_hex: tx_hex }, false)?;
Ok(()) Ok(())
} }
@ -151,7 +155,7 @@ where
/// Retrieve current height from node /// Retrieve current height from node
pub fn node_height(&mut self) -> Result<(u64, bool), Error> { pub fn node_height(&mut self) -> Result<(u64, bool), Error> {
match self.wallet.get_chain_height() { match self.wallet.client().get_chain_height() {
Ok(height) => Ok((height, true)), Ok(height) => Ok((height, true)),
Err(_) => { Err(_) => {
let outputs = self.retrieve_outputs(true, false)?; let outputs = self.retrieve_outputs(true, false)?;
@ -175,27 +179,31 @@ 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<'a, W: ?Sized, K> pub struct APIForeign<'a, W: ?Sized, C, K>
where where
W: WalletBackend<K> + WalletClient + 'a, W: 'a + WalletBackend<C, K>,
C: WalletClient,
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
/// perhaps) /// perhaps)
pub wallet: &'a mut Box<W>, pub wallet: &'a mut Box<W>,
phantom: PhantomData<K>, phantom: PhantomData<K>,
phantom_c: PhantomData<C>,
} }
impl<'a, W: ?Sized, K> APIForeign<'a, W, K> impl<'a, W: ?Sized, C, K> APIForeign<'a, W, C, K>
where where
W: WalletBackend<K> + WalletClient, W: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
/// Create new API instance /// Create new API instance
pub fn new(wallet_in: &'a mut Box<W>) -> APIForeign<W, K> { pub fn new(wallet_in: &'a mut Box<W>) -> APIForeign<W, C, K> {
APIForeign { APIForeign {
wallet: wallet_in, wallet: wallet_in,
phantom: PhantomData, phantom: PhantomData,
phantom_c: PhantomData,
} }
} }

View file

@ -32,7 +32,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::{
BlockFees, CbData, OutputData, SendTXArgs, WalletBackend, WalletClient, WalletInfo, WalletInst, BlockFees, CbData, OutputData, SendTXArgs, WalletBackend, WalletClient, WalletInfo
}; };
use libwallet::{Error, ErrorKind}; use libwallet::{Error, ErrorKind};
@ -40,10 +40,11 @@ use util::LOGGER;
/// 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, K>(wallet: Box<T>, f: F) -> Result<(), Error> pub fn owner_single_use<F, T: ?Sized, C, K>(wallet: Box<T>, f: F) -> Result<(), Error>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
F: FnOnce(&mut APIOwner<T, K>) -> Result<(), Error>, F: FnOnce(&mut APIOwner<T, C, K>) -> Result<(), Error>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let mut w = wallet; let mut w = wallet;
@ -55,10 +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, K>(wallet: &mut Box<T>, f: F) -> Result<(), Error> pub fn foreign_single_use<F, T: ?Sized, C, K>(wallet: &mut Box<T>, f: F) -> Result<(), Error>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
F: FnOnce(&mut APIForeign<T, K>) -> Result<(), Error>, F: FnOnce(&mut APIForeign<T, C, K>) -> Result<(), Error>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
wallet.open_with_credentials()?; wallet.open_with_credentials()?;
@ -69,11 +71,12 @@ 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, K>(wallet: Box<T>, addr: &str) -> Result<(), Error> pub fn owner_listener<T: ?Sized, C, K>(wallet: Box<T>, addr: &str) -> Result<(), Error>
where where
T: WalletInst<K>, T: WalletBackend<C, K>,
OwnerAPIGetHandler<T, K>: Handler, OwnerAPIGetHandler<T, C, K>: Handler,
OwnerAPIPostHandler<T, K>: Handler, OwnerAPIPostHandler<T, C, K>: Handler,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let wallet_arc = Arc::new(Mutex::new(wallet)); let wallet_arc = Arc::new(Mutex::new(wallet));
@ -101,10 +104,11 @@ 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, K>(wallet: Box<T>, addr: &str) -> Result<(), Error> pub fn foreign_listener<T: ?Sized, C, K>(wallet: Box<T>, addr: &str) -> Result<(), Error>
where where
T: WalletInst<K>, T: WalletBackend<C, K>,
ForeignAPIHandler<T, K>: Handler, ForeignAPIHandler<T, C, K>: Handler,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let api_handler = ForeignAPIHandler::new(Arc::new(Mutex::new(wallet))); let api_handler = ForeignAPIHandler::new(Arc::new(Mutex::new(wallet)));
@ -126,33 +130,37 @@ where
} }
/// API Handler/Wrapper for owner functions /// API Handler/Wrapper for owner functions
pub struct OwnerAPIGetHandler<T: ?Sized, K> pub struct OwnerAPIGetHandler<T: ?Sized, C, K>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
/// Wallet instance /// Wallet instance
pub wallet: Arc<Mutex<Box<T>>>, pub wallet: Arc<Mutex<Box<T>>>,
phantom: PhantomData<K>, phantom: PhantomData<K>,
phantom_c: PhantomData<C>,
} }
impl<T: ?Sized, K> OwnerAPIGetHandler<T, K> impl<T: ?Sized, C, K> OwnerAPIGetHandler<T, C, K>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
/// Create a new owner API handler for GET methods /// Create a new owner API handler for GET methods
pub fn new(wallet: Arc<Mutex<Box<T>>>) -> OwnerAPIGetHandler<T, K> { pub fn new(wallet: Arc<Mutex<Box<T>>>) -> OwnerAPIGetHandler<T, C, K> {
OwnerAPIGetHandler { OwnerAPIGetHandler {
wallet, wallet,
phantom: PhantomData, phantom: PhantomData,
phantom_c: PhantomData,
} }
} }
fn retrieve_outputs( fn retrieve_outputs(
&self, &self,
req: &mut Request, req: &mut Request,
api: &mut APIOwner<T, K>, api: &mut APIOwner<T, C, K>,
) -> Result<(bool, Vec<OutputData>), Error> { ) -> Result<(bool, Vec<OutputData>), Error> {
let mut update_from_node = false; let mut update_from_node = false;
if let Ok(params) = req.get_ref::<UrlEncodedQuery>() { if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
@ -166,7 +174,7 @@ where
fn retrieve_summary_info( fn retrieve_summary_info(
&self, &self,
req: &mut Request, req: &mut Request,
api: &mut APIOwner<T, K>, api: &mut APIOwner<T, C, K>,
) -> Result<(bool, WalletInfo), Error> { ) -> Result<(bool, WalletInfo), Error> {
let mut update_from_node = false; let mut update_from_node = false;
if let Ok(params) = req.get_ref::<UrlEncodedQuery>() { if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
@ -180,12 +188,12 @@ where
fn node_height( fn node_height(
&self, &self,
_req: &mut Request, _req: &mut Request,
api: &mut APIOwner<T, K>, api: &mut APIOwner<T, C, K>,
) -> Result<(u64, bool), Error> { ) -> Result<(u64, bool), Error> {
api.node_height() api.node_height()
} }
fn handle_request(&self, req: &mut Request, api: &mut APIOwner<T, K>) -> IronResult<Response> { fn handle_request(&self, req: &mut Request, api: &mut APIOwner<T, C, K>) -> IronResult<Response> {
let url = req.url.clone(); let url = req.url.clone();
let path_elems = url.path(); let path_elems = url.path();
match *path_elems.last().unwrap() { match *path_elems.last().unwrap() {
@ -203,9 +211,10 @@ where
} }
} }
impl<T: ?Sized, K> Handler for OwnerAPIGetHandler<T, K> impl<T: ?Sized, C, K> Handler for OwnerAPIGetHandler<T, C, K>
where where
T: WalletBackend<K> + WalletClient + Send + Sync + 'static, T: WalletBackend<C, K> + Send + Sync + 'static,
C: WalletClient + 'static,
K: Keychain + 'static, K: Keychain + 'static,
{ {
fn handle(&self, req: &mut Request) -> IronResult<Response> { fn handle(&self, req: &mut Request) -> IronResult<Response> {
@ -235,30 +244,34 @@ where
} }
/// Handles all owner API POST requests /// Handles all owner API POST requests
pub struct OwnerAPIPostHandler<T: ?Sized, K> pub struct OwnerAPIPostHandler<T: ?Sized, C, K>
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
/// Wallet instance /// Wallet instance
pub wallet: Arc<Mutex<Box<T>>>, pub wallet: Arc<Mutex<Box<T>>>,
phantom: PhantomData<K>, phantom: PhantomData<K>,
phantom_c: PhantomData<C>,
} }
impl<T: ?Sized, K> OwnerAPIPostHandler<T, K> impl<T: ?Sized, C, K> OwnerAPIPostHandler<T, C, K>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
/// New POST handler /// New POST handler
pub fn new(wallet: Arc<Mutex<Box<T>>>) -> OwnerAPIPostHandler<T, K> { pub fn new(wallet: Arc<Mutex<Box<T>>>) -> OwnerAPIPostHandler<T, C, K> {
OwnerAPIPostHandler { OwnerAPIPostHandler {
wallet, wallet,
phantom: PhantomData, phantom: PhantomData,
phantom_c: PhantomData,
} }
} }
fn issue_send_tx(&self, req: &mut Request, api: &mut APIOwner<T, K>) -> Result<(), Error> { fn issue_send_tx(&self, req: &mut Request, api: &mut APIOwner<T, C, K>) -> Result<(), Error> {
let struct_body = req.get::<bodyparser::Struct<SendTXArgs>>(); let struct_body = req.get::<bodyparser::Struct<SendTXArgs>>();
match struct_body { match struct_body {
Ok(Some(args)) => api.issue_send_tx( Ok(Some(args)) => api.issue_send_tx(
@ -284,12 +297,12 @@ where
} }
} }
fn issue_burn_tx(&self, _req: &mut Request, api: &mut APIOwner<T, K>) -> Result<(), Error> { fn issue_burn_tx(&self, _req: &mut Request, api: &mut APIOwner<T, C, K>) -> Result<(), Error> {
// TODO: Args // TODO: Args
api.issue_burn_tx(60, 10, 1000) api.issue_burn_tx(60, 10, 1000)
} }
fn handle_request(&self, req: &mut Request, api: &mut APIOwner<T, K>) -> Result<String, Error> { fn handle_request(&self, req: &mut Request, api: &mut APIOwner<T, C, K>) -> Result<String, Error> {
let url = req.url.clone(); let url = req.url.clone();
let path_elems = url.path(); let path_elems = url.path();
match *path_elems.last().unwrap() { match *path_elems.last().unwrap() {
@ -323,9 +336,10 @@ where
} }
} }
impl<T: ?Sized, K> Handler for OwnerAPIPostHandler<T, K> impl<T: ?Sized, C, K> Handler for OwnerAPIPostHandler<T, C, K>
where where
T: WalletBackend<K> + WalletClient + Send + Sync + 'static, T: WalletBackend<C, K> + Send + Sync + 'static,
C: WalletClient + 'static,
K: Keychain + 'static, K: Keychain + 'static,
{ {
fn handle(&self, req: &mut Request) -> IronResult<Response> { fn handle(&self, req: &mut Request) -> IronResult<Response> {
@ -373,33 +387,37 @@ impl Handler for OwnerAPIOptionsHandler where {
} }
/// API Handler/Wrapper for foreign functions /// API Handler/Wrapper for foreign functions
pub struct ForeignAPIHandler<T: ?Sized, K> pub struct ForeignAPIHandler<T: ?Sized, C, K>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
/// Wallet instance /// Wallet instance
pub wallet: Arc<Mutex<Box<T>>>, pub wallet: Arc<Mutex<Box<T>>>,
phantom: PhantomData<K>, phantom: PhantomData<K>,
phantom_c: PhantomData<C>,
} }
impl<T: ?Sized, K> ForeignAPIHandler<T, K> impl<T: ?Sized, C, K> ForeignAPIHandler<T, C, K>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
/// create a new api handler /// create a new api handler
pub fn new(wallet: Arc<Mutex<Box<T>>>) -> ForeignAPIHandler<T, K> { pub fn new(wallet: Arc<Mutex<Box<T>>>) -> ForeignAPIHandler<T, C, K> {
ForeignAPIHandler { ForeignAPIHandler {
wallet, wallet,
phantom: PhantomData, phantom: PhantomData,
phantom_c: PhantomData,
} }
} }
fn build_coinbase( fn build_coinbase(
&self, &self,
req: &mut Request, req: &mut Request,
api: &mut APIForeign<T, K>, api: &mut APIForeign<T, C, K>,
) -> Result<CbData, Error> { ) -> Result<CbData, Error> {
let struct_body = req.get::<bodyparser::Struct<BlockFees>>(); let struct_body = req.get::<bodyparser::Struct<BlockFees>>();
match struct_body { match struct_body {
@ -419,7 +437,7 @@ where
} }
} }
fn receive_tx(&self, req: &mut Request, api: &mut APIForeign<T, K>) -> Result<Slate, Error> { fn receive_tx(&self, req: &mut Request, api: &mut APIForeign<T, C, K>) -> Result<Slate, Error> {
let struct_body = req.get::<bodyparser::Struct<Slate>>(); let struct_body = req.get::<bodyparser::Struct<Slate>>();
if let Ok(Some(mut slate)) = struct_body { if let Ok(Some(mut slate)) = struct_body {
api.receive_tx(&mut slate)?; api.receive_tx(&mut slate)?;
@ -432,7 +450,7 @@ where
fn handle_request( fn handle_request(
&self, &self,
req: &mut Request, req: &mut Request,
api: &mut APIForeign<T, K>, api: &mut APIForeign<T, C, K>,
) -> IronResult<Response> { ) -> IronResult<Response> {
let url = req.url.clone(); let url = req.url.clone();
let path_elems = url.path(); let path_elems = url.path();
@ -448,9 +466,10 @@ where
} }
} }
} }
impl<T: ?Sized, K> Handler for ForeignAPIHandler<T, K> impl<T: ?Sized, C, K> Handler for ForeignAPIHandler<T, C, K>
where where
T: WalletBackend<K> + WalletClient + Send + Sync + 'static, T: WalletBackend<C, K> + Send + Sync + 'static,
C: WalletClient + Send + Sync + 'static,
K: Keychain + 'static, K: Keychain + 'static,
{ {
fn handle(&self, req: &mut Request) -> IronResult<Response> { fn handle(&self, req: &mut Request) -> IronResult<Response> {

View file

@ -72,6 +72,10 @@ pub enum ErrorKind {
#[fail(display = "Transaction error")] #[fail(display = "Transaction error")]
Transaction(transaction::Error), Transaction(transaction::Error),
/// API Error
#[fail(display = "Client Callback Error: {}", _0)]
ClientCallback(&'static str),
/// Secp Error /// Secp Error
#[fail(display = "Secp error")] #[fail(display = "Secp error")]
Secp, Secp,

View file

@ -15,12 +15,13 @@
//! Wallet key management functions //! Wallet key management functions
use keychain::{Identifier, Keychain}; use keychain::{Identifier, Keychain};
use libwallet::error::Error; use libwallet::error::Error;
use libwallet::types::WalletBackend; use libwallet::types::{WalletBackend, WalletClient};
/// Get next available key in the wallet /// Get next available key in the wallet
pub fn next_available_key<T: ?Sized, K>(wallet: &mut T) -> Result<(Identifier, u32), Error> pub fn next_available_key<T: ?Sized, C, K>(wallet: &mut T) -> Result<(Identifier, u32), Error>
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let root_key_id = wallet.keychain().root_key_id(); let root_key_id = wallet.keychain().root_key_id();
@ -30,12 +31,13 @@ where
} }
/// Retrieve an existing key from a wallet /// Retrieve an existing key from a wallet
pub fn retrieve_existing_key<T: ?Sized, 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<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let existing = wallet.get(&key_id)?; let existing = wallet.get(&key_id)?;

View file

@ -41,12 +41,13 @@ struct OutputResult {
pub blinding: SecretKey, pub blinding: SecretKey,
} }
fn identify_utxo_outputs<T, K>( fn identify_utxo_outputs<T, C, K>(
wallet: &mut T, wallet: &mut T,
outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool)>, outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool)>,
) -> Result<Vec<OutputResult>, Error> ) -> Result<Vec<OutputResult>, Error>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let mut wallet_outputs: Vec<OutputResult> = Vec::new(); let mut wallet_outputs: Vec<OutputResult> = Vec::new();
@ -56,7 +57,7 @@ where
"Scanning {} outputs in the current Grin utxo set", "Scanning {} outputs in the current Grin utxo set",
outputs.len(), outputs.len(),
); );
let current_chain_height = wallet.get_chain_height()?; let current_chain_height = wallet.client().get_chain_height()?;
for output in outputs.iter() { for output in outputs.iter() {
let (commit, proof, is_coinbase) = output; let (commit, proof, is_coinbase) = output;
@ -96,13 +97,14 @@ where
/// Attempts to populate a list of outputs with their /// Attempts to populate a list of outputs with their
/// correct child indices based on the root key /// correct child indices based on the root key
fn populate_child_indices<T, K>( fn populate_child_indices<T, C, K>(
wallet: &mut T, wallet: &mut T,
outputs: &mut Vec<OutputResult>, outputs: &mut Vec<OutputResult>,
max_derivations: u32, max_derivations: u32,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
info!( info!(
@ -146,9 +148,10 @@ where
} }
/// Restore a wallet /// Restore a wallet
pub fn restore<T, K>(wallet: &mut T) -> Result<(), Error> pub fn restore<T, C, K>(wallet: &mut T) -> Result<(), Error>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let max_derivations = 1_000_000; let max_derivations = 1_000_000;
@ -170,7 +173,7 @@ where
let mut result_vec: Vec<OutputResult> = vec![]; let mut result_vec: Vec<OutputResult> = vec![];
loop { loop {
let (highest_index, last_retrieved_index, outputs) = let (highest_index, last_retrieved_index, outputs) =
wallet.get_outputs_by_pmmr_index(start_index, batch_size)?; wallet.client().get_outputs_by_pmmr_index(start_index, batch_size)?;
info!( info!(
LOGGER, LOGGER,
"Retrieved {} outputs, up to index {}. (Highest index: {})", "Retrieved {} outputs, up to index {}. (Highest index: {})",

View file

@ -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, 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,
@ -43,7 +43,8 @@ pub fn build_send_tx_slate<T: ?Sized, K>(
Error, Error,
> >
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let (elems, inputs, change, change_derivation, amount, fee) = select_send_tx( let (elems, inputs, change, change_derivation, amount, fee) = select_send_tx(
@ -122,7 +123,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, K>( pub fn build_recipient_output_with_slate<T: ?Sized, C, K>(
wallet: &mut T, wallet: &mut T,
slate: &mut Slate, slate: &mut Slate,
) -> Result< ) -> Result<
@ -134,7 +135,8 @@ pub fn build_recipient_output_with_slate<T: ?Sized, K>(
Error, Error,
> >
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
// Create a potential output for this transaction // Create a potential output for this transaction
@ -182,7 +184,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, 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,
@ -202,7 +204,8 @@ pub fn select_send_tx<T: ?Sized, K>(
Error, Error,
> >
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let key_id = wallet.keychain().root_key_id(); let key_id = wallet.keychain().root_key_id();
@ -287,14 +290,15 @@ where
} }
/// Selects inputs and change for a transaction /// Selects inputs and change for a transaction
pub fn inputs_and_change<T: ?Sized, 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,
fee: u64, fee: u64,
) -> Result<(Vec<Box<build::Append<K>>>, u64, Option<u32>), Error> ) -> Result<(Vec<Box<build::Append<K>>>, u64, Option<u32>), Error>
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let mut parts = vec![]; let mut parts = vec![];

View file

@ -25,9 +25,10 @@ use util::LOGGER;
/// 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, K>(wallet: &mut T, slate: &mut Slate) -> Result<(), Error> pub fn receive_tx<T: ?Sized, C, K>(wallet: &mut T, slate: &mut Slate) -> Result<(), Error>
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
// create an output using the amount in the slate // create an output using the amount in the slate
@ -53,7 +54,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, 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,
@ -68,11 +69,12 @@ pub fn create_send_tx<T: ?Sized, K>(
Error, Error,
> >
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
// Get lock height // Get lock height
let current_height = wallet.get_chain_height()?; let current_height = wallet.client().get_chain_height()?;
// ensure outputs we're selecting are up to date // ensure outputs we're selecting are up to date
updater::refresh_outputs(wallet)?; updater::refresh_outputs(wallet)?;
@ -110,13 +112,14 @@ where
} }
/// Complete a transaction as the sender /// Complete a transaction as the sender
pub fn complete_tx<T: ?Sized, K>( pub fn complete_tx<T: ?Sized, C, K>(
wallet: &mut T, wallet: &mut T,
slate: &mut Slate, slate: &mut Slate,
context: &sigcontext::Context, context: &sigcontext::Context,
) -> Result<(), Error> ) -> Result<(), Error>
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
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)?;
@ -129,14 +132,15 @@ where
} }
/// Issue a burn tx /// Issue a burn tx
pub fn issue_burn_tx<T: ?Sized, 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,
max_outputs: usize, max_outputs: usize,
) -> Result<Transaction, Error> ) -> Result<Transaction, Error>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
// TODO // TODO
@ -144,7 +148,7 @@ where
// &Identifier::zero()); // &Identifier::zero());
let keychain = wallet.keychain().clone(); let keychain = wallet.keychain().clone();
let current_height = wallet.get_chain_height()?; let current_height = wallet.client().get_chain_height()?;
let _ = updater::refresh_outputs(wallet); let _ = updater::refresh_outputs(wallet);

View file

@ -33,12 +33,13 @@ use util::secp::pedersen;
use util::{self, LOGGER}; use util::{self, LOGGER};
/// 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, K>( pub fn retrieve_outputs<T: ?Sized, C, K>(
wallet: &mut T, wallet: &mut T,
show_spent: bool, show_spent: bool,
) -> Result<Vec<OutputData>, Error> ) -> Result<Vec<OutputData>, Error>
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let root_key_id = wallet.keychain().clone().root_key_id(); let root_key_id = wallet.keychain().clone().root_key_id();
@ -61,23 +62,25 @@ 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, K>(wallet: &mut T) -> Result<(), Error> pub fn refresh_outputs<T: ?Sized, C, K>(wallet: &mut T) -> Result<(), Error>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let height = wallet.get_chain_height()?; let height = wallet.client().get_chain_height()?;
refresh_output_state(wallet, height)?; refresh_output_state(wallet, height)?;
Ok(()) Ok(())
} }
/// 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, K>( pub fn map_wallet_outputs<T: ?Sized, C, K>(
wallet: &mut T, wallet: &mut T,
) -> Result<HashMap<pedersen::Commitment, Identifier>, Error> ) -> Result<HashMap<pedersen::Commitment, Identifier>, Error>
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let mut wallet_outputs: HashMap<pedersen::Commitment, Identifier> = HashMap::new(); let mut wallet_outputs: HashMap<pedersen::Commitment, Identifier> = HashMap::new();
@ -94,14 +97,15 @@ where
} }
/// Apply refreshed API output data to the wallet /// Apply refreshed API output data to the wallet
pub fn apply_api_outputs<T: ?Sized, 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>, api_outputs: &HashMap<pedersen::Commitment, String>,
height: u64, height: u64,
) -> Result<(), libwallet::Error> ) -> Result<(), libwallet::Error>
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
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
@ -129,9 +133,10 @@ 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, K>(wallet: &mut T, height: u64) -> Result<(), Error> fn refresh_output_state<T: ?Sized, C, K>(wallet: &mut T, height: u64) -> Result<(), Error>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
debug!(LOGGER, "Refreshing wallet outputs"); debug!(LOGGER, "Refreshing wallet outputs");
@ -142,15 +147,16 @@ where
let wallet_output_keys = wallet_outputs.keys().map(|commit| commit.clone()).collect(); let wallet_output_keys = wallet_outputs.keys().map(|commit| commit.clone()).collect();
let api_outputs = wallet.get_outputs_from_node(wallet_output_keys)?; let api_outputs = wallet.client().get_outputs_from_node(wallet_output_keys)?;
apply_api_outputs(wallet, &wallet_outputs, &api_outputs, height)?; apply_api_outputs(wallet, &wallet_outputs, &api_outputs, height)?;
clean_old_unconfirmed(wallet, height)?; clean_old_unconfirmed(wallet, height)?;
Ok(()) Ok(())
} }
fn clean_old_unconfirmed<T: ?Sized, 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<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
if height < 500 { if height < 500 {
@ -172,9 +178,10 @@ 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, K>(wallet: &mut T) -> Result<WalletInfo, Error> pub fn retrieve_info<T: ?Sized, C, K>(wallet: &mut T) -> Result<WalletInfo, Error>
where where
T: WalletBackend<K> + WalletClient, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let current_height = wallet.details().last_confirmed_height; let current_height = wallet.details().last_confirmed_height;
@ -213,9 +220,10 @@ where
} }
/// Build a coinbase output and insert into wallet /// Build a coinbase output and insert into wallet
pub fn build_coinbase<T: ?Sized, K>(wallet: &mut T, block_fees: &BlockFees) -> Result<CbData, Error> pub fn build_coinbase<T: ?Sized, C, K>(wallet: &mut T, block_fees: &BlockFees) -> Result<CbData, Error>
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
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)?;
@ -238,12 +246,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, 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<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
let root_key_id = wallet.keychain().root_key_id(); let root_key_id = wallet.keychain().root_key_id();

View file

@ -34,14 +34,16 @@ use libwallet::error::{Error, ErrorKind};
use util::secp::pedersen; use util::secp::pedersen;
/// Combined trait to allow dynamic wallet dispatch /// Combined trait to allow dynamic wallet dispatch
pub trait WalletInst<K>: WalletBackend<K> + WalletClient + Send + Sync + 'static pub trait WalletInst<C, K>: WalletBackend<C, K> + Send + Sync + 'static
where where
C: WalletClient,
K: Keychain, K: Keychain,
{ {
} }
impl<T, K> WalletInst<K> for T impl<T, C, K> WalletInst<C, K> for T
where where
T: WalletBackend<K> + WalletClient + Send + Sync + 'static, T: WalletBackend<C, K> + Send + Sync + 'static,
C: WalletClient,
K: Keychain, K: Keychain,
{ {
} }
@ -50,8 +52,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<K> pub trait WalletBackend<C, K>
where where
C: WalletClient,
K: Keychain, K: Keychain,
{ {
/// Initialize with whatever stored credentials we have /// Initialize with whatever stored credentials we have
@ -63,6 +66,9 @@ where
/// Return the keychain being used /// Return the keychain being used
fn keychain(&mut self) -> &mut K; fn keychain(&mut self) -> &mut K;
/// Return the client being used
fn client(&mut self) -> &mut C;
/// Iterate over all output data stored by the backend /// Iterate over all output data stored by the backend
fn iter<'a>(&'a self) -> Box<Iterator<Item = OutputData> + 'a>; fn iter<'a>(&'a self) -> Box<Iterator<Item = OutputData> + 'a>;
@ -121,12 +127,12 @@ pub trait WalletOutputBatch {
/// Encapsulate all communication functions. No functions within libwallet /// Encapsulate all communication functions. No functions within libwallet
/// should care about communication details /// should care about communication details
pub trait WalletClient { pub trait WalletClient: 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;
/// Call the wallet API to create a coinbase transaction /// Call the wallet API to create a coinbase transaction
fn create_coinbase(&self, block_fees: &BlockFees) -> Result<CbData, Error>; fn create_coinbase(&self, dest: &str, block_fees: &BlockFees) -> Result<CbData, Error>;
/// Send a transaction slate to another listening wallet and return result /// Send a transaction slate to another listening wallet and return result
/// TODO: Probably need a slate wrapper type /// TODO: Probably need a slate wrapper type

View file

@ -13,7 +13,6 @@
// limitations under the License. // limitations under the License.
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use std::{fs, path}; use std::{fs, path};
@ -22,12 +21,9 @@ use failure::ResultExt;
use keychain::{Identifier, Keychain}; use keychain::{Identifier, Keychain};
use store::{self, option_to_not_found, to_key}; use store::{self, option_to_not_found, to_key};
use client;
use libtx::slate::Slate;
use libwallet::types::*; use libwallet::types::*;
use libwallet::{internal, Error, ErrorKind}; use libwallet::{internal, Error, ErrorKind};
use types::{WalletConfig, WalletSeed}; use types::{WalletConfig, WalletSeed};
use util::secp::pedersen;
pub const DB_DIR: &'static str = "wallet_data"; pub const DB_DIR: &'static str = "wallet_data";
@ -47,18 +43,19 @@ pub fn wallet_db_exists(config: WalletConfig) -> bool {
db_path.exists() db_path.exists()
} }
pub struct LMDBBackend<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
passphrase: String, passphrase: String,
/// Keychain /// Keychain
keychain: Option<K>, keychain: Option<K>,
/// client
client: C,
} }
impl<K> LMDBBackend<K> { impl<C, K> LMDBBackend<C, K> {
pub fn new(config: WalletConfig, passphrase: &str) -> Result<Self, Error> { pub fn new(config: WalletConfig, passphrase: &str, client: C) -> 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!");
@ -69,6 +66,7 @@ impl<K> LMDBBackend<K> {
config: config.clone(), config: config.clone(),
passphrase: String::from(passphrase), passphrase: String::from(passphrase),
keychain: None, keychain: None,
client: client,
}) })
} }
@ -80,8 +78,9 @@ impl<K> LMDBBackend<K> {
} }
} }
impl<K> WalletBackend<K> for LMDBBackend<K> impl<C, K> WalletBackend<C, K> for LMDBBackend<C, K>
where where
C: WalletClient,
K: Keychain, K: Keychain,
{ {
/// Initialise with whatever stored credentials we have /// Initialise with whatever stored credentials we have
@ -106,6 +105,11 @@ where
self.keychain.as_mut().unwrap() self.keychain.as_mut().unwrap()
} }
/// Return the client being used
fn client(&mut self) -> &mut C {
&mut self.client
}
fn get(&self, id: &Identifier) -> Result<OutputData, Error> { fn get(&self, id: &Identifier) -> Result<OutputData, Error> {
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()); let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
option_to_not_found(self.db.get_ser(&key), &format!("Key Id: {}", id)).map_err(|e| e.into()) option_to_not_found(self.db.get_ser(&key), &format!("Key Id: {}", id)).map_err(|e| e.into())
@ -159,13 +163,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, K: 'a> { pub struct Batch<'a, C: 'a, K: 'a>
store: &'a LMDBBackend<K>, where
C: WalletClient,
K: Keychain,
{
store: &'a LMDBBackend<C, K>,
db: RefCell<Option<store::Batch<'a>>>, db: RefCell<Option<store::Batch<'a>>>,
} }
#[allow(missing_docs)] #[allow(missing_docs)]
impl<'a, K> WalletOutputBatch for Batch<'a, K> { impl<'a, C, K> WalletOutputBatch for Batch<'a, C, K>
where
C: WalletClient,
K: Keychain,
{
fn save(&mut self, out: OutputData) -> Result<(), Error> { fn save(&mut self, out: OutputData) -> Result<(), Error> {
let key = to_key(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec()); let key = to_key(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec());
self.db.borrow().as_ref().unwrap().put_ser(&key, &out)?; self.db.borrow().as_ref().unwrap().put_ser(&key, &out)?;
@ -205,65 +217,3 @@ impl<'a, K> WalletOutputBatch for Batch<'a, K> {
Ok(()) Ok(())
} }
} }
impl<K> WalletClient for LMDBBackend<K> {
/// Return URL for check node
fn node_url(&self) -> &str {
&self.config.check_node_api_http_addr
}
/// Call the wallet API to create a coinbase transaction
fn create_coinbase(&self, block_fees: &BlockFees) -> Result<CbData, Error> {
let res = client::create_coinbase(self.node_url(), block_fees)
.context(ErrorKind::WalletComms(format!("Creating Coinbase")))?;
Ok(res)
}
/// Send a transaction slate to another listening wallet and return result
fn send_tx_slate(&self, addr: &str, slate: &Slate) -> Result<Slate, Error> {
let res = client::send_tx_slate(addr, slate)
.context(ErrorKind::WalletComms(format!("Sending transaction")))?;
Ok(res)
}
/// Posts a tranaction to a grin node
fn post_tx(&self, tx: &TxWrapper, fluff: bool) -> Result<(), Error> {
let res = client::post_tx(self.node_url(), tx, fluff).context(ErrorKind::Node)?;
Ok(res)
}
/// retrieves the current tip from the specified grin node
fn get_chain_height(&self) -> Result<u64, Error> {
let res = client::get_chain_height(self.node_url()).context(ErrorKind::Node)?;
Ok(res)
}
/// retrieve a list of outputs from the specified grin node
/// need "by_height" and "by_id" variants
fn get_outputs_from_node(
&self,
wallet_outputs: Vec<pedersen::Commitment>,
) -> Result<HashMap<pedersen::Commitment, String>, Error> {
let res = client::get_outputs_from_node(self.node_url(), wallet_outputs)
.context(ErrorKind::Node)?;
Ok(res)
}
/// Outputs by PMMR index
fn get_outputs_by_pmmr_index(
&self,
start_height: u64,
max_outputs: u64,
) -> Result<
(
u64,
u64,
Vec<(pedersen::Commitment, pedersen::RangeProof, bool)>,
),
Error,
> {
let res = client::get_outputs_by_pmmr_index(self.node_url(), start_height, max_outputs)
.context(ErrorKind::Node)?;
Ok(res)
}
}

View file

@ -28,11 +28,11 @@ use core::core::hash::Hashed;
use core::core::{Output, OutputFeatures, OutputIdentifier, Transaction, TxKernel}; use core::core::{Output, OutputFeatures, OutputIdentifier, Transaction, TxKernel};
use core::{consensus, global, pow}; use core::{consensus, global, pow};
use keychain::ExtKeychain; use keychain::ExtKeychain;
use wallet::WalletConfig; use wallet::{HTTPWalletClient, WalletConfig};
use wallet::file_wallet::FileWallet; use wallet::file_wallet::FileWallet;
use wallet::libwallet::internal::updater; use wallet::libwallet::internal::updater;
use wallet::libwallet::types::{BlockFees, BlockIdentifier, OutputStatus, use wallet::libwallet::types::{BlockFees, BlockIdentifier, OutputStatus,
WalletBackend}; WalletBackend, WalletClient};
use wallet::libwallet::{Error, ErrorKind}; use wallet::libwallet::{Error, ErrorKind};
use util; use util;
@ -40,9 +40,10 @@ use util::secp::pedersen;
/// Mostly for testing, refreshes output state against a local chain instance /// Mostly for testing, refreshes output state against a local chain instance
/// instead of via an http API call /// instead of via an http API call
pub fn refresh_output_state_local<T, K>(wallet: &mut T, chain: &chain::Chain) -> Result<(), Error> pub fn refresh_output_state_local<T, C, K>(wallet: &mut T, chain: &chain::Chain) -> Result<(), Error>
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: keychain::Keychain, K: keychain::Keychain,
{ {
let wallet_outputs = updater::map_wallet_outputs(wallet)?; let wallet_outputs = updater::map_wallet_outputs(wallet)?;
@ -71,12 +72,13 @@ where
/// (0:total, 1:amount_awaiting_confirmation, 2:confirmed but locked, /// (0:total, 1:amount_awaiting_confirmation, 2:confirmed but locked,
/// 3:currently_spendable, 4:locked total) TODO: Should be a wallet lib /// 3:currently_spendable, 4:locked total) TODO: Should be a wallet lib
/// function with nicer return values /// function with nicer return values
pub fn get_wallet_balances<T, K>( pub fn get_wallet_balances<T, C, K>(
wallet: &mut T, wallet: &mut T,
height: u64, height: u64,
) -> Result<(u64, u64, u64, u64, u64), Error> ) -> Result<(u64, u64, u64, u64, u64), Error>
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: keychain::Keychain, K: keychain::Keychain,
{ {
let mut unspent_total = 0; let mut unspent_total = 0;
@ -156,9 +158,10 @@ pub fn add_block_with_reward(chain: &Chain, txs: Vec<&Transaction>, reward: (Out
/// 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<T, K>(chain: &Chain, txs: Vec<&Transaction>, wallet: &mut T) pub fn award_block_to_wallet<T, C, K>(chain: &Chain, txs: Vec<&Transaction>, wallet: &mut T)
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: keychain::Keychain, K: keychain::Keychain,
{ {
let prev = chain.head_header().unwrap(); let prev = chain.head_header().unwrap();
@ -183,9 +186,10 @@ where
} }
/// adds many block rewards to a wallet, no transactions /// adds many block rewards to a wallet, no transactions
pub fn award_blocks_to_wallet<T, K>(chain: &Chain, wallet: &mut T, num_rewards: usize) pub fn award_blocks_to_wallet<T, C, K>(chain: &Chain, wallet: &mut T, num_rewards: usize)
where where
T: WalletBackend<K>, T: WalletBackend<C, K>,
C: WalletClient,
K: keychain::Keychain, K: keychain::Keychain,
{ {
for _ in 0..num_rewards { for _ in 0..num_rewards {
@ -194,11 +198,11 @@ where
} }
/// Create a new wallet in a particular directory /// Create a new wallet in a particular directory
pub fn create_wallet(dir: &str) -> FileWallet<ExtKeychain> { pub fn create_wallet(dir: &str, client: HTTPWalletClient) -> FileWallet<HTTPWalletClient, 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);
wallet::WalletSeed::init_file(&wallet_config).expect("Failed to create wallet seed file."); wallet::WalletSeed::init_file(&wallet_config).expect("Failed to create wallet seed file.");
let mut wallet = FileWallet::new(wallet_config.clone(), "") let mut wallet = FileWallet::new(wallet_config.clone(), "", 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!(

View file

@ -35,6 +35,7 @@ use chain::types::NoopAdapter;
use core::global::ChainTypes; use core::global::ChainTypes;
use core::{global, pow}; use core::{global, pow};
use util::LOGGER; use util::LOGGER;
use wallet::HTTPWalletClient;
use wallet::libwallet::internal::selection; use wallet::libwallet::internal::selection;
fn clean_output_dir(test_dir: &str) { fn clean_output_dir(test_dir: &str) {
@ -60,9 +61,10 @@ fn setup(test_dir: &str, chain_dir: &str) -> Chain {
/// Build and test new version of sending API /// Build and test new version of sending API
#[test] #[test]
fn build_transaction() { fn build_transaction() {
let client = HTTPWalletClient::new("");
let chain = setup("test_output", "build_transaction_2/.grin"); let chain = setup("test_output", "build_transaction_2/.grin");
let mut wallet1 = common::create_wallet("test_output/build_transaction_2/wallet1"); let mut wallet1 = common::create_wallet("test_output/build_transaction_2/wallet1", client.clone());
let mut wallet2 = common::create_wallet("test_output/build_transaction_2/wallet2"); let mut wallet2 = common::create_wallet("test_output/build_transaction_2/wallet2", client);
common::award_blocks_to_wallet(&chain, &mut wallet1, 10); common::award_blocks_to_wallet(&chain, &mut wallet1, 10);
// Wallet 1 has 600 Grins, wallet 2 has 0. Create a transaction that sends // Wallet 1 has 600 Grins, wallet 2 has 0. Create a transaction that sends
// 300 Grins from wallet 1 to wallet 2, using libtx // 300 Grins from wallet 1 to wallet 2, using libtx