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:
Antioch Peverell 2018-08-17 12:15:06 +01:00 committed by GitHub
parent d47a3bc225
commit a10557c756
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 160 additions and 52 deletions

View file

@ -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,

View file

@ -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, K> WalletOutputBatch<K> for FileBatch<'a, K>
where
K: Keychain,
{
fn keychain(&mut self) -> &mut K {
unimplemented!();
} }
impl<'a> WalletOutputBatch for FileBatch<'a> {
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",
)); ));

View 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)?;
@ -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,
}; };

View file

@ -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("/")

View file

@ -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(())
} }

View file

@ -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

View file

@ -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

View file

@ -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> {
// Save the output data to the db.
{
let key = to_key(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec()); let key = to_key(OUTPUT_PREFIX, &mut out.key_id.to_bytes().to_vec());
self.db.borrow().as_ref().unwrap().put_ser(&key, &out)?; self.db.borrow().as_ref().unwrap().put_ser(&key, &out)?;
}
// 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> {
// Delete the output data.
{
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()); let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
self.db.borrow().as_ref().unwrap().delete(&key)?; 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(())
} }

View file

@ -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};

View file

@ -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)?;