fixes to wallet based on testing, ensure details file is being written properly for file wallet (#1204)

This commit is contained in:
Yeastplume 2018-06-27 16:57:40 +01:00 committed by GitHub
parent ccf862f76b
commit 5ac61b0bc8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 108 additions and 45 deletions

View file

@ -739,4 +739,6 @@ fn wallet_command(wallet_args: &ArgMatches, global_config: GlobalConfig) {
} }
}); });
} }
// we need to give log output a chance to catch up before exiting
thread::sleep(Duration::from_millis(100));
} }

View file

@ -14,7 +14,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::{Read, Write}; use std::io::Write;
use std::path::{Path, MAIN_SEPARATOR}; use std::path::{Path, MAIN_SEPARATOR};
use serde_json; use serde_json;
@ -44,14 +44,17 @@ const DET_BCK_FILE: &'static str = "wallet.detbck";
const DAT_FILE: &'static str = "wallet.dat"; const DAT_FILE: &'static str = "wallet.dat";
const BCK_FILE: &'static str = "wallet.bck"; const BCK_FILE: &'static str = "wallet.bck";
const LOCK_FILE: &'static str = "wallet.lock"; const LOCK_FILE: &'static str = "wallet.lock";
const SEED_FILE: &'static str = "wallet.seed";
#[derive(Debug)] #[derive(Debug)]
struct FileBatch<'a> { struct FileBatch<'a> {
/// List of outputs /// List of outputs
outputs: &'a mut HashMap<String, OutputData>, outputs: &'a mut HashMap<String, OutputData>,
/// Wallet Details
details: &'a mut WalletDetails,
/// Data file path /// Data file path
data_file_path: String, data_file_path: String,
/// Details file path
details_file_path: String,
/// lock file path /// lock file path
lock_file_path: String, lock_file_path: String,
} }
@ -62,6 +65,10 @@ impl<'a> WalletOutputBatch for FileBatch<'a> {
Ok(()) Ok(())
} }
fn details(&mut self) -> &mut WalletDetails {
&mut self.details
}
fn get(&self, id: &Identifier) -> Result<OutputData, libwallet::Error> { fn get(&self, id: &Identifier) -> Result<OutputData, libwallet::Error> {
self.outputs self.outputs
.get(&id.to_hex()) .get(&id.to_hex())
@ -69,6 +76,10 @@ impl<'a> WalletOutputBatch for FileBatch<'a> {
.ok_or(libwallet::ErrorKind::Backend("not found".to_string()).into()) .ok_or(libwallet::ErrorKind::Backend("not found".to_string()).into())
} }
fn iter<'b>(&'b self) -> Box<Iterator<Item = OutputData> + 'b> {
Box::new(self.outputs.values().cloned())
}
fn delete(&mut self, id: &Identifier) -> Result<(), libwallet::Error> { fn delete(&mut self, id: &Identifier) -> Result<(), libwallet::Error> {
let _ = self.outputs.remove(&id.to_hex()); let _ = self.outputs.remove(&id.to_hex());
Ok(()) Ok(())
@ -86,17 +97,27 @@ impl<'a> WalletOutputBatch for FileBatch<'a> {
fn commit(&self) -> Result<(), libwallet::Error> { fn commit(&self) -> Result<(), libwallet::Error> {
let mut data_file = File::create(self.data_file_path.clone()) let mut data_file = File::create(self.data_file_path.clone())
.context(libwallet::ErrorKind::CallbackImpl("Could not create"))?; .context(libwallet::ErrorKind::CallbackImpl("Could not create"))?;
let mut details_file = File::create(self.details_file_path.clone())
.context(libwallet::ErrorKind::CallbackImpl("Could not create"))?;
let mut outputs = self.outputs.values().collect::<Vec<_>>(); let mut outputs = self.outputs.values().collect::<Vec<_>>();
outputs.sort(); outputs.sort();
let res_json = serde_json::to_vec_pretty(&outputs).context( let res_json = serde_json::to_vec_pretty(&outputs).context(
libwallet::ErrorKind::CallbackImpl("Error serializing wallet data"), libwallet::ErrorKind::CallbackImpl("Error serializing wallet output data"),
)?;
let details_res_json = serde_json::to_vec_pretty(&self.details).context(
libwallet::ErrorKind::CallbackImpl("Error serializing wallet details data"),
)?; )?;
data_file data_file
.write_all(res_json.as_slice()) .write_all(res_json.as_slice())
.context(libwallet::ErrorKind::CallbackImpl( .context(libwallet::ErrorKind::CallbackImpl(
"Error writing wallet file", "Error writing wallet data file",
)) ))?;
.map_err(|e| e.into()) details_file
.write_all(details_res_json.as_slice())
.context(libwallet::ErrorKind::CallbackImpl(
"Error writing wallet details file",
))?;
Ok(())
} }
} }
@ -189,7 +210,9 @@ where
Ok(Box::new(FileBatch { Ok(Box::new(FileBatch {
outputs: &mut self.outputs, outputs: &mut self.outputs,
details: &mut self.details,
data_file_path: self.data_file_path.clone(), data_file_path: self.data_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(),
})) }))
} }
@ -199,19 +222,23 @@ where
&'a mut self, &'a mut self,
root_key_id: keychain::Identifier, root_key_id: keychain::Identifier,
) -> Result<u32, libwallet::Error> { ) -> Result<u32, libwallet::Error> {
let mut max_n = 0; let mut batch = self.batch()?;
for out in self.outputs.values() { {
if max_n < out.n_child && out.root_key_id == root_key_id { let mut max_n = 0;
max_n = out.n_child; for out in batch.iter() {
if max_n < out.n_child && out.root_key_id == root_key_id {
max_n = out.n_child;
}
}
let details = batch.details();
if details.last_child_index <= max_n {
details.last_child_index = max_n + 1;
} else {
details.last_child_index += 1;
} }
} }
batch.commit()?;
if self.details.last_child_index <= max_n { Ok(batch.details().last_child_index)
self.details.last_child_index = max_n + 1;
} else {
self.details.last_child_index += 1;
}
Ok(self.details.last_child_index)
} }
/// Select spendable coins from the wallet. /// Select spendable coins from the wallet.
@ -304,18 +331,30 @@ impl<K> WalletClient for FileWallet<K> {
Ok(r) => Ok(r), Ok(r) => Ok(r),
Err(e) => { Err(e) => {
let message = format!("{}", e.cause().unwrap()); let message = format!("{}", e.cause().unwrap());
error!(
LOGGER,
"Create Coinbase: Communication error: {},{}",
e.cause().unwrap(),
e.backtrace().unwrap()
);
Err(libwallet::ErrorKind::WalletComms(message))? Err(libwallet::ErrorKind::WalletComms(message))?
} }
} }
} }
/// Send a transaction slate to another listening wallet and return result /// Send a transaction slate to another listening wallet and return result
fn send_tx_slate(&self, slate: &Slate) -> Result<Slate, libwallet::Error> { fn send_tx_slate(&self, addr: &str, slate: &Slate) -> Result<Slate, libwallet::Error> {
let res = client::send_tx_slate(self.node_url(), slate); let res = client::send_tx_slate(addr, slate);
match res { match res {
Ok(r) => Ok(r), Ok(r) => Ok(r),
Err(e) => { Err(e) => {
let message = format!("{}", e.cause().unwrap()); let message = format!("{}", e.cause().unwrap());
error!(
LOGGER,
"Send TX Slate: Communication error: {},{}",
e.cause().unwrap(),
e.backtrace().unwrap()
);
Err(libwallet::ErrorKind::WalletComms(message))? Err(libwallet::ErrorKind::WalletComms(message))?
} }
} }

View file

@ -103,7 +103,7 @@ where
selection_strategy_is_use_all, selection_strategy_is_use_all,
)?; )?;
let mut slate = match self.wallet.send_tx_slate(&slate) { let mut slate = match self.wallet.send_tx_slate(dest, &slate) {
Ok(s) => s, Ok(s) => s,
Err(e) => { Err(e) => {
error!( error!(

View file

@ -31,8 +31,9 @@ use failure::Fail;
use keychain::Keychain; use keychain::Keychain;
use libtx::slate::Slate; use libtx::slate::Slate;
use libwallet::api::{APIForeign, APIOwner}; use libwallet::api::{APIForeign, APIOwner};
use libwallet::types::{BlockFees, CbData, OutputData, SendTXArgs, WalletBackend, WalletClient, use libwallet::types::{
WalletInfo}; BlockFees, CbData, OutputData, SendTXArgs, WalletBackend, WalletClient, WalletInfo,
};
use libwallet::{Error, ErrorKind}; use libwallet::{Error, ErrorKind};
use util::LOGGER; use util::LOGGER;
@ -379,6 +380,7 @@ where
T: WalletBackend<K> + WalletClient, T: WalletBackend<K> + WalletClient,
K: Keychain, K: Keychain,
{ {
/// create a new api handler
pub fn new(wallet: Arc<Mutex<T>>) -> ForeignAPIHandler<T, K> { pub fn new(wallet: Arc<Mutex<T>>) -> ForeignAPIHandler<T, K> {
ForeignAPIHandler { ForeignAPIHandler {
wallet, wallet,

View file

@ -48,8 +48,11 @@ pub enum ErrorKind {
}, },
/// Fee Exceeds amount /// Fee Exceeds amount
#[fail(display = "Fee exceeds amount: sender amount {}, recipient fee {}", sender_amount, #[fail(
recipient_fee)] display = "Fee exceeds amount: sender amount {}, recipient fee {}",
sender_amount,
recipient_fee
)]
FeeExceedsAmount { FeeExceedsAmount {
/// sender amount /// sender amount
sender_amount: u64, sender_amount: u64,

View file

@ -15,7 +15,7 @@
//! Selection of inputs for building transactions //! Selection of inputs for building transactions
use keychain::{Identifier, Keychain}; use keychain::{Identifier, Keychain};
use libtx::{build, tx_fee, slate::Slate}; use libtx::{build, slate::Slate, tx_fee};
use libwallet::error::{Error, ErrorKind}; use libwallet::error::{Error, ErrorKind};
use libwallet::internal::{keys, sigcontext}; use libwallet::internal::{keys, sigcontext};
use libwallet::types::*; use libwallet::types::*;

View file

@ -32,7 +32,7 @@ where
{ {
// create an output using the amount in the slate // create an output using the amount in the slate
let (_, mut context, receiver_create_fn) = let (_, mut context, receiver_create_fn) =
selection::build_recipient_output_with_slate(wallet, slate).unwrap(); selection::build_recipient_output_with_slate(wallet, slate)?;
// fill public keys // fill public keys
let _ = slate.fill_round_1( let _ = slate.fill_round_1(

View file

@ -16,7 +16,6 @@
//! the wallet storage and update them. //! the wallet storage and update them.
use failure::ResultExt; use failure::ResultExt;
use std::collections::hash_map::Entry;
use std::collections::HashMap; use std::collections::HashMap;
use core::consensus::reward; use core::consensus::reward;
@ -185,13 +184,15 @@ where
Some(_) => output.mark_unspent(), Some(_) => output.mark_unspent(),
None => output.mark_spent(), None => output.mark_spent(),
}; };
batch.save(output); batch.save(output)?;
} }
} }
{
let details = batch.details();
details.last_confirmed_height = height;
}
batch.commit()?; batch.commit()?;
} }
let details = wallet.details();
details.last_confirmed_height = height;
Ok(()) Ok(())
} }
@ -328,18 +329,20 @@ where
{ {
// Now acquire the wallet lock and write the new output. // Now acquire the wallet lock and write the new output.
let mut batch = wallet.batch()?; let mut batch = wallet.batch()?;
batch.save(OutputData { {
root_key_id: root_key_id.clone(), batch.save(OutputData {
key_id: key_id.clone(), root_key_id: root_key_id.clone(),
n_child: derivation, key_id: key_id.clone(),
value: reward(block_fees.fees), n_child: derivation,
status: OutputStatus::Unconfirmed, value: reward(block_fees.fees),
height: height, status: OutputStatus::Unconfirmed,
lock_height: lock_height, height: height,
is_coinbase: true, lock_height: lock_height,
block: None, is_coinbase: true,
merkle_proof: None, block: None,
}); merkle_proof: None,
})?;
}
batch.commit()?; batch.commit()?;
} }

View file

@ -88,9 +88,15 @@ pub trait WalletOutputBatch {
/// 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>;
/// Get wallet details
fn details(&mut self) -> &mut WalletDetails;
/// Gets output data by id /// Gets output data by id
fn get(&self, id: &Identifier) -> Result<OutputData, Error>; fn get(&self, id: &Identifier) -> Result<OutputData, Error>;
/// Iterate over all output data in batch
fn iter<'b>(&'b self) -> Box<Iterator<Item = OutputData> + 'b>;
/// Delete data about an output to the backend /// Delete data about an output to the backend
fn delete(&mut self, id: &Identifier) -> Result<(), Error>; fn delete(&mut self, id: &Identifier) -> Result<(), Error>;
@ -112,7 +118,7 @@ pub trait WalletClient {
/// Send a transaction slate to another listening wallet and return result /// Send a transaction slate to another listening wallet and return result
/// TODO: Probably need a slate wrapper type /// TODO: Probably need a slate wrapper type
fn send_tx_slate(&self, slate: &Slate) -> Result<Slate, Error>; fn send_tx_slate(&self, addr: &str, slate: &Slate) -> Result<Slate, Error>;
/// Posts a transaction to a grin node /// Posts a transaction to a grin node
fn post_tx(&self, tx: &TxWrapper, fluff: bool) -> Result<(), Error>; fn post_tx(&self, tx: &TxWrapper, fluff: bool) -> Result<(), Error>;

View file

@ -160,11 +160,19 @@ impl<'a, K> WalletOutputBatch for Batch<'a, K> {
Ok(()) Ok(())
} }
fn details(&mut self) -> &mut WalletDetails {
unimplemented!()
}
fn get(&self, id: &Identifier) -> Result<OutputData, Error> { fn get(&self, id: &Identifier) -> Result<OutputData, Error> {
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec()); let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
option_to_not_found(self.db.borrow().as_ref().unwrap().get_ser(&key)).map_err(|e| e.into()) option_to_not_found(self.db.borrow().as_ref().unwrap().get_ser(&key)).map_err(|e| e.into())
} }
fn iter<'b>(&'b self) -> Box<Iterator<Item = OutputData> + 'b> {
unimplemented!();
}
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()); let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
self.db.borrow().as_ref().unwrap().delete(&key)?; self.db.borrow().as_ref().unwrap().delete(&key)?;
@ -197,8 +205,8 @@ impl<K> WalletClient for LMDBBackend<K> {
} }
/// Send a transaction slate to another listening wallet and return result /// Send a transaction slate to another listening wallet and return result
fn send_tx_slate(&self, slate: &Slate) -> Result<Slate, Error> { fn send_tx_slate(&self, addr: &str, slate: &Slate) -> Result<Slate, Error> {
let res = client::send_tx_slate(self.node_url(), slate) let res = client::send_tx_slate(addr, slate)
.context(ErrorKind::WalletComms(format!("Sending transaction")))?; .context(ErrorKind::WalletComms(format!("Sending transaction")))?;
Ok(res) Ok(res)
} }