mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
WalletClient trait refactor (#1238)
* refactor WalletClient * revert chain changes * missing files
This commit is contained in:
parent
c7d78446f3
commit
49cbab90f6
17 changed files with 432 additions and 433 deletions
|
@ -27,7 +27,7 @@ use std::default::Default;
|
|||
use std::sync::{Arc, Mutex};
|
||||
use std::{fs, thread, time};
|
||||
|
||||
use wallet::{FileWallet, WalletConfig};
|
||||
use wallet::{HTTPWalletClient, FileWallet, WalletConfig};
|
||||
|
||||
/// Just removes all results from previous runs
|
||||
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 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 {
|
||||
//panic!("Error initting wallet seed: {}", e);
|
||||
}
|
||||
|
||||
let wallet: FileWallet<keychain::ExtKeychain> =
|
||||
FileWallet::new(self.wallet_config.clone(), "").unwrap_or_else(|e| {
|
||||
let wallet: FileWallet<HTTPWalletClient, keychain::ExtKeychain> =
|
||||
FileWallet::new(self.wallet_config.clone(), "", client).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Error creating wallet: {:?} Config: {:?}",
|
||||
e, self.wallet_config
|
||||
|
@ -300,7 +302,8 @@ impl LocalServerContainer {
|
|||
let keychain: keychain::ExtKeychain = wallet_seed
|
||||
.derive_keychain("")
|
||||
.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));
|
||||
wallet.keychain = Some(keychain);
|
||||
let _ = wallet::libwallet::internal::updater::refresh_outputs(&mut wallet);
|
||||
|
@ -324,9 +327,11 @@ impl LocalServerContainer {
|
|||
let keychain: keychain::ExtKeychain = wallet_seed
|
||||
.derive_keychain("")
|
||||
.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 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));
|
||||
wallet.keychain = Some(keychain);
|
||||
let _ =
|
||||
|
|
|
@ -53,7 +53,8 @@ use core::core::amount_to_hr_string;
|
|||
use core::global;
|
||||
use tui::ui;
|
||||
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
|
||||
pub mod built_info {
|
||||
|
@ -553,9 +554,10 @@ fn instantiate_wallet(
|
|||
wallet_config: WalletConfig,
|
||||
passphrase: &str,
|
||||
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 {
|
||||
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!(
|
||||
"Error creating DB wallet: {} Config: {:?}",
|
||||
e, wallet_config
|
||||
|
@ -564,7 +566,7 @@ fn instantiate_wallet(
|
|||
info!(LOGGER, "Using LMDB Backend for wallet");
|
||||
Box::new(db_wallet)
|
||||
} 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));
|
||||
info!(LOGGER, "Using File Backend for 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.");
|
||||
info!(LOGGER, "Wallet seed file created");
|
||||
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| {
|
||||
panic!(
|
||||
"Error creating DB wallet: {} Config: {:?}",
|
||||
|
|
|
@ -28,10 +28,188 @@ use tokio_core::reactor;
|
|||
|
||||
use api;
|
||||
use error::{Error, ErrorKind};
|
||||
use libwallet;
|
||||
use libtx::slate::Slate;
|
||||
use util::secp::pedersen;
|
||||
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.
|
||||
/// Will retry based on default "retry forever with backoff" behavior.
|
||||
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, "Backtrace: {}", e.backtrace().unwrap());
|
||||
Err(e)
|
||||
Err(e)?
|
||||
}
|
||||
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.
|
||||
fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
|
||||
let mut core =
|
||||
|
@ -116,94 +257,3 @@ fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, E
|
|||
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)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,9 +139,11 @@ impl<'a> Drop for FileBatch<'a> {
|
|||
/// Wallet information tracking all our outputs. Based on HD derivation and
|
||||
/// avoids storing any key data, only storing output amounts and child index.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileWallet<K> {
|
||||
pub struct FileWallet<C, K> {
|
||||
/// Keychain
|
||||
pub keychain: Option<K>,
|
||||
/// Client implementation
|
||||
pub client: C,
|
||||
/// Configuration
|
||||
pub config: WalletConfig,
|
||||
/// passphrase: TODO better ways of dealing with this other than storing
|
||||
|
@ -162,8 +164,9 @@ pub struct FileWallet<K> {
|
|||
pub details_bak_path: String,
|
||||
}
|
||||
|
||||
impl<K> WalletBackend<K> for FileWallet<K>
|
||||
impl<C, K> WalletBackend<C, K> for FileWallet<C, K>
|
||||
where
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// Initialize with whatever stored credentials we have
|
||||
|
@ -190,6 +193,11 @@ where
|
|||
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> {
|
||||
Box::new(self.outputs.values().cloned())
|
||||
}
|
||||
|
@ -320,98 +328,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<K> WalletClient for FileWallet<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>
|
||||
impl<C, K> FileWallet<C, K>
|
||||
where
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// 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 {
|
||||
keychain: None,
|
||||
client: client,
|
||||
config: config.clone(),
|
||||
passphrase: String::from(passphrase),
|
||||
outputs: HashMap::new(),
|
||||
|
|
|
@ -56,10 +56,10 @@ pub mod libwallet;
|
|||
pub mod lmdb_wallet;
|
||||
mod types;
|
||||
|
||||
pub use client::create_coinbase;
|
||||
pub use error::{Error, ErrorKind};
|
||||
pub use file_wallet::FileWallet;
|
||||
pub use client::{create_coinbase, HTTPWalletClient};
|
||||
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 types::{WalletConfig, WalletSeed};
|
||||
|
|
|
@ -31,27 +31,31 @@ use util::{self, LOGGER};
|
|||
|
||||
/// Wrapper around internal API functions, containing a reference to
|
||||
/// the wallet/keychain that they're acting upon
|
||||
pub struct APIOwner<'a, W: ?Sized, K>
|
||||
pub struct APIOwner<'a, W: ?Sized, C, K>
|
||||
where
|
||||
W: 'a + WalletBackend<K> + WalletClient,
|
||||
W: 'a + WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// Wallet, contains its keychain (TODO: Split these up into 2 traits
|
||||
/// perhaps)
|
||||
pub wallet: &'a mut Box<W>,
|
||||
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
|
||||
W: 'a + WalletBackend<K> + WalletClient,
|
||||
W: 'a + WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// 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 {
|
||||
wallet: wallet_in,
|
||||
phantom: PhantomData,
|
||||
phantom_c: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +107,7 @@ where
|
|||
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,
|
||||
Err(e) => {
|
||||
error!(
|
||||
|
@ -119,7 +123,7 @@ where
|
|||
|
||||
// All good here, so let's post it
|
||||
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
|
||||
lock_fn(self.wallet)?;
|
||||
|
@ -140,7 +144,7 @@ where
|
|||
max_outputs,
|
||||
)?;
|
||||
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(())
|
||||
}
|
||||
|
||||
|
@ -151,7 +155,7 @@ where
|
|||
|
||||
/// Retrieve current height from node
|
||||
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)),
|
||||
Err(_) => {
|
||||
let outputs = self.retrieve_outputs(true, false)?;
|
||||
|
@ -175,27 +179,31 @@ where
|
|||
|
||||
/// Wrapper around external API functions, intended to communicate
|
||||
/// with other parties
|
||||
pub struct APIForeign<'a, W: ?Sized, K>
|
||||
pub struct APIForeign<'a, W: ?Sized, C, K>
|
||||
where
|
||||
W: WalletBackend<K> + WalletClient + 'a,
|
||||
W: 'a + WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// Wallet, contains its keychain (TODO: Split these up into 2 traits
|
||||
/// perhaps)
|
||||
pub wallet: &'a mut Box<W>,
|
||||
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
|
||||
W: WalletBackend<K> + WalletClient,
|
||||
W: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// 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 {
|
||||
wallet: wallet_in,
|
||||
phantom: PhantomData,
|
||||
phantom_c: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ use keychain::Keychain;
|
|||
use libtx::slate::Slate;
|
||||
use libwallet::api::{APIForeign, APIOwner};
|
||||
use libwallet::types::{
|
||||
BlockFees, CbData, OutputData, SendTXArgs, WalletBackend, WalletClient, WalletInfo, WalletInst,
|
||||
BlockFees, CbData, OutputData, SendTXArgs, WalletBackend, WalletClient, WalletInfo
|
||||
};
|
||||
use libwallet::{Error, ErrorKind};
|
||||
|
||||
|
@ -40,10 +40,11 @@ use util::LOGGER;
|
|||
|
||||
/// Instantiate wallet Owner API for a single-use (command line) call
|
||||
/// Return a function containing a loaded API context to call
|
||||
pub fn owner_single_use<F, T: ?Sized, 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
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
F: FnOnce(&mut APIOwner<T, K>) -> Result<(), Error>,
|
||||
T: WalletBackend<C, K>,
|
||||
F: FnOnce(&mut APIOwner<T, C, K>) -> Result<(), Error>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut w = wallet;
|
||||
|
@ -55,10 +56,11 @@ where
|
|||
|
||||
/// Instantiate wallet Foreign API for a single-use (command line) call
|
||||
/// Return a function containing a loaded API context to call
|
||||
pub fn foreign_single_use<F, T: ?Sized, 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
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
F: FnOnce(&mut APIForeign<T, K>) -> Result<(), Error>,
|
||||
T: WalletBackend<C, K>,
|
||||
F: FnOnce(&mut APIForeign<T, C, K>) -> Result<(), Error>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
wallet.open_with_credentials()?;
|
||||
|
@ -69,11 +71,12 @@ where
|
|||
|
||||
/// Listener version, providing same API but listening for requests on a
|
||||
/// 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
|
||||
T: WalletInst<K>,
|
||||
OwnerAPIGetHandler<T, K>: Handler,
|
||||
OwnerAPIPostHandler<T, K>: Handler,
|
||||
T: WalletBackend<C, K>,
|
||||
OwnerAPIGetHandler<T, C, K>: Handler,
|
||||
OwnerAPIPostHandler<T, C, K>: Handler,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let wallet_arc = Arc::new(Mutex::new(wallet));
|
||||
|
@ -101,10 +104,11 @@ where
|
|||
|
||||
/// Listener version, providing same API but listening for requests on a
|
||||
/// 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
|
||||
T: WalletInst<K>,
|
||||
ForeignAPIHandler<T, K>: Handler,
|
||||
T: WalletBackend<C, K>,
|
||||
ForeignAPIHandler<T, C, K>: Handler,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let api_handler = ForeignAPIHandler::new(Arc::new(Mutex::new(wallet)));
|
||||
|
@ -126,33 +130,37 @@ where
|
|||
}
|
||||
/// API Handler/Wrapper for owner functions
|
||||
|
||||
pub struct OwnerAPIGetHandler<T: ?Sized, K>
|
||||
pub struct OwnerAPIGetHandler<T: ?Sized, C, K>
|
||||
where
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// Wallet instance
|
||||
pub wallet: Arc<Mutex<Box<T>>>,
|
||||
phantom: PhantomData<K>,
|
||||
phantom_c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized, K> OwnerAPIGetHandler<T, K>
|
||||
impl<T: ?Sized, C, K> OwnerAPIGetHandler<T, C, K>
|
||||
where
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// 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 {
|
||||
wallet,
|
||||
phantom: PhantomData,
|
||||
phantom_c: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn retrieve_outputs(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIOwner<T, K>,
|
||||
api: &mut APIOwner<T, C, K>,
|
||||
) -> Result<(bool, Vec<OutputData>), Error> {
|
||||
let mut update_from_node = false;
|
||||
if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
|
||||
|
@ -166,7 +174,7 @@ where
|
|||
fn retrieve_summary_info(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIOwner<T, K>,
|
||||
api: &mut APIOwner<T, C, K>,
|
||||
) -> Result<(bool, WalletInfo), Error> {
|
||||
let mut update_from_node = false;
|
||||
if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
|
||||
|
@ -180,12 +188,12 @@ where
|
|||
fn node_height(
|
||||
&self,
|
||||
_req: &mut Request,
|
||||
api: &mut APIOwner<T, K>,
|
||||
api: &mut APIOwner<T, C, K>,
|
||||
) -> Result<(u64, bool), Error> {
|
||||
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 path_elems = url.path();
|
||||
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
|
||||
T: WalletBackend<K> + WalletClient + Send + Sync + 'static,
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: WalletClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
fn handle(&self, req: &mut Request) -> IronResult<Response> {
|
||||
|
@ -235,30 +244,34 @@ where
|
|||
}
|
||||
|
||||
/// Handles all owner API POST requests
|
||||
pub struct OwnerAPIPostHandler<T: ?Sized, K>
|
||||
pub struct OwnerAPIPostHandler<T: ?Sized, C, K>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// Wallet instance
|
||||
pub wallet: Arc<Mutex<Box<T>>>,
|
||||
phantom: PhantomData<K>,
|
||||
phantom_c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized, K> OwnerAPIPostHandler<T, K>
|
||||
impl<T: ?Sized, C, K> OwnerAPIPostHandler<T, C, K>
|
||||
where
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// 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 {
|
||||
wallet,
|
||||
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>>();
|
||||
match struct_body {
|
||||
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
|
||||
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 path_elems = url.path();
|
||||
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
|
||||
T: WalletBackend<K> + WalletClient + Send + Sync + 'static,
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: WalletClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
fn handle(&self, req: &mut Request) -> IronResult<Response> {
|
||||
|
@ -373,33 +387,37 @@ impl Handler for OwnerAPIOptionsHandler where {
|
|||
}
|
||||
/// API Handler/Wrapper for foreign functions
|
||||
|
||||
pub struct ForeignAPIHandler<T: ?Sized, K>
|
||||
pub struct ForeignAPIHandler<T: ?Sized, C, K>
|
||||
where
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// Wallet instance
|
||||
pub wallet: Arc<Mutex<Box<T>>>,
|
||||
phantom: PhantomData<K>,
|
||||
phantom_c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized, K> ForeignAPIHandler<T, K>
|
||||
impl<T: ?Sized, C, K> ForeignAPIHandler<T, C, K>
|
||||
where
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// 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 {
|
||||
wallet,
|
||||
phantom: PhantomData,
|
||||
phantom_c: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_coinbase(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIForeign<T, K>,
|
||||
api: &mut APIForeign<T, C, K>,
|
||||
) -> Result<CbData, Error> {
|
||||
let struct_body = req.get::<bodyparser::Struct<BlockFees>>();
|
||||
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>>();
|
||||
if let Ok(Some(mut slate)) = struct_body {
|
||||
api.receive_tx(&mut slate)?;
|
||||
|
@ -432,7 +450,7 @@ where
|
|||
fn handle_request(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIForeign<T, K>,
|
||||
api: &mut APIForeign<T, C, K>,
|
||||
) -> IronResult<Response> {
|
||||
let url = req.url.clone();
|
||||
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
|
||||
T: WalletBackend<K> + WalletClient + Send + Sync + 'static,
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: WalletClient + Send + Sync + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
fn handle(&self, req: &mut Request) -> IronResult<Response> {
|
||||
|
|
|
@ -72,6 +72,10 @@ pub enum ErrorKind {
|
|||
#[fail(display = "Transaction error")]
|
||||
Transaction(transaction::Error),
|
||||
|
||||
/// API Error
|
||||
#[fail(display = "Client Callback Error: {}", _0)]
|
||||
ClientCallback(&'static str),
|
||||
|
||||
/// Secp Error
|
||||
#[fail(display = "Secp error")]
|
||||
Secp,
|
||||
|
|
|
@ -15,12 +15,13 @@
|
|||
//! Wallet key management functions
|
||||
use keychain::{Identifier, Keychain};
|
||||
use libwallet::error::Error;
|
||||
use libwallet::types::WalletBackend;
|
||||
use libwallet::types::{WalletBackend, WalletClient};
|
||||
|
||||
/// 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
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let root_key_id = wallet.keychain().root_key_id();
|
||||
|
@ -30,12 +31,13 @@ where
|
|||
}
|
||||
|
||||
/// 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,
|
||||
key_id: Identifier,
|
||||
) -> Result<(Identifier, u32), Error>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let existing = wallet.get(&key_id)?;
|
||||
|
|
|
@ -41,12 +41,13 @@ struct OutputResult {
|
|||
pub blinding: SecretKey,
|
||||
}
|
||||
|
||||
fn identify_utxo_outputs<T, K>(
|
||||
fn identify_utxo_outputs<T, C, K>(
|
||||
wallet: &mut T,
|
||||
outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool)>,
|
||||
) -> Result<Vec<OutputResult>, Error>
|
||||
where
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut wallet_outputs: Vec<OutputResult> = Vec::new();
|
||||
|
@ -56,7 +57,7 @@ where
|
|||
"Scanning {} outputs in the current Grin utxo set",
|
||||
outputs.len(),
|
||||
);
|
||||
let current_chain_height = wallet.get_chain_height()?;
|
||||
let current_chain_height = wallet.client().get_chain_height()?;
|
||||
|
||||
for output in outputs.iter() {
|
||||
let (commit, proof, is_coinbase) = output;
|
||||
|
@ -96,13 +97,14 @@ where
|
|||
|
||||
/// Attempts to populate a list of outputs with their
|
||||
/// correct child indices based on the root key
|
||||
fn populate_child_indices<T, K>(
|
||||
fn populate_child_indices<T, C, K>(
|
||||
wallet: &mut T,
|
||||
outputs: &mut Vec<OutputResult>,
|
||||
max_derivations: u32,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
info!(
|
||||
|
@ -146,9 +148,10 @@ where
|
|||
}
|
||||
|
||||
/// 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
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let max_derivations = 1_000_000;
|
||||
|
@ -170,7 +173,7 @@ where
|
|||
let mut result_vec: Vec<OutputResult> = vec![];
|
||||
loop {
|
||||
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!(
|
||||
LOGGER,
|
||||
"Retrieved {} outputs, up to index {}. (Highest index: {})",
|
||||
|
|
|
@ -25,7 +25,7 @@ use libwallet::types::*;
|
|||
/// and saves the private wallet identifiers of our selected outputs
|
||||
/// 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,
|
||||
num_participants: usize,
|
||||
amount: u64,
|
||||
|
@ -43,7 +43,8 @@ pub fn build_send_tx_slate<T: ?Sized, K>(
|
|||
Error,
|
||||
>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
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
|
||||
/// that actually performs the addition of the output to the
|
||||
/// 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,
|
||||
slate: &mut Slate,
|
||||
) -> Result<
|
||||
|
@ -134,7 +135,8 @@ pub fn build_recipient_output_with_slate<T: ?Sized, K>(
|
|||
Error,
|
||||
>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// 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
|
||||
/// wallet and the amount to send. Handles reading through the wallet data file,
|
||||
/// selecting outputs to spend and building the change.
|
||||
pub fn select_send_tx<T: ?Sized, K>(
|
||||
pub fn select_send_tx<T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
amount: u64,
|
||||
current_height: u64,
|
||||
|
@ -202,7 +204,8 @@ pub fn select_send_tx<T: ?Sized, K>(
|
|||
Error,
|
||||
>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let key_id = wallet.keychain().root_key_id();
|
||||
|
@ -287,14 +290,15 @@ where
|
|||
}
|
||||
|
||||
/// 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>,
|
||||
wallet: &mut T,
|
||||
amount: u64,
|
||||
fee: u64,
|
||||
) -> Result<(Vec<Box<build::Append<K>>>, u64, Option<u32>), Error>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut parts = vec![];
|
||||
|
|
|
@ -25,9 +25,10 @@ use util::LOGGER;
|
|||
|
||||
/// Receive a transaction, modifying the slate accordingly (which can then be
|
||||
/// 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
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// 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
|
||||
/// wallet
|
||||
pub fn create_send_tx<T: ?Sized, K>(
|
||||
pub fn create_send_tx<T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
amount: u64,
|
||||
minimum_confirmations: u64,
|
||||
|
@ -68,11 +69,12 @@ pub fn create_send_tx<T: ?Sized, K>(
|
|||
Error,
|
||||
>
|
||||
where
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// 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
|
||||
updater::refresh_outputs(wallet)?;
|
||||
|
||||
|
@ -110,13 +112,14 @@ where
|
|||
}
|
||||
|
||||
/// Complete a transaction as the sender
|
||||
pub fn complete_tx<T: ?Sized, K>(
|
||||
pub fn complete_tx<T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
slate: &mut Slate,
|
||||
context: &sigcontext::Context,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 0)?;
|
||||
|
@ -129,14 +132,15 @@ where
|
|||
}
|
||||
|
||||
/// Issue a burn tx
|
||||
pub fn issue_burn_tx<T: ?Sized, K>(
|
||||
pub fn issue_burn_tx<T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
amount: u64,
|
||||
minimum_confirmations: u64,
|
||||
max_outputs: usize,
|
||||
) -> Result<Transaction, Error>
|
||||
where
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// TODO
|
||||
|
@ -144,7 +148,7 @@ where
|
|||
// &Identifier::zero());
|
||||
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);
|
||||
|
||||
|
|
|
@ -33,12 +33,13 @@ use util::secp::pedersen;
|
|||
use util::{self, LOGGER};
|
||||
|
||||
/// 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,
|
||||
show_spent: bool,
|
||||
) -> Result<Vec<OutputData>, Error>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
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
|
||||
/// 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
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let height = wallet.get_chain_height()?;
|
||||
let height = wallet.client().get_chain_height()?;
|
||||
refresh_output_state(wallet, height)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// build a local map of wallet outputs keyed by commit
|
||||
/// and a list of outputs we want to query the node for
|
||||
pub fn map_wallet_outputs<T: ?Sized, K>(
|
||||
pub fn map_wallet_outputs<T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
) -> Result<HashMap<pedersen::Commitment, Identifier>, Error>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let mut wallet_outputs: HashMap<pedersen::Commitment, Identifier> = HashMap::new();
|
||||
|
@ -94,14 +97,15 @@ where
|
|||
}
|
||||
|
||||
/// 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_outputs: &HashMap<pedersen::Commitment, Identifier>,
|
||||
api_outputs: &HashMap<pedersen::Commitment, String>,
|
||||
height: u64,
|
||||
) -> Result<(), libwallet::Error>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
// 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.
|
||||
/// 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
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
debug!(LOGGER, "Refreshing wallet outputs");
|
||||
|
@ -142,15 +147,16 @@ where
|
|||
|
||||
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)?;
|
||||
clean_old_unconfirmed(wallet, height)?;
|
||||
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
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
if height < 500 {
|
||||
|
@ -172,9 +178,10 @@ where
|
|||
|
||||
/// Retrieve summary info about the wallet
|
||||
/// 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
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let current_height = wallet.details().last_confirmed_height;
|
||||
|
@ -213,9 +220,10 @@ where
|
|||
}
|
||||
|
||||
/// 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
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
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
|
||||
/// 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,
|
||||
block_fees: &BlockFees,
|
||||
) -> Result<(Output, TxKernel, BlockFees), Error>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let root_key_id = wallet.keychain().root_key_id();
|
||||
|
|
|
@ -34,14 +34,16 @@ use libwallet::error::{Error, ErrorKind};
|
|||
use util::secp::pedersen;
|
||||
|
||||
/// 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
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
}
|
||||
impl<T, K> WalletInst<K> for T
|
||||
impl<T, C, K> WalletInst<C, K> for T
|
||||
where
|
||||
T: WalletBackend<K> + WalletClient + Send + Sync + 'static,
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
}
|
||||
|
@ -50,8 +52,9 @@ where
|
|||
/// Wallets should implement this backend for their storage. All functions
|
||||
/// here expect that the wallet instance has instantiated itself or stored
|
||||
/// whatever credentials it needs
|
||||
pub trait WalletBackend<K>
|
||||
pub trait WalletBackend<C, K>
|
||||
where
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// Initialize with whatever stored credentials we have
|
||||
|
@ -63,6 +66,9 @@ where
|
|||
/// Return the keychain being used
|
||||
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
|
||||
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
|
||||
/// should care about communication details
|
||||
pub trait WalletClient {
|
||||
pub trait WalletClient: Sync + Send + Clone {
|
||||
/// Return the URL of the check node
|
||||
fn node_url(&self) -> &str;
|
||||
|
||||
/// 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
|
||||
/// TODO: Probably need a slate wrapper type
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
// limitations under the License.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::{fs, path};
|
||||
|
||||
|
@ -22,12 +21,9 @@ use failure::ResultExt;
|
|||
use keychain::{Identifier, Keychain};
|
||||
use store::{self, option_to_not_found, to_key};
|
||||
|
||||
use client;
|
||||
use libtx::slate::Slate;
|
||||
use libwallet::types::*;
|
||||
use libwallet::{internal, Error, ErrorKind};
|
||||
use types::{WalletConfig, WalletSeed};
|
||||
use util::secp::pedersen;
|
||||
|
||||
pub const DB_DIR: &'static str = "wallet_data";
|
||||
|
||||
|
@ -47,18 +43,19 @@ pub fn wallet_db_exists(config: WalletConfig) -> bool {
|
|||
db_path.exists()
|
||||
}
|
||||
|
||||
pub struct LMDBBackend<K> {
|
||||
pub struct LMDBBackend<C, K> {
|
||||
db: store::Store,
|
||||
config: WalletConfig,
|
||||
/// passphrase: TODO better ways of dealing with this other than storing
|
||||
passphrase: String,
|
||||
|
||||
/// Keychain
|
||||
keychain: Option<K>,
|
||||
/// client
|
||||
client: C,
|
||||
}
|
||||
|
||||
impl<K> LMDBBackend<K> {
|
||||
pub fn new(config: WalletConfig, passphrase: &str) -> Result<Self, Error> {
|
||||
impl<C, K> LMDBBackend<C, K> {
|
||||
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);
|
||||
fs::create_dir_all(&db_path).expect("Couldn't create wallet backend directory!");
|
||||
|
||||
|
@ -69,6 +66,7 @@ impl<K> LMDBBackend<K> {
|
|||
config: config.clone(),
|
||||
passphrase: String::from(passphrase),
|
||||
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
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// Initialise with whatever stored credentials we have
|
||||
|
@ -106,6 +105,11 @@ where
|
|||
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> {
|
||||
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())
|
||||
|
@ -159,13 +163,21 @@ where
|
|||
|
||||
/// An atomic batch in which all changes can be committed all at once or
|
||||
/// discarded on error.
|
||||
pub struct Batch<'a, K: 'a> {
|
||||
store: &'a LMDBBackend<K>,
|
||||
pub struct Batch<'a, C: 'a, K: 'a>
|
||||
where
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
store: &'a LMDBBackend<C, K>,
|
||||
db: RefCell<Option<store::Batch<'a>>>,
|
||||
}
|
||||
|
||||
#[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> {
|
||||
let key = to_key(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec());
|
||||
self.db.borrow().as_ref().unwrap().put_ser(&key, &out)?;
|
||||
|
@ -205,65 +217,3 @@ impl<'a, K> WalletOutputBatch for Batch<'a, K> {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,11 +28,11 @@ use core::core::hash::Hashed;
|
|||
use core::core::{Output, OutputFeatures, OutputIdentifier, Transaction, TxKernel};
|
||||
use core::{consensus, global, pow};
|
||||
use keychain::ExtKeychain;
|
||||
use wallet::WalletConfig;
|
||||
use wallet::{HTTPWalletClient, WalletConfig};
|
||||
use wallet::file_wallet::FileWallet;
|
||||
use wallet::libwallet::internal::updater;
|
||||
use wallet::libwallet::types::{BlockFees, BlockIdentifier, OutputStatus,
|
||||
WalletBackend};
|
||||
WalletBackend, WalletClient};
|
||||
use wallet::libwallet::{Error, ErrorKind};
|
||||
|
||||
use util;
|
||||
|
@ -40,9 +40,10 @@ use util::secp::pedersen;
|
|||
|
||||
/// Mostly for testing, refreshes output state against a local chain instance
|
||||
/// 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
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: keychain::Keychain,
|
||||
{
|
||||
let wallet_outputs = updater::map_wallet_outputs(wallet)?;
|
||||
|
@ -71,12 +72,13 @@ where
|
|||
/// (0:total, 1:amount_awaiting_confirmation, 2:confirmed but locked,
|
||||
/// 3:currently_spendable, 4:locked total) TODO: Should be a wallet lib
|
||||
/// function with nicer return values
|
||||
pub fn get_wallet_balances<T, K>(
|
||||
pub fn get_wallet_balances<T, C, K>(
|
||||
wallet: &mut T,
|
||||
height: u64,
|
||||
) -> Result<(u64, u64, u64, u64, u64), Error>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: keychain::Keychain,
|
||||
{
|
||||
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
|
||||
/// the block and adds it to the chain, with option transactions included.
|
||||
/// Helpful for building up precise wallet balances for testing.
|
||||
pub fn award_block_to_wallet<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
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: keychain::Keychain,
|
||||
{
|
||||
let prev = chain.head_header().unwrap();
|
||||
|
@ -183,9 +186,10 @@ where
|
|||
}
|
||||
|
||||
/// 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
|
||||
T: WalletBackend<K>,
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: keychain::Keychain,
|
||||
{
|
||||
for _ in 0..num_rewards {
|
||||
|
@ -194,11 +198,11 @@ where
|
|||
}
|
||||
|
||||
/// 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();
|
||||
wallet_config.data_file_dir = String::from(dir);
|
||||
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));
|
||||
wallet.open_with_credentials().unwrap_or_else(|e| {
|
||||
panic!(
|
||||
|
|
|
@ -35,6 +35,7 @@ use chain::types::NoopAdapter;
|
|||
use core::global::ChainTypes;
|
||||
use core::{global, pow};
|
||||
use util::LOGGER;
|
||||
use wallet::HTTPWalletClient;
|
||||
use wallet::libwallet::internal::selection;
|
||||
|
||||
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
|
||||
#[test]
|
||||
fn build_transaction() {
|
||||
let client = HTTPWalletClient::new("");
|
||||
let chain = setup("test_output", "build_transaction_2/.grin");
|
||||
let mut wallet1 = common::create_wallet("test_output/build_transaction_2/wallet1");
|
||||
let mut wallet2 = common::create_wallet("test_output/build_transaction_2/wallet2");
|
||||
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", client);
|
||||
common::award_blocks_to_wallet(&chain, &mut wallet1, 10);
|
||||
// Wallet 1 has 600 Grins, wallet 2 has 0. Create a transaction that sends
|
||||
// 300 Grins from wallet 1 to wallet 2, using libtx
|
||||
|
|
Loading…
Reference in a new issue