mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
grin wallet outputs now displays output commitments (#1365)
* commit - wip * rustfmt * save output commitment when saving output in wallet * rustfmt * fixup wallet tests with commitments in the wallet db * rustfmt
This commit is contained in:
parent
d47a3bc225
commit
a10557c756
10 changed files with 160 additions and 52 deletions
|
@ -18,9 +18,15 @@ use libwallet::Error;
|
||||||
use prettytable;
|
use prettytable;
|
||||||
use std::io::prelude::Write;
|
use std::io::prelude::Write;
|
||||||
use term;
|
use term;
|
||||||
|
use util;
|
||||||
|
use util::secp::pedersen;
|
||||||
|
|
||||||
/// Display outputs in a pretty way
|
/// Display outputs in a pretty way
|
||||||
pub fn outputs(cur_height: u64, validated: bool, outputs: Vec<OutputData>) -> Result<(), Error> {
|
pub fn outputs(
|
||||||
|
cur_height: u64,
|
||||||
|
validated: bool,
|
||||||
|
outputs: Vec<(OutputData, pedersen::Commitment)>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let title = format!("Wallet Outputs - Block Height: {}", cur_height);
|
let title = format!("Wallet Outputs - Block Height: {}", cur_height);
|
||||||
println!();
|
println!();
|
||||||
let mut t = term::stdout().unwrap();
|
let mut t = term::stdout().unwrap();
|
||||||
|
@ -31,20 +37,18 @@ pub fn outputs(cur_height: u64, validated: bool, outputs: Vec<OutputData>) -> Re
|
||||||
let mut table = table!();
|
let mut table = table!();
|
||||||
|
|
||||||
table.set_titles(row![
|
table.set_titles(row![
|
||||||
bMG->"Key Id",
|
bMG->"Output Commitment",
|
||||||
bMG->"Child Key Index",
|
|
||||||
bMG->"Block Height",
|
bMG->"Block Height",
|
||||||
bMG->"Locked Until",
|
bMG->"Locked Until",
|
||||||
bMG->"Status",
|
bMG->"Status",
|
||||||
bMG->"Is Coinbase?",
|
bMG->"Coinbase?",
|
||||||
bMG->"Num. of Confirmations",
|
bMG->"# Confirms",
|
||||||
bMG->"Value",
|
bMG->"Value",
|
||||||
bMG->"Transaction"
|
bMG->"Tx"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
for out in outputs {
|
for (out, commit) in outputs {
|
||||||
let key_id = format!("{}", out.key_id);
|
let commit = format!("{}", util::to_hex(commit.as_ref().to_vec()));
|
||||||
let n_child = format!("{}", out.n_child);
|
|
||||||
let height = format!("{}", out.height);
|
let height = format!("{}", out.height);
|
||||||
let lock_height = format!("{}", out.lock_height);
|
let lock_height = format!("{}", out.lock_height);
|
||||||
let status = format!("{:?}", out.status);
|
let status = format!("{:?}", out.status);
|
||||||
|
@ -52,12 +56,11 @@ pub fn outputs(cur_height: u64, validated: bool, outputs: Vec<OutputData>) -> Re
|
||||||
let num_confirmations = format!("{}", out.num_confirmations(cur_height));
|
let num_confirmations = format!("{}", out.num_confirmations(cur_height));
|
||||||
let value = format!("{}", core::amount_to_hr_string(out.value));
|
let value = format!("{}", core::amount_to_hr_string(out.value));
|
||||||
let tx = match out.tx_log_entry {
|
let tx = match out.tx_log_entry {
|
||||||
None => "None".to_owned(),
|
None => "".to_owned(),
|
||||||
Some(t) => t.to_string(),
|
Some(t) => t.to_string(),
|
||||||
};
|
};
|
||||||
table.add_row(row![
|
table.add_row(row![
|
||||||
bFC->key_id,
|
bFC->commit,
|
||||||
bFC->n_child,
|
|
||||||
bFB->height,
|
bFB->height,
|
||||||
bFB->lock_height,
|
bFB->lock_height,
|
||||||
bFR->status,
|
bFR->status,
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::marker;
|
||||||
use std::path::{Path, MAIN_SEPARATOR};
|
use std::path::{Path, MAIN_SEPARATOR};
|
||||||
|
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
@ -26,6 +27,7 @@ use uuid::Uuid;
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
|
|
||||||
use keychain::{self, Identifier, Keychain};
|
use keychain::{self, Identifier, Keychain};
|
||||||
|
use util::secp::pedersen;
|
||||||
use util::LOGGER;
|
use util::LOGGER;
|
||||||
|
|
||||||
use error::{Error, ErrorKind};
|
use error::{Error, ErrorKind};
|
||||||
|
@ -45,7 +47,10 @@ const BCK_FILE: &'static str = "wallet.bck";
|
||||||
const LOCK_FILE: &'static str = "wallet.lock";
|
const LOCK_FILE: &'static str = "wallet.lock";
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FileBatch<'a> {
|
struct FileBatch<'a, K: 'a>
|
||||||
|
where
|
||||||
|
K: Keychain,
|
||||||
|
{
|
||||||
/// List of outputs
|
/// List of outputs
|
||||||
outputs: &'a mut HashMap<String, OutputData>,
|
outputs: &'a mut HashMap<String, OutputData>,
|
||||||
/// Wallet Details
|
/// Wallet Details
|
||||||
|
@ -56,9 +61,18 @@ struct FileBatch<'a> {
|
||||||
details_file_path: String,
|
details_file_path: String,
|
||||||
/// lock file path
|
/// lock file path
|
||||||
lock_file_path: String,
|
lock_file_path: String,
|
||||||
|
/// PhantomData for our K: Keychain.
|
||||||
|
_marker: marker::PhantomData<K>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> WalletOutputBatch for FileBatch<'a> {
|
impl<'a, K> WalletOutputBatch<K> for FileBatch<'a, K>
|
||||||
|
where
|
||||||
|
K: Keychain,
|
||||||
|
{
|
||||||
|
fn keychain(&mut self) -> &mut K {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
fn save(&mut self, out: OutputData) -> Result<(), libwallet::Error> {
|
fn save(&mut self, out: OutputData) -> Result<(), libwallet::Error> {
|
||||||
let _ = self.outputs.insert(out.key_id.to_hex(), out);
|
let _ = self.outputs.insert(out.key_id.to_hex(), out);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -134,7 +148,10 @@ impl<'a> WalletOutputBatch for FileBatch<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Drop for FileBatch<'a> {
|
impl<'a, K> Drop for FileBatch<'a, K>
|
||||||
|
where
|
||||||
|
K: Keychain,
|
||||||
|
{
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// delete the lock file
|
// delete the lock file
|
||||||
if let Err(e) = fs::remove_dir(&self.lock_file_path) {
|
if let Err(e) = fs::remove_dir(&self.lock_file_path) {
|
||||||
|
@ -229,7 +246,14 @@ where
|
||||||
.ok_or(libwallet::ErrorKind::Backend("not found".to_string()).into())
|
.ok_or(libwallet::ErrorKind::Backend("not found".to_string()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn batch<'a>(&'a mut self) -> Result<Box<WalletOutputBatch + 'a>, libwallet::Error> {
|
fn get_commitment(
|
||||||
|
&mut self,
|
||||||
|
_id: &Identifier,
|
||||||
|
) -> Result<pedersen::Commitment, libwallet::Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn batch<'a>(&'a mut self) -> Result<Box<WalletOutputBatch<K> + 'a>, libwallet::Error> {
|
||||||
self.lock()?;
|
self.lock()?;
|
||||||
|
|
||||||
// We successfully acquired the lock - so do what needs to be done.
|
// We successfully acquired the lock - so do what needs to be done.
|
||||||
|
@ -244,6 +268,7 @@ where
|
||||||
data_file_path: self.data_file_path.clone(),
|
data_file_path: self.data_file_path.clone(),
|
||||||
details_file_path: self.details_file_path.clone(),
|
details_file_path: self.details_file_path.clone(),
|
||||||
lock_file_path: self.lock_file_path.clone(),
|
lock_file_path: self.lock_file_path.clone(),
|
||||||
|
_marker: marker::PhantomData,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +351,8 @@ where
|
||||||
let mut core = reactor::Core::new().unwrap();
|
let mut core = reactor::Core::new().unwrap();
|
||||||
let retry_strategy = FibonacciBackoff::from_millis(1000).take(10);
|
let retry_strategy = FibonacciBackoff::from_millis(1000).take(10);
|
||||||
let retry_future = Retry::spawn(core.handle(), retry_strategy, action);
|
let retry_future = Retry::spawn(core.handle(), retry_strategy, action);
|
||||||
let retry_result = core.run(retry_future)
|
let retry_result = core
|
||||||
|
.run(retry_future)
|
||||||
.context(libwallet::ErrorKind::CallbackImpl(
|
.context(libwallet::ErrorKind::CallbackImpl(
|
||||||
"Failed to acquire lock file",
|
"Failed to acquire lock file",
|
||||||
));
|
));
|
||||||
|
|
|
@ -27,11 +27,12 @@ use serde_json as json;
|
||||||
use core::ser;
|
use core::ser;
|
||||||
use keychain::Keychain;
|
use keychain::Keychain;
|
||||||
use libtx::slate::Slate;
|
use libtx::slate::Slate;
|
||||||
use libwallet::internal::{tx, updater, selection, sigcontext};
|
use libwallet::internal::{selection, sigcontext, tx, updater};
|
||||||
use libwallet::types::{
|
use libwallet::types::{
|
||||||
BlockFees, CbData, OutputData, TxLogEntry, TxWrapper, WalletBackend, WalletClient, WalletInfo,
|
BlockFees, CbData, OutputData, TxLogEntry, TxWrapper, WalletBackend, WalletClient, WalletInfo,
|
||||||
};
|
};
|
||||||
use libwallet::{Error, ErrorKind};
|
use libwallet::{Error, ErrorKind};
|
||||||
|
use util::secp::pedersen;
|
||||||
use util::{self, LOGGER};
|
use util::{self, LOGGER};
|
||||||
|
|
||||||
/// Wrapper around internal API functions, containing a reference to
|
/// Wrapper around internal API functions, containing a reference to
|
||||||
|
@ -72,7 +73,7 @@ where
|
||||||
include_spent: bool,
|
include_spent: bool,
|
||||||
refresh_from_node: bool,
|
refresh_from_node: bool,
|
||||||
tx_id: Option<u32>,
|
tx_id: Option<u32>,
|
||||||
) -> Result<(bool, Vec<OutputData>), Error> {
|
) -> Result<(bool, Vec<(OutputData, pedersen::Commitment)>), Error> {
|
||||||
let mut w = self.wallet.lock().unwrap();
|
let mut w = self.wallet.lock().unwrap();
|
||||||
w.open_with_credentials()?;
|
w.open_with_credentials()?;
|
||||||
|
|
||||||
|
@ -214,11 +215,7 @@ where
|
||||||
/// A sender provided a transaction file with appropriate public keys and
|
/// A sender provided a transaction file with appropriate public keys and
|
||||||
/// metadata. Complete the receivers' end of it to generate another file
|
/// metadata. Complete the receivers' end of it to generate another file
|
||||||
/// to send back.
|
/// to send back.
|
||||||
pub fn file_receive_tx(
|
pub fn file_receive_tx(&mut self, source: &str) -> Result<(), Error> {
|
||||||
&mut self,
|
|
||||||
source: &str,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
|
|
||||||
let mut pub_tx_f = File::open(source)?;
|
let mut pub_tx_f = File::open(source)?;
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
pub_tx_f.read_to_string(&mut content)?;
|
pub_tx_f.read_to_string(&mut content)?;
|
||||||
|
@ -237,7 +234,7 @@ where
|
||||||
&mut context.sec_key,
|
&mut context.sec_key,
|
||||||
&context.sec_nonce,
|
&context.sec_nonce,
|
||||||
1,
|
1,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// perform partial sig
|
// perform partial sig
|
||||||
let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 1)?;
|
let _ = slate.fill_round_2(wallet.keychain(), &context.sec_key, &context.sec_nonce, 1)?;
|
||||||
|
@ -260,7 +257,6 @@ where
|
||||||
private_tx_file: &str,
|
private_tx_file: &str,
|
||||||
receiver_file: &str,
|
receiver_file: &str,
|
||||||
) -> Result<Slate, Error> {
|
) -> Result<Slate, Error> {
|
||||||
|
|
||||||
let mut pub_tx_f = File::open(receiver_file)?;
|
let mut pub_tx_f = File::open(receiver_file)?;
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
pub_tx_f.read_to_string(&mut content)?;
|
pub_tx_f.read_to_string(&mut content)?;
|
||||||
|
@ -349,7 +345,7 @@ where
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let outputs = self.retrieve_outputs(true, false, None)?;
|
let outputs = self.retrieve_outputs(true, false, None)?;
|
||||||
let height = match outputs.1.iter().map(|out| out.height).max() {
|
let height = match outputs.1.iter().map(|(out, _)| out.height).max() {
|
||||||
Some(height) => height,
|
Some(height) => height,
|
||||||
None => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,6 +37,7 @@ use libwallet::types::{
|
||||||
use libwallet::{Error, ErrorKind};
|
use libwallet::{Error, ErrorKind};
|
||||||
use url::form_urlencoded;
|
use url::form_urlencoded;
|
||||||
|
|
||||||
|
use util::secp::pedersen;
|
||||||
use util::LOGGER;
|
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
|
||||||
|
@ -187,7 +188,7 @@ where
|
||||||
&self,
|
&self,
|
||||||
req: &Request<Body>,
|
req: &Request<Body>,
|
||||||
api: APIOwner<T, C, K>,
|
api: APIOwner<T, C, K>,
|
||||||
) -> Result<(bool, Vec<OutputData>), Error> {
|
) -> Result<(bool, Vec<(OutputData, pedersen::Commitment)>), Error> {
|
||||||
let mut update_from_node = false;
|
let mut update_from_node = false;
|
||||||
let mut id = None;
|
let mut id = None;
|
||||||
let mut show_spent = false;
|
let mut show_spent = false;
|
||||||
|
@ -247,7 +248,8 @@ where
|
||||||
|
|
||||||
fn handle_get_request(&self, req: &Request<Body>) -> Result<Response<Body>, Error> {
|
fn handle_get_request(&self, req: &Request<Body>) -> Result<Response<Body>, Error> {
|
||||||
let api = APIOwner::new(self.wallet.clone());
|
let api = APIOwner::new(self.wallet.clone());
|
||||||
Ok(match req.uri()
|
Ok(match req
|
||||||
|
.uri()
|
||||||
.path()
|
.path()
|
||||||
.trim_right_matches("/")
|
.trim_right_matches("/")
|
||||||
.rsplit("/")
|
.rsplit("/")
|
||||||
|
@ -292,7 +294,8 @@ where
|
||||||
|
|
||||||
fn handle_post_request(&self, req: Request<Body>) -> WalletResponseFuture {
|
fn handle_post_request(&self, req: Request<Body>) -> WalletResponseFuture {
|
||||||
let api = APIOwner::new(self.wallet.clone());
|
let api = APIOwner::new(self.wallet.clone());
|
||||||
match req.uri()
|
match req
|
||||||
|
.uri()
|
||||||
.path()
|
.path()
|
||||||
.trim_right_matches("/")
|
.trim_right_matches("/")
|
||||||
.rsplit("/")
|
.rsplit("/")
|
||||||
|
@ -398,7 +401,8 @@ where
|
||||||
|
|
||||||
fn handle_request(&self, req: Request<Body>) -> WalletResponseFuture {
|
fn handle_request(&self, req: Request<Body>) -> WalletResponseFuture {
|
||||||
let api = *APIForeign::new(self.wallet.clone());
|
let api = *APIForeign::new(self.wallet.clone());
|
||||||
match req.uri()
|
match req
|
||||||
|
.uri()
|
||||||
.path()
|
.path()
|
||||||
.trim_right_matches("/")
|
.trim_right_matches("/")
|
||||||
.rsplit("/")
|
.rsplit("/")
|
||||||
|
|
|
@ -150,7 +150,8 @@ where
|
||||||
return Err(ErrorKind::TransactionNotCancellable(tx_id))?;
|
return Err(ErrorKind::TransactionNotCancellable(tx_id))?;
|
||||||
}
|
}
|
||||||
// get outputs associated with tx
|
// get outputs associated with tx
|
||||||
let outputs = updater::retrieve_outputs(wallet, false, Some(tx_id))?;
|
let res = updater::retrieve_outputs(wallet, false, Some(tx_id))?;
|
||||||
|
let outputs = res.iter().map(|(out, _)| out).cloned().collect();
|
||||||
updater::cancel_tx_and_outputs(wallet, tx, outputs)?;
|
updater::cancel_tx_and_outputs(wallet, tx, outputs)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,13 +38,14 @@ pub fn retrieve_outputs<T: ?Sized, C, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
show_spent: bool,
|
show_spent: bool,
|
||||||
tx_id: Option<u32>,
|
tx_id: Option<u32>,
|
||||||
) -> Result<Vec<OutputData>, Error>
|
) -> Result<Vec<(OutputData, pedersen::Commitment)>, Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K>,
|
T: WalletBackend<C, K>,
|
||||||
C: WalletClient,
|
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();
|
||||||
|
|
||||||
// just read the wallet here, no need for a write lock
|
// just read the wallet here, no need for a write lock
|
||||||
let mut outputs = wallet
|
let mut outputs = wallet
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -57,6 +58,7 @@ where
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// only include outputs with a given tx_id if provided
|
// only include outputs with a given tx_id if provided
|
||||||
if let Some(id) = tx_id {
|
if let Some(id) = tx_id {
|
||||||
outputs = outputs
|
outputs = outputs
|
||||||
|
@ -64,8 +66,17 @@ where
|
||||||
.filter(|out| out.tx_log_entry == Some(id))
|
.filter(|out| out.tx_log_entry == Some(id))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
outputs.sort_by_key(|out| out.n_child);
|
outputs.sort_by_key(|out| out.n_child);
|
||||||
Ok(outputs)
|
|
||||||
|
let res = outputs
|
||||||
|
.into_iter()
|
||||||
|
.map(|out| {
|
||||||
|
let commit = wallet.get_commitment(&out.key_id).unwrap();
|
||||||
|
(out, commit)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve all of the transaction entries, or a particular entry
|
/// Retrieve all of the transaction entries, or a particular entry
|
||||||
|
|
|
@ -47,8 +47,7 @@ where
|
||||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
C: WalletClient,
|
C: WalletClient,
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
/// TODO:
|
/// TODO:
|
||||||
/// Wallets should implement this backend for their storage. All functions
|
/// Wallets should implement this backend for their storage. All functions
|
||||||
|
@ -77,6 +76,9 @@ where
|
||||||
/// Get output data by id
|
/// Get output data by id
|
||||||
fn get(&self, id: &Identifier) -> Result<OutputData, Error>;
|
fn get(&self, id: &Identifier) -> Result<OutputData, Error>;
|
||||||
|
|
||||||
|
/// Get associated output commitment by id.
|
||||||
|
fn get_commitment(&mut self, id: &Identifier) -> Result<pedersen::Commitment, Error>;
|
||||||
|
|
||||||
/// Get an (Optional) tx log entry by uuid
|
/// Get an (Optional) tx log entry by uuid
|
||||||
fn get_tx_log_entry(&self, uuid: &Uuid) -> Result<Option<TxLogEntry>, Error>;
|
fn get_tx_log_entry(&self, uuid: &Uuid) -> Result<Option<TxLogEntry>, Error>;
|
||||||
|
|
||||||
|
@ -84,7 +86,7 @@ where
|
||||||
fn tx_log_iter<'a>(&'a self) -> Box<Iterator<Item = TxLogEntry> + 'a>;
|
fn tx_log_iter<'a>(&'a self) -> Box<Iterator<Item = TxLogEntry> + 'a>;
|
||||||
|
|
||||||
/// Create a new write batch to update or remove output data
|
/// Create a new write batch to update or remove output data
|
||||||
fn batch<'a>(&'a mut self) -> Result<Box<WalletOutputBatch + 'a>, Error>;
|
fn batch<'a>(&'a mut self) -> Result<Box<WalletOutputBatch<K> + 'a>, Error>;
|
||||||
|
|
||||||
/// Next child ID when we want to create a new output
|
/// Next child ID when we want to create a new output
|
||||||
fn next_child<'a>(&mut self, root_key_id: Identifier) -> Result<u32, Error>;
|
fn next_child<'a>(&mut self, root_key_id: Identifier) -> Result<u32, Error>;
|
||||||
|
@ -101,7 +103,13 @@ where
|
||||||
/// commit method can't take ownership.
|
/// commit method can't take ownership.
|
||||||
/// TODO: Should these be split into separate batch objects, for outputs,
|
/// TODO: Should these be split into separate batch objects, for outputs,
|
||||||
/// tx_log entries and meta/details?
|
/// tx_log entries and meta/details?
|
||||||
pub trait WalletOutputBatch {
|
pub trait WalletOutputBatch<K>
|
||||||
|
where
|
||||||
|
K: Keychain,
|
||||||
|
{
|
||||||
|
/// Return the keychain being used
|
||||||
|
fn keychain(&mut self) -> &mut K;
|
||||||
|
|
||||||
/// Add or update data about an output to the backend
|
/// Add or update data about an output to the backend
|
||||||
fn save(&mut self, out: OutputData) -> Result<(), Error>;
|
fn save(&mut self, out: OutputData) -> Result<(), Error>;
|
||||||
|
|
||||||
|
@ -111,7 +119,7 @@ pub trait WalletOutputBatch {
|
||||||
/// Iterate over all output data stored by the backend
|
/// Iterate over all output data stored by the backend
|
||||||
fn iter(&self) -> Box<Iterator<Item = OutputData>>;
|
fn iter(&self) -> Box<Iterator<Item = OutputData>>;
|
||||||
|
|
||||||
/// Delete data about an output to the backend
|
/// Delete data about an output from the backend
|
||||||
fn delete(&mut self, id: &Identifier) -> Result<(), Error>;
|
fn delete(&mut self, id: &Identifier) -> Result<(), Error>;
|
||||||
|
|
||||||
/// save wallet details
|
/// save wallet details
|
||||||
|
|
|
@ -25,9 +25,11 @@ use store::{self, option_to_not_found, to_key, u64_to_key};
|
||||||
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";
|
||||||
|
|
||||||
|
const COMMITMENT_PREFIX: u8 = 'c' as u8;
|
||||||
const OUTPUT_PREFIX: u8 = 'o' as u8;
|
const OUTPUT_PREFIX: u8 = 'o' as u8;
|
||||||
const DERIV_PREFIX: u8 = 'd' as u8;
|
const DERIV_PREFIX: u8 = 'd' as u8;
|
||||||
const CONFIRMED_HEIGHT_PREFIX: u8 = 'c' as u8;
|
const CONFIRMED_HEIGHT_PREFIX: u8 = 'c' as u8;
|
||||||
|
@ -119,6 +121,33 @@ where
|
||||||
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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_commitment(&mut self, id: &Identifier) -> Result<pedersen::Commitment, Error> {
|
||||||
|
let key = to_key(COMMITMENT_PREFIX, &mut id.to_bytes().to_vec());
|
||||||
|
|
||||||
|
let res: Result<pedersen::Commitment, Error> =
|
||||||
|
option_to_not_found(self.db.get_ser(&key), &format!("Key Id: {}", id))
|
||||||
|
.map_err(|e| e.into());
|
||||||
|
|
||||||
|
// "cache hit" and return the commitment
|
||||||
|
if let Ok(commit) = res {
|
||||||
|
Ok(commit)
|
||||||
|
} else {
|
||||||
|
let out = self.get(id)?;
|
||||||
|
|
||||||
|
// Save the output data back to the db
|
||||||
|
// which builds and saves the associated commitment.
|
||||||
|
{
|
||||||
|
let mut batch = self.batch()?;
|
||||||
|
batch.save(out)?;
|
||||||
|
batch.commit()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now retrieve the saved commitment and return it.
|
||||||
|
option_to_not_found(self.db.get_ser(&key), &format!("Key Id: {}", id))
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn iter<'a>(&'a self) -> Box<Iterator<Item = OutputData> + 'a> {
|
fn iter<'a>(&'a self) -> Box<Iterator<Item = OutputData> + 'a> {
|
||||||
Box::new(self.db.iter(&[OUTPUT_PREFIX]).unwrap())
|
Box::new(self.db.iter(&[OUTPUT_PREFIX]).unwrap())
|
||||||
}
|
}
|
||||||
|
@ -132,10 +161,11 @@ where
|
||||||
Box::new(self.db.iter(&[TX_LOG_ENTRY_PREFIX]).unwrap())
|
Box::new(self.db.iter(&[TX_LOG_ENTRY_PREFIX]).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn batch<'a>(&'a mut self) -> Result<Box<WalletOutputBatch + 'a>, Error> {
|
fn batch<'a>(&'a mut self) -> Result<Box<WalletOutputBatch<K> + 'a>, Error> {
|
||||||
Ok(Box::new(Batch {
|
Ok(Box::new(Batch {
|
||||||
_store: self,
|
_store: self,
|
||||||
db: RefCell::new(Some(self.db.batch()?)),
|
db: RefCell::new(Some(self.db.batch()?)),
|
||||||
|
keychain: self.keychain.clone(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,17 +214,34 @@ where
|
||||||
{
|
{
|
||||||
_store: &'a LMDBBackend<C, K>,
|
_store: &'a LMDBBackend<C, K>,
|
||||||
db: RefCell<Option<store::Batch<'a>>>,
|
db: RefCell<Option<store::Batch<'a>>>,
|
||||||
|
/// Keychain
|
||||||
|
keychain: Option<K>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
impl<'a, C, K> WalletOutputBatch for Batch<'a, C, K>
|
impl<'a, C, K> WalletOutputBatch<K> for Batch<'a, C, K>
|
||||||
where
|
where
|
||||||
C: WalletClient,
|
C: WalletClient,
|
||||||
K: Keychain,
|
K: Keychain,
|
||||||
{
|
{
|
||||||
|
fn keychain(&mut self) -> &mut K {
|
||||||
|
self.keychain.as_mut().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
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());
|
// Save the output data to the db.
|
||||||
self.db.borrow().as_ref().unwrap().put_ser(&key, &out)?;
|
{
|
||||||
|
let key = to_key(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec());
|
||||||
|
self.db.borrow().as_ref().unwrap().put_ser(&key, &out)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the associated output commitment.
|
||||||
|
{
|
||||||
|
let key = to_key(COMMITMENT_PREFIX, &mut out.key_id.to_bytes().to_vec());
|
||||||
|
let commit = self.keychain().commit(out.value, &out.key_id)?;
|
||||||
|
self.db.borrow().as_ref().unwrap().put_ser(&key, &commit)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,8 +265,18 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete(&mut self, id: &Identifier) -> Result<(), Error> {
|
fn delete(&mut self, id: &Identifier) -> Result<(), Error> {
|
||||||
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
|
// Delete the output data.
|
||||||
self.db.borrow().as_ref().unwrap().delete(&key)?;
|
{
|
||||||
|
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
|
||||||
|
let _ = self.db.borrow().as_ref().unwrap().delete(&key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the associated output commitment.
|
||||||
|
{
|
||||||
|
let key = to_key(COMMITMENT_PREFIX, &mut id.to_bytes().to_vec());
|
||||||
|
let _ = self.db.borrow().as_ref().unwrap().delete(&key);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
extern crate chrono;
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
extern crate grin_api as api;
|
extern crate grin_api as api;
|
||||||
extern crate grin_chain as chain;
|
extern crate grin_chain as chain;
|
||||||
|
@ -19,10 +20,9 @@ extern crate grin_core as core;
|
||||||
extern crate grin_keychain as keychain;
|
extern crate grin_keychain as keychain;
|
||||||
extern crate grin_wallet as wallet;
|
extern crate grin_wallet as wallet;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate chrono;
|
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use chain::Chain;
|
use chain::Chain;
|
||||||
use core::core::{OutputFeatures, OutputIdentifier, Transaction};
|
use core::core::{OutputFeatures, OutputIdentifier, Transaction};
|
||||||
|
|
|
@ -21,8 +21,8 @@ extern crate grin_wallet as wallet;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate slog;
|
extern crate slog;
|
||||||
extern crate serde;
|
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
|
extern crate serde;
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
@ -315,7 +315,7 @@ fn tx_rollback(test_dir: &str, backend_type: common::BackendType) -> Result<(),
|
||||||
let mut unconfirmed_count = 0;
|
let mut unconfirmed_count = 0;
|
||||||
// get the tx entry, check outputs are as expected
|
// get the tx entry, check outputs are as expected
|
||||||
let (_, outputs) = api.retrieve_outputs(true, false, Some(tx.unwrap().id))?;
|
let (_, outputs) = api.retrieve_outputs(true, false, Some(tx.unwrap().id))?;
|
||||||
for o in outputs.clone() {
|
for (o, _) in outputs.clone() {
|
||||||
if o.status == OutputStatus::Locked {
|
if o.status == OutputStatus::Locked {
|
||||||
locked_count = locked_count + 1;
|
locked_count = locked_count + 1;
|
||||||
}
|
}
|
||||||
|
@ -339,7 +339,7 @@ fn tx_rollback(test_dir: &str, backend_type: common::BackendType) -> Result<(),
|
||||||
assert!(tx.is_some());
|
assert!(tx.is_some());
|
||||||
// get the tx entry, check outputs are as expected
|
// get the tx entry, check outputs are as expected
|
||||||
let (_, outputs) = api.retrieve_outputs(true, false, Some(tx.unwrap().id))?;
|
let (_, outputs) = api.retrieve_outputs(true, false, Some(tx.unwrap().id))?;
|
||||||
for o in outputs.clone() {
|
for (o, _) in outputs.clone() {
|
||||||
if o.status == OutputStatus::Unconfirmed {
|
if o.status == OutputStatus::Unconfirmed {
|
||||||
unconfirmed_count = unconfirmed_count + 1;
|
unconfirmed_count = unconfirmed_count + 1;
|
||||||
}
|
}
|
||||||
|
@ -362,7 +362,8 @@ fn tx_rollback(test_dir: &str, backend_type: common::BackendType) -> Result<(),
|
||||||
let res = api.cancel_tx(1);
|
let res = api.cancel_tx(1);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
let (_, txs) = api.retrieve_txs(true, None)?;
|
let (_, txs) = api.retrieve_txs(true, None)?;
|
||||||
let tx = txs.iter()
|
let tx = txs
|
||||||
|
.iter()
|
||||||
.find(|t| t.tx_slate_id == Some(slate.id))
|
.find(|t| t.tx_slate_id == Some(slate.id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
api.cancel_tx(tx.id)?;
|
api.cancel_tx(tx.id)?;
|
||||||
|
@ -383,7 +384,8 @@ fn tx_rollback(test_dir: &str, backend_type: common::BackendType) -> Result<(),
|
||||||
// Wallet 2 rolls back
|
// Wallet 2 rolls back
|
||||||
wallet::controller::owner_single_use(wallet2.clone(), |api| {
|
wallet::controller::owner_single_use(wallet2.clone(), |api| {
|
||||||
let (_, txs) = api.retrieve_txs(true, None)?;
|
let (_, txs) = api.retrieve_txs(true, None)?;
|
||||||
let tx = txs.iter()
|
let tx = txs
|
||||||
|
.iter()
|
||||||
.find(|t| t.tx_slate_id == Some(slate.id))
|
.find(|t| t.tx_slate_id == Some(slate.id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
api.cancel_tx(tx.id)?;
|
api.cancel_tx(tx.id)?;
|
||||||
|
|
Loading…
Reference in a new issue