Fix duplicate wallet coinbase (#158)

* store wallet output data in hashmap
* cleanup up commented out and debug code
This commit is contained in:
AntiochP
2017-10-06 16:10:30 -04:00
committed by Ignotus Peverell
parent da21388131
commit 4e41365fe8
6 changed files with 109 additions and 84 deletions
+5 -4
View File
@@ -48,11 +48,12 @@ pub fn refresh_outputs(
WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
// check each output that's not spent
for mut out in wallet_data.outputs.iter_mut().filter(|out| {
out.status != OutputStatus::Spent
})
for mut out in wallet_data.outputs
.values_mut()
.filter(|out| {
out.status != OutputStatus::Spent
})
{
// TODO check the pool for unconfirmed
match get_output_from_node(config, keychain, out.value, out.n_child) {
Ok(api_out) => refresh_output(&mut out, api_out, &tip),
+10 -10
View File
@@ -13,7 +13,7 @@
// limitations under the License.
use checker;
use keychain::Keychain;
use keychain::Keychain;
use types::{WalletConfig, WalletData};
pub fn show_info(
@@ -27,18 +27,18 @@ pub fn show_info(
let _ = WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
println!("Outputs - ");
println!("fingerprint, n_child, height, lock_height, status, value");
println!("identifier, height, lock_height, status, value");
println!("----------------------------------");
for out in &mut wallet_data.outputs
.iter()
.filter(|out| out.fingerprint == fingerprint)
{
let pubkey = keychain.derive_pubkey(out.n_child).unwrap();
let mut outputs = wallet_data.outputs
.values()
.filter(|out| out.fingerprint == fingerprint)
.collect::<Vec<_>>();
outputs.sort_by_key(|out| out.n_child);
for out in outputs {
println!(
"{}, {}, {}, {}, {:?}, {}",
pubkey.fingerprint(),
out.n_child,
"{}..., {}, {}, {:?}, {}",
out.identifier.fingerprint(),
out.height,
out.lock_height,
out.status,
+7 -5
View File
@@ -107,7 +107,7 @@ impl ApiEndpoint for WalletReceiver {
"coinbase" => {
match input {
WalletReceiveRequest::Coinbase(cb_fees) => {
debug!("Operation {} with amount {}", op, cb_fees.fees);
debug!("Operation {} with fees {:?}", op, cb_fees);
let (out, kern, derivation) =
receive_coinbase(
&self.config,
@@ -168,7 +168,6 @@ fn receive_coinbase(
fees: u64,
mut derivation: u32,
) -> Result<(Output, TxKernel, u32), Error> {
let fingerprint = keychain.clone().fingerprint();
// operate within a lock on wallet data
@@ -179,8 +178,9 @@ fn receive_coinbase(
let pubkey = keychain.derive_pubkey(derivation)?;
// track the new output and return the stuff needed for reward
wallet_data.append_output(OutputData {
wallet_data.add_output(OutputData {
fingerprint: fingerprint.clone(),
identifier: pubkey.clone(),
n_child: derivation,
value: reward(fees),
status: OutputStatus::Unconfirmed,
@@ -213,7 +213,8 @@ fn receive_transaction(
// TODO - replace with real fee calculation
// TODO - note we are not enforcing this in consensus anywhere yet
let fee_amount = 1;
// Note: consensus rules require this to be an even value so it can be split
let fee_amount = 10;
let out_amount = amount - fee_amount;
let (tx_final, _) = build::transaction(vec![
@@ -227,8 +228,9 @@ fn receive_transaction(
tx_final.validate(&keychain.secp())?;
// track the new output and return the finalized transaction to broadcast
wallet_data.append_output(OutputData {
wallet_data.add_output(OutputData {
fingerprint: fingerprint.clone(),
identifier: pubkey.clone(),
n_child: derivation,
value: out_amount,
status: OutputStatus::Unconfirmed,
+3 -3
View File
@@ -78,12 +78,13 @@ fn build_send_tx(
// derive an additional pubkey for change and build the change output
let change_derivation = wallet_data.next_child(fingerprint.clone());
let change_key = keychain.derive_pubkey(change_derivation)?;
parts.push(build::output(change as u64, change_key));
parts.push(build::output(change as u64, change_key.clone()));
// we got that far, time to start tracking the new output, finalize tx
// and lock the outputs used
wallet_data.append_output(OutputData {
wallet_data.add_output(OutputData {
fingerprint: fingerprint.clone(),
identifier: change_key.clone(),
n_child: change_derivation,
value: change as u64,
status: OutputStatus::Unconfirmed,
@@ -103,7 +104,6 @@ fn build_send_tx(
#[cfg(test)]
mod test {
use core::core::build::{input, output, transaction};
use types::{OutputData, OutputStatus};
use keychain::Keychain;
#[test]
+39 -23
View File
@@ -18,6 +18,7 @@ use std::fs::{self, File, OpenOptions};
use std::io::Write;
use std::path::Path;
use std::path::MAIN_SEPARATOR;
use std::collections::HashMap;
use serde_json;
use secp;
@@ -46,27 +47,39 @@ pub enum Error {
}
impl From<keychain::Error> for Error {
fn from(e: keychain::Error) -> Error { Error::Keychain(e) }
fn from(e: keychain::Error) -> Error {
Error::Keychain(e)
}
}
impl From<secp::Error> for Error {
fn from(e: secp::Error) -> Error { Error::Secp(e) }
fn from(e: secp::Error) -> Error {
Error::Secp(e)
}
}
impl From<transaction::Error> for Error {
fn from(e: transaction::Error) -> Error { Error::Transaction(e) }
fn from(e: transaction::Error) -> Error {
Error::Transaction(e)
}
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Error { Error::Format(e.to_string()) }
fn from(e: serde_json::Error) -> Error {
Error::Format(e.to_string())
}
}
impl From<num::ParseIntError> for Error {
fn from(_: num::ParseIntError) -> Error { Error::Format("Invalid hex".to_string()) }
fn from(_: num::ParseIntError) -> Error {
Error::Format("Invalid hex".to_string())
}
}
impl From<api::Error> for Error {
fn from(e: api::Error) -> Error { Error::Node(e) }
fn from(e: api::Error) -> Error {
Error::Node(e)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -125,6 +138,7 @@ impl fmt::Display for OutputStatus {
pub struct OutputData {
/// Private key fingerprint (in case the wallet tracks multiple)
pub fingerprint: keychain::Fingerprint,
pub identifier: keychain::Identifier,
/// How many derivations down from the root key
pub n_child: u32,
/// Value of the output, necessary to rebuild the commitment
@@ -133,12 +147,13 @@ pub struct OutputData {
pub status: OutputStatus,
/// Height of the output
pub height: u64,
/// Height we are locked until
pub lock_height: u64,
}
impl OutputData {
/// Lock a given output to avoid conflicting use
pub fn lock(&mut self) {
fn lock(&mut self) {
self.status = OutputStatus::Locked;
}
}
@@ -153,7 +168,7 @@ impl OutputData {
/// TODO write locks so files don't get overwritten
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct WalletData {
pub outputs: Vec<OutputData>,
pub outputs: HashMap<String, OutputData>,
}
impl WalletData {
@@ -229,7 +244,7 @@ impl WalletData {
WalletData::read(data_file_path)
} else {
// just create a new instance, it will get written afterward
Ok(WalletData { outputs: vec![] })
Ok(WalletData { outputs: HashMap::new() })
}
}
@@ -248,7 +263,7 @@ impl WalletData {
let mut data_file = File::create(data_file_path).map_err(|e| {
Error::WalletData(format!("Could not create {}: {}", data_file_path, e))
})?;
let res_json = serde_json::to_vec_pretty(self).map_err(|_| {
let res_json = serde_json::to_vec_pretty(self).map_err(|e| {
Error::WalletData(format!("Error serializing wallet data."))
})?;
data_file.write_all(res_json.as_slice()).map_err(|e| {
@@ -256,19 +271,20 @@ impl WalletData {
})
}
/// Append a new output information to the wallet data.
pub fn append_output(&mut self, out: OutputData) {
self.outputs.push(out);
/// Append a new output data to the wallet data.
/// TODO - we should check for overwriting here - only really valid for
/// unconfirmed coinbase
pub fn add_output(&mut self, out: OutputData) {
self.outputs.insert(out.identifier.to_hex(), out.clone());
}
/// Lock an output data.
/// TODO - we should track identifier on these outputs (not just n_child)
pub fn lock_output(&mut self, out: &OutputData) {
if let Some(out_to_lock) =
self.outputs.iter_mut().find(|out_to_lock| {
out_to_lock.n_child == out.n_child && out_to_lock.fingerprint == out.fingerprint &&
out_to_lock.value == out.value
})
{
out_to_lock.lock();
if let Some(out_to_lock) = self.outputs.get_mut(&out.identifier.to_hex()) {
if out_to_lock.value == out.value {
out_to_lock.lock()
}
}
}
@@ -277,14 +293,14 @@ impl WalletData {
pub fn select(
&self,
fingerprint: keychain::Fingerprint,
amount: u64
amount: u64,
) -> (Vec<OutputData>, i64) {
let mut to_spend = vec![];
let mut input_total = 0;
// TODO very naive impl for now - definitely better coin selection
// algos available
for out in &self.outputs {
for out in self.outputs.values() {
if out.status == OutputStatus::Unspent && out.fingerprint == fingerprint {
to_spend.push(out.clone());
input_total += out.value;
@@ -300,7 +316,7 @@ impl WalletData {
/// Next child index when we want to create a new output.
pub fn next_child(&self, fingerprint: keychain::Fingerprint) -> u32 {
let mut max_n = 0;
for out in &self.outputs {
for out in self.outputs.values() {
if max_n < out.n_child && out.fingerprint == fingerprint {
max_n = out.n_child;
}