mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
fixes to wallet based on testing, ensure details file is being written properly for file wallet (#1204)
This commit is contained in:
parent
ccf862f76b
commit
5ac61b0bc8
10 changed files with 108 additions and 45 deletions
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!(
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue