mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
Store additional wallet detail and WalletInfo cleanup (#1167)
* adding wallet detail file, clean up wallet info output * rustfmt * ensure change outputs aren't written early * rustfmt * travis problems AGAIN * file wallet explicit types
This commit is contained in:
parent
a30ee88236
commit
9e0b3b6862
13 changed files with 177 additions and 106 deletions
|
@ -27,15 +27,16 @@ extern crate grin_wallet as wallet;
|
|||
|
||||
mod framework;
|
||||
|
||||
use std::{thread, time};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use framework::{LocalServerContainer, LocalServerContainerConfig};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{thread, time};
|
||||
|
||||
use util::LOGGER;
|
||||
|
||||
/// Start 1 node mining, 1 non mining node and two wallets.
|
||||
/// Then send a transaction from one wallet to another and propagate it a stem transaction
|
||||
/// but without stem relay and check if the transaction is still broadcasted.
|
||||
/// Then send a transaction from one wallet to another and propagate it a stem
|
||||
/// transaction but without stem relay and check if the transaction is still
|
||||
/// broadcasted.
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dandelion_timeout() {
|
||||
|
@ -155,9 +156,7 @@ fn test_dandelion_timeout() {
|
|||
|
||||
// The transaction should be waiting in the node stempool thus cannot be mined.
|
||||
println!("Recipient wallet info: {:?}", recipient_info);
|
||||
assert!(
|
||||
recipient_info.data_confirmed && recipient_info.amount_awaiting_confirmation == 50000000000
|
||||
);
|
||||
assert!(recipient_info.amount_awaiting_confirmation == 50000000000);
|
||||
|
||||
// Wait for stem timeout
|
||||
thread::sleep(time::Duration::from_millis(35000));
|
||||
|
|
|
@ -303,7 +303,8 @@ impl LocalServerContainer {
|
|||
let mut wallet = FileWallet::new(config.clone(), "")
|
||||
.unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config));
|
||||
wallet.keychain = Some(keychain);
|
||||
wallet::libwallet::internal::updater::retrieve_info(&mut wallet, true).unwrap()
|
||||
let _ = wallet::libwallet::internal::updater::refresh_outputs(&mut wallet);
|
||||
wallet::libwallet::internal::updater::retrieve_info(&mut wallet).unwrap()
|
||||
}
|
||||
|
||||
pub fn send_amount_to(
|
||||
|
|
|
@ -114,7 +114,7 @@ fn basic_wallet_transactions() {
|
|||
1,
|
||||
"not_all",
|
||||
"http://127.0.0.1:20002",
|
||||
true,
|
||||
false,
|
||||
);
|
||||
|
||||
//Wait for a confirmation
|
||||
|
@ -125,9 +125,7 @@ fn basic_wallet_transactions() {
|
|||
|
||||
let recipient_info = LocalServerContainer::get_wallet_info(&recp_wallet_config, &recp_seed);
|
||||
println!("Recipient wallet info: {:?}", recipient_info);
|
||||
assert!(
|
||||
recipient_info.data_confirmed && recipient_info.amount_currently_spendable == 50000000000
|
||||
);
|
||||
assert!(recipient_info.amount_currently_spendable == 50000000000);
|
||||
|
||||
warn!(
|
||||
LOGGER,
|
||||
|
@ -140,7 +138,7 @@ fn basic_wallet_transactions() {
|
|||
1,
|
||||
"not_all",
|
||||
"http://127.0.0.1:20002",
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -151,9 +149,7 @@ fn basic_wallet_transactions() {
|
|||
recipient_info
|
||||
);
|
||||
|
||||
assert!(
|
||||
recipient_info.data_confirmed && recipient_info.amount_currently_spendable == 60000000000
|
||||
);
|
||||
assert!(recipient_info.amount_currently_spendable == 60000000000);
|
||||
//send some cash right back
|
||||
LocalServerContainer::send_amount_to(
|
||||
&recp_wallet_config,
|
||||
|
@ -161,7 +157,7 @@ fn basic_wallet_transactions() {
|
|||
1,
|
||||
"all",
|
||||
"http://127.0.0.1:10002",
|
||||
true,
|
||||
false,
|
||||
);
|
||||
|
||||
thread::sleep(time::Duration::from_millis(5000));
|
||||
|
|
|
@ -681,13 +681,14 @@ fn wallet_command(wallet_args: &ArgMatches, global_config: GlobalConfig) {
|
|||
Ok(())
|
||||
}
|
||||
("info", Some(_)) => {
|
||||
let _res = wallet::display::info(&api.retrieve_summary_info(true)?)
|
||||
.unwrap_or_else(|e| {
|
||||
let (validated, wallet_info) =
|
||||
api.retrieve_summary_info(true).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Error getting wallet info: {:?} Config: {:?}",
|
||||
e, wallet_config
|
||||
)
|
||||
});
|
||||
wallet::display::info(&wallet_info, validated);
|
||||
Ok(())
|
||||
}
|
||||
("outputs", Some(_)) => {
|
||||
|
|
|
@ -74,15 +74,15 @@ pub fn outputs(cur_height: u64, validated: bool, outputs: Vec<OutputData>) -> Re
|
|||
}
|
||||
|
||||
/// Display summary info in a pretty way
|
||||
pub fn info(wallet_info: &WalletInfo) -> Result<(), Error> {
|
||||
pub fn info(wallet_info: &WalletInfo, validated: bool) {
|
||||
println!(
|
||||
"\n____ Wallet Summary Info at {} ({}) ____\n",
|
||||
wallet_info.current_height, wallet_info.data_confirmed_from
|
||||
"\n____ Wallet Summary Info as of {} ____\n",
|
||||
wallet_info.last_confirmed_height
|
||||
);
|
||||
let mut table = table!(
|
||||
[bFG->"Total", FG->amount_to_hr_string(wallet_info.total)],
|
||||
[bFY->"Awaiting Confirmation", FY->amount_to_hr_string(wallet_info.amount_awaiting_confirmation)],
|
||||
[bFY->"Confirmed but Still Locked", FY->amount_to_hr_string(wallet_info.amount_confirmed_but_locked)],
|
||||
[bFY->"Immature Coinbase", FY->amount_to_hr_string(wallet_info.amount_immature)],
|
||||
[bFG->"Currently Spendable", FG->amount_to_hr_string(wallet_info.amount_currently_spendable)],
|
||||
[Fw->"---------", Fw->"---------"],
|
||||
[Fr->"(Locked by previous transaction)", Fr->amount_to_hr_string(wallet_info.amount_locked)]
|
||||
|
@ -90,13 +90,11 @@ pub fn info(wallet_info: &WalletInfo) -> Result<(), Error> {
|
|||
table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
|
||||
table.printstd();
|
||||
println!();
|
||||
|
||||
if !wallet_info.data_confirmed {
|
||||
if !validated {
|
||||
println!(
|
||||
"\nWARNING: Failed to verify wallet contents with grin server. \
|
||||
Above info is maybe not fully updated or invalid! \
|
||||
Check that your `grin server` is OK, or see `wallet help restore`"
|
||||
"\nWARNING: Wallet failed to verify data against a live chain. \
|
||||
The above is from local cache and only valid up to the given height! \
|
||||
(is your `grin server` offline or broken?)"
|
||||
);
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,8 +37,10 @@ use client;
|
|||
use libtx::slate::Slate;
|
||||
use libwallet;
|
||||
use libwallet::types::{BlockFees, BlockIdentifier, CbData, MerkleProofWrapper, OutputData,
|
||||
TxWrapper, WalletBackend, WalletClient};
|
||||
TxWrapper, WalletBackend, WalletClient, WalletDetails};
|
||||
|
||||
const DETAIL_FILE: &'static str = "wallet.det";
|
||||
const DET_BCK_FILE: &'static str = "wallet.detbck";
|
||||
const DAT_FILE: &'static str = "wallet.dat";
|
||||
const BCK_FILE: &'static str = "wallet.bck";
|
||||
const LOCK_FILE: &'static str = "wallet.lock";
|
||||
|
@ -176,12 +178,18 @@ pub struct FileWallet<K> {
|
|||
passphrase: String,
|
||||
/// List of outputs
|
||||
pub outputs: HashMap<String, OutputData>,
|
||||
/// Details
|
||||
pub details: WalletDetails,
|
||||
/// Data file path
|
||||
pub data_file_path: String,
|
||||
/// Backup file path
|
||||
pub backup_file_path: String,
|
||||
/// lock file path
|
||||
pub lock_file_path: String,
|
||||
/// details file path
|
||||
pub details_file_path: String,
|
||||
/// Details backup file path
|
||||
pub details_bak_path: String,
|
||||
}
|
||||
|
||||
impl<K> WalletBackend<K> for FileWallet<K>
|
||||
|
@ -274,10 +282,10 @@ where
|
|||
// We successfully acquired the lock - so do what needs to be done.
|
||||
self.read_or_create_paths()
|
||||
.context(libwallet::ErrorKind::CallbackImpl("Lock Error"))?;
|
||||
self.write(&self.backup_file_path)
|
||||
self.write(&self.backup_file_path, &self.details_bak_path)
|
||||
.context(libwallet::ErrorKind::CallbackImpl("Write Error"))?;
|
||||
let res = f(self);
|
||||
self.write(&self.data_file_path)
|
||||
self.write(&self.data_file_path, &self.details_file_path)
|
||||
.context(libwallet::ErrorKind::CallbackImpl("Write Error"))?;
|
||||
|
||||
// delete the lock file
|
||||
|
@ -318,14 +326,20 @@ where
|
|||
}
|
||||
|
||||
/// Next child index when we want to create a new output.
|
||||
fn next_child(&self, root_key_id: keychain::Identifier) -> u32 {
|
||||
fn next_child(&mut self, root_key_id: keychain::Identifier) -> u32 {
|
||||
let mut max_n = 0;
|
||||
for out in self.outputs.values() {
|
||||
if max_n < out.n_child && out.root_key_id == root_key_id {
|
||||
max_n = out.n_child;
|
||||
}
|
||||
}
|
||||
max_n + 1
|
||||
|
||||
if self.details.last_child_index <= max_n {
|
||||
self.details.last_child_index = max_n + 1;
|
||||
} else {
|
||||
self.details.last_child_index += 1;
|
||||
}
|
||||
self.details.last_child_index
|
||||
}
|
||||
|
||||
/// Select spendable coins from the wallet.
|
||||
|
@ -394,6 +408,11 @@ where
|
|||
eligible.iter().take(max_outputs).cloned().collect()
|
||||
}
|
||||
|
||||
/// Return current metadata
|
||||
fn details(&mut self) -> &mut WalletDetails {
|
||||
&mut self.details
|
||||
}
|
||||
|
||||
/// Restore wallet contents
|
||||
fn restore(&mut self) -> Result<(), libwallet::Error> {
|
||||
libwallet::internal::restore::restore(self).context(libwallet::ErrorKind::Restore)?;
|
||||
|
@ -480,8 +499,8 @@ impl<K> WalletClient for FileWallet<K> {
|
|||
/// retrieve merkle proof for a commit from a node
|
||||
fn get_merkle_proof_for_commit(
|
||||
&self,
|
||||
addr: &str,
|
||||
commit: &str,
|
||||
_addr: &str,
|
||||
_commit: &str,
|
||||
) -> Result<MerkleProofWrapper, libwallet::Error> {
|
||||
Err(libwallet::ErrorKind::GenericError("Not Implemented"))?
|
||||
}
|
||||
|
@ -498,9 +517,12 @@ where
|
|||
config: config.clone(),
|
||||
passphrase: String::from(passphrase),
|
||||
outputs: HashMap::new(),
|
||||
details: WalletDetails::default(),
|
||||
data_file_path: format!("{}{}{}", config.data_file_dir, MAIN_SEPARATOR, DAT_FILE),
|
||||
backup_file_path: format!("{}{}{}", config.data_file_dir, MAIN_SEPARATOR, BCK_FILE),
|
||||
lock_file_path: format!("{}{}{}", config.data_file_dir, MAIN_SEPARATOR, LOCK_FILE),
|
||||
details_file_path: format!("{}{}{}", config.data_file_dir, MAIN_SEPARATOR, DETAIL_FILE),
|
||||
details_bak_path: format!("{}{}{}", config.data_file_dir, MAIN_SEPARATOR, DET_BCK_FILE),
|
||||
};
|
||||
match retval.read_or_create_paths() {
|
||||
Ok(_) => Ok(retval),
|
||||
|
@ -519,9 +541,21 @@ where
|
|||
if Path::new(&self.data_file_path.clone()).exists() {
|
||||
self.read()?;
|
||||
}
|
||||
if Path::new(&self.details_file_path.clone()).exists() {
|
||||
self.details = self.read_details()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read details file from disk
|
||||
fn read_details(&self) -> Result<WalletDetails, Error> {
|
||||
let details_file = File::open(self.details_file_path.clone())
|
||||
.context(ErrorKind::FileWallet(&"Could not open wallet details file"))?;
|
||||
serde_json::from_reader(details_file)
|
||||
.context(ErrorKind::Format)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Read output_data vec from disk.
|
||||
fn read_outputs(&self) -> Result<Vec<OutputData>, Error> {
|
||||
let data_file = File::open(self.data_file_path.clone())
|
||||
|
@ -541,8 +575,8 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Write the wallet data to disk.
|
||||
fn write(&self, data_file_path: &str) -> Result<(), Error> {
|
||||
/// Write the wallet and details data to disk.
|
||||
fn write(&self, data_file_path: &str, details_file_path: &str) -> Result<(), Error> {
|
||||
let mut data_file =
|
||||
File::create(data_file_path).context(ErrorKind::FileWallet(&"Could not create "))?;
|
||||
let mut outputs = self.outputs.values().collect::<Vec<_>>();
|
||||
|
@ -551,7 +585,16 @@ where
|
|||
.context(ErrorKind::FileWallet("Error serializing wallet data"))?;
|
||||
data_file
|
||||
.write_all(res_json.as_slice())
|
||||
.context(ErrorKind::FileWallet(&"Error writing wallet file"))
|
||||
.context(ErrorKind::FileWallet(&"Error writing wallet file"))?;
|
||||
// write details file
|
||||
let mut details_file =
|
||||
File::create(details_file_path).context(ErrorKind::FileWallet(&"Could not create "))?;
|
||||
let res_json = serde_json::to_string_pretty(&self.details).context(ErrorKind::FileWallet(
|
||||
"Error serializing wallet details file",
|
||||
))?;
|
||||
details_file
|
||||
.write_all(res_json.into_bytes().as_slice())
|
||||
.context(ErrorKind::FileWallet(&"Error writing wallet details file"))
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
|
|
|
@ -72,12 +72,16 @@ where
|
|||
}
|
||||
|
||||
/// Retrieve summary info for wallet
|
||||
pub fn retrieve_summary_info(&mut self, refresh_from_node: bool) -> Result<WalletInfo, Error> {
|
||||
pub fn retrieve_summary_info(
|
||||
&mut self,
|
||||
refresh_from_node: bool,
|
||||
) -> Result<(bool, WalletInfo), Error> {
|
||||
let mut validated = false;
|
||||
if refresh_from_node {
|
||||
validated = self.update_outputs();
|
||||
}
|
||||
updater::retrieve_info(self.wallet, validated)
|
||||
let wallet_info = updater::retrieve_info(self.wallet)?;
|
||||
Ok((validated, wallet_info))
|
||||
}
|
||||
|
||||
/// Issues a send transaction and sends to recipient
|
||||
|
|
|
@ -164,7 +164,7 @@ where
|
|||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIOwner<T, K>,
|
||||
) -> Result<WalletInfo, Error> {
|
||||
) -> Result<(bool, WalletInfo), Error> {
|
||||
let mut update_from_node = false;
|
||||
if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
|
||||
if let Some(_) = params.get("refresh") {
|
||||
|
|
|
@ -46,7 +46,7 @@ where
|
|||
T: WalletBackend<K>,
|
||||
K: Keychain,
|
||||
{
|
||||
let (elems, inputs, change_id, amount, fee) = select_send_tx(
|
||||
let (elems, inputs, change, change_derivation, amount, fee) = select_send_tx(
|
||||
wallet,
|
||||
amount,
|
||||
current_height,
|
||||
|
@ -78,8 +78,9 @@ where
|
|||
}
|
||||
|
||||
// Store change output
|
||||
if change_id.is_some() {
|
||||
context.add_output(&change_id.unwrap());
|
||||
if change_derivation.is_some() {
|
||||
let change_id = keychain.derive_key_id(change_derivation.unwrap()).unwrap();
|
||||
context.add_output(&change_id);
|
||||
}
|
||||
|
||||
let lock_inputs = context.get_inputs().clone();
|
||||
|
@ -89,10 +90,28 @@ where
|
|||
// so we avoid accidental double spend attempt.
|
||||
let update_sender_wallet_fn = move |wallet: &mut T| {
|
||||
wallet.with_wallet(|wallet_data| {
|
||||
// Lock the inputs we've selected
|
||||
for id in lock_inputs {
|
||||
let coin = wallet_data.get_output(&id).unwrap().clone();
|
||||
wallet_data.lock_output(&coin);
|
||||
}
|
||||
if let Some(d) = change_derivation {
|
||||
// Add our change output to the wallet
|
||||
let root_key_id = keychain.root_key_id();
|
||||
let change_id = keychain.derive_key_id(change_derivation.unwrap()).unwrap();
|
||||
wallet_data.add_output(OutputData {
|
||||
root_key_id: root_key_id.clone(),
|
||||
key_id: change_id,
|
||||
n_child: d,
|
||||
value: change as u64,
|
||||
status: OutputStatus::Unconfirmed,
|
||||
height: current_height,
|
||||
lock_height: 0,
|
||||
is_coinbase: false,
|
||||
block: None,
|
||||
merkle_proof: None,
|
||||
});
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
|
@ -176,7 +195,8 @@ pub fn select_send_tx<T, K>(
|
|||
(
|
||||
Vec<Box<build::Append<K>>>,
|
||||
Vec<OutputData>,
|
||||
Option<Identifier>,
|
||||
u64, //change
|
||||
Option<u32>, //change derivation
|
||||
u64, // amount
|
||||
u64, // fee
|
||||
),
|
||||
|
@ -264,13 +284,14 @@ where
|
|||
}
|
||||
|
||||
// build transaction skeleton with inputs and change
|
||||
let (mut parts, change_key) = inputs_and_change(&coins, wallet, current_height, amount, fee)?;
|
||||
let (mut parts, change, change_derivation) =
|
||||
inputs_and_change(&coins, wallet, current_height, amount, fee)?;
|
||||
|
||||
// This is more proof of concept than anything but here we set lock_height
|
||||
// on tx being sent (based on current chain height via api).
|
||||
parts.push(build::with_lock_height(lock_height));
|
||||
|
||||
Ok((parts, coins, change_key, amount, fee))
|
||||
Ok((parts, coins, change, change_derivation, amount, fee))
|
||||
}
|
||||
|
||||
/// coins proof count
|
||||
|
@ -285,7 +306,7 @@ pub fn inputs_and_change<T, K>(
|
|||
height: u64,
|
||||
amount: u64,
|
||||
fee: u64,
|
||||
) -> Result<(Vec<Box<build::Append<K>>>, Option<Identifier>), Error>
|
||||
) -> Result<(Vec<Box<build::Append<K>>>, u64, Option<u32>), Error>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
K: Keychain,
|
||||
|
@ -321,34 +342,20 @@ where
|
|||
}
|
||||
}
|
||||
let change_key;
|
||||
let mut change_derivation = None;
|
||||
if change != 0 {
|
||||
// track the output representing our change
|
||||
change_key = wallet.with_wallet(|wallet_data| {
|
||||
let keychain = wallet_data.keychain().clone();
|
||||
let root_key_id = keychain.root_key_id();
|
||||
let change_derivation = wallet_data.next_child(root_key_id.clone());
|
||||
let change_key = keychain.derive_key_id(change_derivation).unwrap();
|
||||
|
||||
wallet_data.add_output(OutputData {
|
||||
root_key_id: root_key_id.clone(),
|
||||
key_id: change_key.clone(),
|
||||
n_child: change_derivation,
|
||||
value: change as u64,
|
||||
status: OutputStatus::Unconfirmed,
|
||||
height: height,
|
||||
lock_height: 0,
|
||||
is_coinbase: false,
|
||||
block: None,
|
||||
merkle_proof: None,
|
||||
});
|
||||
|
||||
let cd = wallet_data.next_child(root_key_id.clone());
|
||||
let change_key = keychain.derive_key_id(cd).unwrap();
|
||||
change_derivation = Some(cd);
|
||||
Some(change_key)
|
||||
})?;
|
||||
|
||||
parts.push(build::output(change, change_key.clone().unwrap()));
|
||||
} else {
|
||||
change_key = None
|
||||
}
|
||||
|
||||
Ok((parts, change_key))
|
||||
Ok((parts, change, change_derivation))
|
||||
}
|
||||
|
|
|
@ -165,7 +165,10 @@ where
|
|||
debug!(LOGGER, "selected some coins - {}", coins.len());
|
||||
|
||||
let fee = tx_fee(coins.len(), 2, selection::coins_proof_count(&coins), None);
|
||||
let (mut parts, _) = selection::inputs_and_change(&coins, wallet, current_height, amount, fee)?;
|
||||
let (mut parts, _, _) =
|
||||
selection::inputs_and_change(&coins, wallet, current_height, amount, fee)?;
|
||||
|
||||
//TODO: If we end up using this, create change output here
|
||||
|
||||
// add burn output and fees
|
||||
parts.push(build::output(amount - fee, Identifier::zero()));
|
||||
|
|
|
@ -24,6 +24,7 @@ use core::core::{Output, TxKernel};
|
|||
use core::{global, ser};
|
||||
use keychain::{Identifier, Keychain};
|
||||
use libtx::reward;
|
||||
use libwallet;
|
||||
use libwallet::error::{Error, ErrorKind};
|
||||
use libwallet::internal::keys;
|
||||
use libwallet::types::{BlockFees, CbData, OutputData, OutputStatus, WalletBackend, WalletClient,
|
||||
|
@ -181,7 +182,8 @@ pub fn apply_api_outputs<T, K>(
|
|||
wallet: &mut T,
|
||||
wallet_outputs: &HashMap<pedersen::Commitment, Identifier>,
|
||||
api_outputs: &HashMap<pedersen::Commitment, String>,
|
||||
) -> Result<(), Error>
|
||||
height: u64,
|
||||
) -> Result<(), libwallet::Error>
|
||||
where
|
||||
T: WalletBackend<K>,
|
||||
K: Keychain,
|
||||
|
@ -199,7 +201,10 @@ where
|
|||
};
|
||||
}
|
||||
}
|
||||
})
|
||||
let details = wallet_data.details();
|
||||
details.last_confirmed_height = height;
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Builds a single api query to retrieve the latest output data from the node.
|
||||
|
@ -218,7 +223,7 @@ where
|
|||
let wallet_output_keys = wallet_outputs.keys().map(|commit| commit.clone()).collect();
|
||||
|
||||
let api_outputs = wallet.get_outputs_from_node(wallet.node_url(), wallet_output_keys)?;
|
||||
apply_api_outputs(wallet, &wallet_outputs, &api_outputs)?;
|
||||
apply_api_outputs(wallet, &wallet_outputs, &api_outputs, height)?;
|
||||
clean_old_unconfirmed(wallet, height)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -241,23 +246,15 @@ where
|
|||
|
||||
/// Retrieve summary info about the wallet
|
||||
/// caller should refresh first if desired
|
||||
pub fn retrieve_info<T, K>(wallet: &mut T, refreshed: bool) -> Result<WalletInfo, Error>
|
||||
pub fn retrieve_info<T, K>(wallet: &mut T) -> Result<WalletInfo, Error>
|
||||
where
|
||||
T: WalletBackend<K> + WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
let height_res = wallet.get_chain_height(&wallet.node_url());
|
||||
|
||||
let ret_val = wallet.read_wallet(|wallet_data| {
|
||||
let (current_height, from) = match height_res {
|
||||
Ok(height) => (height, "from server node"),
|
||||
Err(_) => match wallet_data.outputs().values().map(|out| out.height).max() {
|
||||
Some(height) => (height, "from wallet"),
|
||||
None => (0, "node/wallet unavailable"),
|
||||
},
|
||||
};
|
||||
let current_height = wallet_data.details().last_confirmed_height;
|
||||
let mut unspent_total = 0;
|
||||
let mut unspent_but_locked_total = 0;
|
||||
let mut immature_total = 0;
|
||||
let mut unconfirmed_total = 0;
|
||||
let mut locked_total = 0;
|
||||
for out in wallet_data
|
||||
|
@ -266,11 +263,11 @@ where
|
|||
.values()
|
||||
.filter(|out| out.root_key_id == wallet_data.keychain().root_key_id())
|
||||
{
|
||||
if out.status == OutputStatus::Unspent {
|
||||
if out.status == OutputStatus::Unspent && out.lock_height <= current_height {
|
||||
unspent_total += out.value;
|
||||
if out.lock_height > current_height {
|
||||
unspent_but_locked_total += out.value;
|
||||
}
|
||||
if out.status == OutputStatus::Unspent && out.lock_height > current_height {
|
||||
immature_total += out.value;
|
||||
}
|
||||
if out.status == OutputStatus::Unconfirmed && !out.is_coinbase {
|
||||
unconfirmed_total += out.value;
|
||||
|
@ -281,14 +278,12 @@ where
|
|||
}
|
||||
|
||||
Ok(WalletInfo {
|
||||
current_height: current_height,
|
||||
total: unspent_total + unconfirmed_total,
|
||||
last_confirmed_height: wallet_data.details().last_confirmed_height,
|
||||
total: unspent_total + unconfirmed_total + immature_total,
|
||||
amount_awaiting_confirmation: unconfirmed_total,
|
||||
amount_confirmed_but_locked: unspent_but_locked_total,
|
||||
amount_currently_spendable: unspent_total - unspent_but_locked_total,
|
||||
amount_immature: immature_total,
|
||||
amount_locked: locked_total,
|
||||
data_confirmed: refreshed,
|
||||
data_confirmed_from: String::from(from),
|
||||
amount_currently_spendable: unspent_total,
|
||||
})
|
||||
});
|
||||
ret_val
|
||||
|
|
|
@ -76,7 +76,11 @@ where
|
|||
fn get_output(&self, key_id: &Identifier) -> Option<&OutputData>;
|
||||
|
||||
/// Next child ID when we want to create a new output
|
||||
fn next_child(&self, root_key_id: Identifier) -> u32;
|
||||
/// Should also increment index
|
||||
fn next_child(&mut self, root_key_id: Identifier) -> u32;
|
||||
|
||||
/// Return current details
|
||||
fn details(&mut self) -> &mut WalletDetails;
|
||||
|
||||
/// Select spendable coins from the wallet
|
||||
fn select_coins(
|
||||
|
@ -399,21 +403,40 @@ pub struct CbData {
|
|||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct WalletInfo {
|
||||
/// height from which info was taken
|
||||
pub current_height: u64,
|
||||
pub last_confirmed_height: u64,
|
||||
/// total amount in the wallet
|
||||
pub total: u64,
|
||||
/// amount awaiting confirmation
|
||||
pub amount_awaiting_confirmation: u64,
|
||||
/// confirmed but locked
|
||||
pub amount_confirmed_but_locked: u64,
|
||||
/// coinbases waiting for lock height
|
||||
pub amount_immature: u64,
|
||||
/// amount currently spendable
|
||||
pub amount_currently_spendable: u64,
|
||||
/// amount locked by previous transactions
|
||||
/// amount locked via previous transactions
|
||||
pub amount_locked: u64,
|
||||
/// whether the data was confirmed against a live node
|
||||
pub data_confirmed: bool,
|
||||
/// node confirming the data
|
||||
pub data_confirmed_from: String,
|
||||
}
|
||||
|
||||
/// Separate data for a wallet, containing fields
|
||||
/// that are needed but not necessarily represented
|
||||
/// via simple rows of OutputData
|
||||
/// If a wallet is restored from seed this is obvious
|
||||
/// lost and re-populated as well as possible
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct WalletDetails {
|
||||
/// The last block height at which the wallet data
|
||||
/// was confirmed against a node
|
||||
pub last_confirmed_height: u64,
|
||||
/// The last child index used
|
||||
pub last_child_index: u32,
|
||||
}
|
||||
|
||||
impl Default for WalletDetails {
|
||||
fn default() -> WalletDetails {
|
||||
WalletDetails {
|
||||
last_confirmed_height: 0,
|
||||
last_child_index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dummy wrapper for the hex-encoded serialized transaction.
|
||||
|
|
|
@ -61,7 +61,8 @@ where
|
|||
None => {}
|
||||
}
|
||||
}
|
||||
updater::apply_api_outputs(wallet, &wallet_outputs, &api_outputs)?;
|
||||
let height = chain.head().unwrap().height;
|
||||
updater::apply_api_outputs(wallet, &wallet_outputs, &api_outputs, height)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue