From 1df409fa698b55f9495e5bb03517afdec3430e4b Mon Sep 17 00:00:00 2001 From: Antioch Peverell <30642645+antiochp@users.noreply.github.com> Date: Thu, 28 Jun 2018 21:56:07 -0400 Subject: [PATCH] verify coinbase maturity via output_mmr_size (#1203) * we do not need Merkle proofs to spend coinbase outputs we only need the output_mmr_size from the block header * tests working with no Merkle proofs in inputs --- chain/src/store.rs | 2 - chain/src/txhashset.rs | 53 ++++++------ chain/src/types.rs | 3 +- chain/tests/mine_simple_chain.rs | 2 - chain/tests/test_coinbase_maturity.rs | 2 +- chain/tests/test_txhashset_raw_txs.rs | 4 - core/src/core/transaction.rs | 71 ++-------------- pool/tests/common/mod.rs | 2 - wallet/src/client.rs | 73 ---------------- wallet/src/file_wallet.rs | 27 +----- wallet/src/libtx/build.rs | 18 +--- wallet/src/libtx/mod.rs | 4 +- wallet/src/libtx/slate.rs | 1 - wallet/src/libwallet/api.rs | 3 +- wallet/src/libwallet/internal/restore.rs | 10 --- wallet/src/libwallet/internal/selection.rs | 21 +---- wallet/src/libwallet/internal/tx.rs | 2 +- wallet/src/libwallet/internal/updater.rs | 96 +++------------------- wallet/src/libwallet/types.rs | 76 ----------------- wallet/src/lmdb_wallet.rs | 24 ------ wallet/tests/common/mod.rs | 10 +-- 21 files changed, 61 insertions(+), 443 deletions(-) diff --git a/chain/src/store.rs b/chain/src/store.rs index 69f49db8b..fed82349c 100644 --- a/chain/src/store.rs +++ b/chain/src/store.rs @@ -39,8 +39,6 @@ const HEADER_HEAD_PREFIX: u8 = 'I' as u8; const SYNC_HEAD_PREFIX: u8 = 's' as u8; const HEADER_HEIGHT_PREFIX: u8 = '8' as u8; const COMMIT_POS_PREFIX: u8 = 'c' as u8; -const BLOCK_MARKER_PREFIX: u8 = 'm' as u8; -const BLOCK_SUMS_PREFIX: u8 = 'M' as u8; const BLOCK_INPUT_BITMAP_PREFIX: u8 = 'B' as u8; /// All chain-related database operations diff --git a/chain/src/txhashset.rs b/chain/src/txhashset.rs index 8c04b901c..4b469df9c 100644 --- a/chain/src/txhashset.rs +++ b/chain/src/txhashset.rs @@ -564,36 +564,39 @@ impl<'a> Extension<'a> { inputs: &Vec, height: u64, ) -> Result<(), Error> { - for input in inputs { - if input.features.contains(OutputFeatures::COINBASE_OUTPUT) { - self.verify_maturity_via_merkle_proof(input, height)?; + // Find the greatest output pos of any coinbase + // outputs we are attempting to spend. + let pos = inputs + .iter() + .filter(|x| x.features.contains(OutputFeatures::COINBASE_OUTPUT)) + .filter_map(|x| self.commit_index.get_output_pos(&x.commitment()).ok()) + .max() + .unwrap_or(0); + + if pos > 0 { + // If we have not yet reached 1,000 blocks then + // we can fail immediately as coinbase cannot be mature. + if height < global::coinbase_maturity() { + return Err(Error::ImmatureCoinbase); + } + + // Find the "cutoff" pos in the output MMR based on the + // header from 1,000 blocks ago. + let cutoff_height = height.checked_sub(global::coinbase_maturity()).unwrap_or(0); + let cutoff_header = self.commit_index.get_header_by_height(cutoff_height)?; + let cutoff_pos = cutoff_header.output_mmr_size; + + + // If any output pos exceeed the cutoff_pos + // we know they have not yet sufficiently matured. + if pos > cutoff_pos { + return Err(Error::ImmatureCoinbase); } } + Ok(()) } - fn verify_maturity_via_merkle_proof(&self, input: &Input, height: u64) -> Result<(), Error> { - let header = self.commit_index.get_block_header(&input.block_hash())?; - - // Check that the height indicates it has matured sufficiently - // we will check the Merkle proof below to ensure we are being - // honest about the height - if header.height + global::coinbase_maturity() >= height { - return Err(Error::ImmatureCoinbase); - } - - // We need the MMR pos to verify the Merkle proof - let pos = self.get_output_pos(&input.commitment())?; - - let out_id = OutputIdentifier::from_input(input); - let res = input - .merkle_proof() - .verify(header.output_root, &out_id, pos) - .map_err(|_| Error::MerkleProof)?; - - Ok(res) - } - /// Apply a new set of blocks on top the existing sum trees. Blocks are /// applied in order of the provided Vec. If pruning is enabled, inputs also /// prune MMR data. diff --git a/chain/src/types.rs b/chain/src/types.rs index 3b0f4c4cf..0c3a61a93 100644 --- a/chain/src/types.rs +++ b/chain/src/types.rs @@ -18,13 +18,12 @@ use std::{error, fmt, io}; use util::secp; use util::secp::pedersen::Commitment; -use util::secp_static; use core::core::committed; use core::core::hash::{Hash, Hashed}; use core::core::target::Difficulty; use core::core::{block, transaction, Block, BlockHeader}; -use core::ser::{self, Readable, Reader, Writeable, Writer}; +use core::ser; use grin_store as store; use keychain; diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index 13c49a84d..ffe0e10ef 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -263,8 +263,6 @@ fn spend_in_fork_and_compact() { vec![ build::coinbase_input( consensus::REWARD, - block_hash, - merkle_proof, kc.derive_key_id(2).unwrap(), ), build::output(consensus::REWARD - 20000, kc.derive_key_id(30).unwrap()), diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs index 162b8eef4..fb035ff22 100644 --- a/chain/tests/test_coinbase_maturity.rs +++ b/chain/tests/test_coinbase_maturity.rs @@ -106,7 +106,7 @@ fn test_coinbase_maturity() { // this is not a valid tx as the coinbase output cannot be spent yet let coinbase_txn = build::transaction( vec![ - build::coinbase_input(amount, block_hash, merkle_proof.clone(), key_id1.clone()), + build::coinbase_input(amount, key_id1.clone()), build::output(amount - 2, key_id2.clone()), build::with_fee(2), ], diff --git a/chain/tests/test_txhashset_raw_txs.rs b/chain/tests/test_txhashset_raw_txs.rs index 7dbd6caa6..f96306dfa 100644 --- a/chain/tests/test_txhashset_raw_txs.rs +++ b/chain/tests/test_txhashset_raw_txs.rs @@ -81,8 +81,6 @@ fn test_some_raw_txs() { vec![ build::coinbase_input( coinbase_reward, - block.hash(), - MerkleProof::default(), key_id1.clone(), ), build::output(100, key_id2.clone()), @@ -97,8 +95,6 @@ fn test_some_raw_txs() { vec![ build::coinbase_input( coinbase_reward, - block.hash(), - MerkleProof::default(), key_id1.clone(), ), build::output(100, key_id4.clone()), diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index bb06a8093..bddbe8550 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -17,19 +17,17 @@ use std::cmp::Ordering; use std::cmp::max; use std::collections::HashSet; -use std::io::Cursor; use std::{error, fmt}; -use util::secp::pedersen::{Commitment, ProofMessage, RangeProof}; +use util::secp::pedersen::{Commitment, RangeProof}; use util::secp::{self, Message, Signature}; use util::{kernel_sig_msg, static_secp_instance}; use consensus::{self, VerifySortOrder}; -use core::hash::{Hash, Hashed, ZERO_HASH}; -use core::merkle_proof::MerkleProof; +use core::hash::Hashed; use core::{committed, Committed}; use keychain::{self, BlindingFactor}; -use ser::{self, read_and_verify_sorted, ser_vec, PMMRable, Readable, Reader, Writeable, +use ser::{self, read_and_verify_sorted, PMMRable, Readable, Reader, Writeable, WriteableSorted, Writer}; use util; @@ -441,22 +439,13 @@ impl Transaction { Transaction::weight( self.inputs.len(), self.outputs.len(), - self.input_proofs_count(), ) } - /// Collect input's Merkle proofs - pub fn input_proofs_count(&self) -> usize { - self.inputs - .iter() - .filter(|i| i.merkle_proof.is_some()) - .count() - } - /// Calculate transaction weight from transaction details - pub fn weight(input_len: usize, output_len: usize, proof_len: usize) -> u32 { + pub fn weight(input_len: usize, output_len: usize) -> u32 { let mut tx_weight = - -1 * (input_len as i32) + (4 * output_len as i32) + (proof_len as i32) + 1; + -1 * (input_len as i32) + (4 * output_len as i32) + 1; if tx_weight < 1 { tx_weight = 1; } @@ -636,13 +625,6 @@ pub struct Input { pub features: OutputFeatures, /// The commit referencing the output being spent. pub commit: Commitment, - /// The hash of the block the output originated from. - /// Currently we only care about this for coinbase outputs. - pub block_hash: Option, - /// The Merkle Proof that shows the output being spent by this input - /// existed and was unspent at the time of this block (proof of inclusion - /// in output_root) - pub merkle_proof: Option, } hashable_ord!(Input); @@ -663,17 +645,6 @@ impl Writeable for Input { fn write(&self, writer: &mut W) -> Result<(), ser::Error> { writer.write_u8(self.features.bits())?; self.commit.write(writer)?; - - if writer.serialization_mode() != ser::SerializationMode::Hash { - if self.features.contains(OutputFeatures::COINBASE_OUTPUT) { - let block_hash = &self.block_hash.unwrap_or(ZERO_HASH); - let merkle_proof = self.merkle_proof(); - - writer.write_fixed_bytes(block_hash)?; - merkle_proof.write(writer)?; - } - } - Ok(()) } } @@ -687,13 +658,7 @@ impl Readable for Input { let commit = Commitment::read(reader)?; - if features.contains(OutputFeatures::COINBASE_OUTPUT) { - let block_hash = Some(Hash::read(reader)?); - let merkle_proof = Some(MerkleProof::read(reader)?); - Ok(Input::new(features, commit, block_hash, merkle_proof)) - } else { - Ok(Input::new(features, commit, None, None)) - } + Ok(Input::new(features, commit)) } } @@ -707,14 +672,10 @@ impl Input { pub fn new( features: OutputFeatures, commit: Commitment, - block_hash: Option, - merkle_proof: Option, ) -> Input { Input { features, commit, - block_hash, - merkle_proof, } } @@ -725,21 +686,6 @@ impl Input { pub fn commitment(&self) -> Commitment { self.commit } - - /// Convenience function to return the (optional) block_hash for this input. - /// Will return the default hash if we do not have one. - pub fn block_hash(&self) -> Hash { - self.block_hash.unwrap_or_else(Hash::default) - } - - /// Convenience function to return the (optional) merkle_proof for this - /// input. Will return the "empty" Merkle proof if we do not have one. - /// We currently only care about the Merkle proof for inputs spending - /// coinbase outputs. - pub fn merkle_proof(&self) -> MerkleProof { - let merkle_proof = self.merkle_proof.clone(); - merkle_proof.unwrap_or_else(MerkleProof::empty) - } } bitflags! { @@ -920,6 +866,7 @@ impl Readable for OutputIdentifier { #[cfg(test)] mod test { use super::*; + use core::hash::Hash; use core::id::{ShortId, ShortIdentifiable}; use keychain::{ExtKeychain, Keychain}; use util::secp; @@ -991,8 +938,6 @@ mod test { let input = Input { features: OutputFeatures::DEFAULT_OUTPUT, commit: commit, - block_hash: None, - merkle_proof: None, }; let block_hash = Hash::from_hex( @@ -1009,8 +954,6 @@ mod test { let input = Input { features: OutputFeatures::COINBASE_OUTPUT, commit: commit, - block_hash: None, - merkle_proof: None, }; let short_id = input.short_id(&block_hash, nonce); diff --git a/pool/tests/common/mod.rs b/pool/tests/common/mod.rs index 4d06ebf60..c65fe5d12 100644 --- a/pool/tests/common/mod.rs +++ b/pool/tests/common/mod.rs @@ -126,8 +126,6 @@ where let key_id = keychain.derive_key_id(header.height as u32).unwrap(); tx_elements.push(libtx::build::coinbase_input( coinbase_reward, - header.hash(), - MerkleProof::default(), key_id, )); } diff --git a/wallet/src/client.rs b/wallet/src/client.rs index 782882c43..152b27fa1 100644 --- a/wallet/src/client.rs +++ b/wallet/src/client.rs @@ -207,76 +207,3 @@ pub fn get_outputs_by_pmmr_index( } } } - -/// Get any missing block hashes from node -pub fn get_missing_block_hashes_from_node( - addr: &str, - height: u64, - wallet_outputs: Vec, -) -> Result< - ( - HashMap, - HashMap, - ), - Error, -> { - let id_params: Vec = wallet_outputs - .iter() - .map(|commit| format!("id={}", util::to_hex(commit.as_ref().to_vec()))) - .collect(); - - let height_params = [format!("start_height={}&end_height={}", 0, height)]; - let mut api_blocks: HashMap = HashMap::new(); - let mut api_merkle_proofs: HashMap = HashMap::new(); - - // Split up into separate requests, to avoid hitting http limits - for mut query_chunk in id_params.chunks(1000) { - let url = format!( - "{}/v1/chain/outputs/byheight?{}", - addr, - [&height_params, query_chunk].concat().join("&"), - ); - - match api::client::get::>(url.as_str()) { - Ok(blocks) => for block in blocks { - for out in block.outputs { - api_blocks.insert( - out.commit, - ( - block.header.height, - BlockIdentifier::from_hex(&block.header.hash).unwrap(), - ), - ); - if let Some(merkle_proof) = out.merkle_proof { - let wrapper = MerkleProofWrapper(merkle_proof); - api_merkle_proofs.insert(out.commit, wrapper); - } - } - }, - Err(e) => { - // if we got anything other than 200 back from server, bye - return Err(e)?; - } - } - } - Ok((api_blocks, api_merkle_proofs)) -} - -/// Create a merkle proof at the given height for the given commit -pub fn create_merkle_proof(addr: &str, commit: &str) -> Result { - let url = format!("{}/v1/txhashset/merkleproof?id={}", addr, commit); - - match api::client::get::(url.as_str()) { - Ok(output) => Ok(MerkleProofWrapper(output.merkle_proof.unwrap())), - Err(e) => { - // if we got anything other than 200 back from server, bye - error!( - LOGGER, - "get_merkle_proof_for_pos: Restore failed... unable to create merkle proof for commit {}. Error: {}", - commit, - e - ); - Err(e)? - } - } -} diff --git a/wallet/src/file_wallet.rs b/wallet/src/file_wallet.rs index b439f8838..671585a52 100644 --- a/wallet/src/file_wallet.rs +++ b/wallet/src/file_wallet.rs @@ -34,7 +34,7 @@ use client; use libtx::slate::Slate; use libwallet; use libwallet::types::{ - BlockFees, BlockIdentifier, CbData, MerkleProofWrapper, OutputData, TxWrapper, WalletBackend, + BlockFees, BlockIdentifier, CbData, OutputData, TxWrapper, WalletBackend, WalletClient, WalletDetails, WalletOutputBatch, }; use types::{WalletConfig, WalletSeed}; @@ -400,31 +400,6 @@ impl WalletClient for FileWallet { .context(libwallet::ErrorKind::Node)?; Ok(res) } - - /// Get any missing block hashes from node - fn get_missing_block_hashes_from_node( - &self, - height: u64, - wallet_outputs: Vec, - ) -> Result< - ( - HashMap, - HashMap, - ), - libwallet::Error, - > { - let res = - client::get_missing_block_hashes_from_node(self.node_url(), height, wallet_outputs) - .context(libwallet::ErrorKind::Node)?; - Ok(res) - } - - /// retrieve merkle proof for a commit from a node - fn create_merkle_proof(&self, commit: &str) -> Result { - let res = client::create_merkle_proof(self.node_url(), commit) - .context(libwallet::ErrorKind::Node)?; - Ok(res) - } } impl FileWallet diff --git a/wallet/src/libtx/build.rs b/wallet/src/libtx/build.rs index b1ed5ece6..1cdd74a9c 100644 --- a/wallet/src/libtx/build.rs +++ b/wallet/src/libtx/build.rs @@ -28,7 +28,6 @@ use util::{kernel_sig_msg, secp}; use core::core::hash::Hash; -use core::core::merkle_proof::MerkleProof; use core::core::{Input, Output, OutputFeatures, Transaction, TxKernel}; use keychain::{self, BlindSum, BlindingFactor, Identifier, Keychain}; use libtx::{aggsig, proof}; @@ -52,8 +51,6 @@ pub type Append = for<'a> Fn(&'a mut Context, (Transaction, TxKe fn build_input( value: u64, features: OutputFeatures, - block_hash: Option, - merkle_proof: Option, key_id: Identifier, ) -> Box> where @@ -62,7 +59,7 @@ where Box::new( move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) { let commit = build.keychain.commit(value, &key_id).unwrap(); - let input = Input::new(features, commit, block_hash.clone(), merkle_proof.clone()); + let input = Input::new(features, commit); (tx.with_input(input), kern, sum.sub_key_id(key_id.clone())) }, ) @@ -78,15 +75,12 @@ where LOGGER, "Building input (spending regular output): {}, {}", value, key_id ); - build_input(value, OutputFeatures::DEFAULT_OUTPUT, None, None, key_id) + build_input(value, OutputFeatures::DEFAULT_OUTPUT, key_id) } /// Adds a coinbase input spending a coinbase output. -/// We will use the block hash to verify coinbase maturity. pub fn coinbase_input( value: u64, - block_hash: Hash, - merkle_proof: MerkleProof, key_id: Identifier, ) -> Box> where @@ -96,13 +90,7 @@ where LOGGER, "Building input (spending coinbase): {}, {}", value, key_id ); - build_input( - value, - OutputFeatures::COINBASE_OUTPUT, - Some(block_hash), - Some(merkle_proof), - key_id, - ) + build_input(value, OutputFeatures::COINBASE_OUTPUT, key_id) } /// Adds an output with the provided value and key identifier from the diff --git a/wallet/src/libtx/mod.rs b/wallet/src/libtx/mod.rs index b1378058d..f29363b5a 100644 --- a/wallet/src/libtx/mod.rs +++ b/wallet/src/libtx/mod.rs @@ -36,11 +36,11 @@ pub use libtx::error::{Error, ErrorKind}; const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN; /// Transaction fee calculation -pub fn tx_fee(input_len: usize, output_len: usize, proof_len: usize, base_fee: Option) -> u64 { +pub fn tx_fee(input_len: usize, output_len: usize, base_fee: Option) -> u64 { let use_base_fee = match base_fee { Some(bf) => bf, None => DEFAULT_BASE_FEE, }; - (Transaction::weight(input_len, output_len, proof_len) as u64) * use_base_fee + (Transaction::weight(input_len, output_len) as u64) * use_base_fee } diff --git a/wallet/src/libtx/slate.rs b/wallet/src/libtx/slate.rs index d9c65f6a4..5ffd62d93 100644 --- a/wallet/src/libtx/slate.rs +++ b/wallet/src/libtx/slate.rs @@ -264,7 +264,6 @@ impl Slate { let fee = tx_fee( self.tx.inputs.len(), self.tx.outputs.len(), - self.tx.input_proofs_count(), None, ); if fee > self.tx.fee() { diff --git a/wallet/src/libwallet/api.rs b/wallet/src/libwallet/api.rs index c7809f6c6..052c01989 100644 --- a/wallet/src/libwallet/api.rs +++ b/wallet/src/libwallet/api.rs @@ -108,7 +108,8 @@ where Err(e) => { error!( LOGGER, - "Communication with receiver failed on SenderInitiation send. Aborting transaction" + "Communication with receiver failed on SenderInitiation send. Aborting transaction {:?}", + e, ); return Err(e)?; } diff --git a/wallet/src/libwallet/internal/restore.rs b/wallet/src/libwallet/internal/restore.rs index 611cd463b..98e079212 100644 --- a/wallet/src/libwallet/internal/restore.rs +++ b/wallet/src/libwallet/internal/restore.rs @@ -38,8 +38,6 @@ struct OutputResult { /// pub is_coinbase: bool, /// - pub merkle_proof: Option, - /// pub blinding: SecretKey, } @@ -75,13 +73,8 @@ where "Output found: {:?}, amount: {:?}", commit, info.value ); - let mut merkle_proof = None; let commit_str = util::to_hex(commit.as_ref().to_vec()); - if *is_coinbase { - merkle_proof = Some(wallet.create_merkle_proof(&commit_str)?); - } - let height = current_chain_height; let lock_height = if *is_coinbase { height + global::coinbase_maturity() @@ -97,7 +90,6 @@ where height: height, lock_height: lock_height, is_coinbase: *is_coinbase, - merkle_proof: merkle_proof, blinding: info.blinding, }); } @@ -219,8 +211,6 @@ where height: output.height, lock_height: output.lock_height, is_coinbase: output.is_coinbase, - block: None, - merkle_proof: output.merkle_proof, }); } else { warn!( diff --git a/wallet/src/libwallet/internal/selection.rs b/wallet/src/libwallet/internal/selection.rs index 30de86a51..89d4a4612 100644 --- a/wallet/src/libwallet/internal/selection.rs +++ b/wallet/src/libwallet/internal/selection.rs @@ -109,8 +109,6 @@ where height: current_height, lock_height: 0, is_coinbase: false, - block: None, - merkle_proof: None, }); } batch.commit()?; @@ -174,8 +172,6 @@ where height: height, lock_height: 0, is_coinbase: false, - block: None, - merkle_proof: None, }); batch.commit()?; Ok(()) @@ -238,7 +234,7 @@ where // sender let mut fee; // First attempt to spend without change - fee = tx_fee(coins.len(), 1, coins_proof_count(&coins), None); + fee = tx_fee(coins.len(), 1, None); let mut total: u64 = coins.iter().map(|c| c.value).sum(); let mut amount_with_fee = amount + fee; @@ -251,7 +247,7 @@ where // Check if we need to use a change address if total > amount_with_fee { - fee = tx_fee(coins.len(), 2, coins_proof_count(&coins), None); + fee = tx_fee(coins.len(), 2, None); amount_with_fee = amount + fee; // Here check if we have enough outputs for the amount including fee otherwise @@ -274,7 +270,7 @@ where max_outputs, selection_strategy_is_use_all, ); - fee = tx_fee(coins.len(), 2, coins_proof_count(&coins), None); + fee = tx_fee(coins.len(), 2, None); total = coins.iter().map(|c| c.value).sum(); amount_with_fee = amount + fee; } @@ -290,11 +286,6 @@ where Ok((parts, coins, change, change_derivation, amount, fee)) } -/// coins proof count -pub fn coins_proof_count(coins: &Vec) -> usize { - coins.iter().filter(|c| c.merkle_proof.is_some()).count() -} - /// Selects inputs and change for a transaction pub fn inputs_and_change( coins: &Vec, @@ -322,14 +313,8 @@ where for coin in coins { let key_id = wallet.keychain().derive_key_id(coin.n_child)?; if coin.is_coinbase { - let block = coin.block.clone(); - let merkle_proof = coin.merkle_proof.clone(); - let merkle_proof = merkle_proof.unwrap().merkle_proof(); - parts.push(build::coinbase_input( coin.value, - block.unwrap().hash(), - merkle_proof, key_id, )); } else { diff --git a/wallet/src/libwallet/internal/tx.rs b/wallet/src/libwallet/internal/tx.rs index 6893341d8..cfca90fbc 100644 --- a/wallet/src/libwallet/internal/tx.rs +++ b/wallet/src/libwallet/internal/tx.rs @@ -162,7 +162,7 @@ where debug!(LOGGER, "selected some coins - {}", coins.len()); - let fee = tx_fee(coins.len(), 2, selection::coins_proof_count(&coins), None); + let fee = tx_fee(coins.len(), 2, None); let (mut parts, _, _) = selection::inputs_and_change(&coins, wallet, amount, fee)?; //TODO: If we end up using this, create change output here diff --git a/wallet/src/libwallet/internal/updater.rs b/wallet/src/libwallet/internal/updater.rs index 5b355721b..19efc6b22 100644 --- a/wallet/src/libwallet/internal/updater.rs +++ b/wallet/src/libwallet/internal/updater.rs @@ -65,55 +65,6 @@ where { let height = wallet.get_chain_height()?; refresh_output_state(wallet, height)?; - refresh_missing_block_hashes(wallet, height)?; - Ok(()) -} - -// TODO - this might be slow if we have really old outputs that have never been -// refreshed -fn refresh_missing_block_hashes(wallet: &mut T, height: u64) -> Result<(), Error> -where - T: WalletBackend + WalletClient, - K: Keychain, -{ - // build a local map of wallet outputs keyed by commit - // and a list of outputs we want to query the node for - let wallet_outputs = map_wallet_outputs_missing_block(wallet)?; - - let wallet_output_keys = wallet_outputs.keys().map(|commit| commit.clone()).collect(); - - // nothing to do so return (otherwise we hit the api with a monster query...) - if wallet_outputs.is_empty() { - return Ok(()); - } - - debug!( - LOGGER, - "Refreshing missing block hashes (and heights) for {} outputs", - wallet_outputs.len(), - ); - - let (api_blocks, api_merkle_proofs) = - wallet.get_missing_block_hashes_from_node(height, wallet_output_keys)?; - - // now for each commit, find the output in the wallet and - // the corresponding api output (if it exists) - // and refresh it in-place in the wallet. - // Note: minimizing the time we spend holding the wallet lock. - let mut batch = wallet.batch()?; - for (commit, id) in wallet_outputs.iter() { - if let Some((height, bid)) = api_blocks.get(&commit) { - if let Ok(mut output) = batch.get(id) { - output.height = *height; - output.block = Some(bid.clone()); - if let Some(merkle_proof) = api_merkle_proofs.get(&commit) { - output.merkle_proof = Some(merkle_proof.clone()); - } - batch.save(output); - } - } - } - batch.commit()?; Ok(()) } @@ -139,29 +90,6 @@ where Ok(wallet_outputs) } -/// As above, but only return unspent outputs with missing block hashes -/// and a list of outputs we want to query the node for -pub fn map_wallet_outputs_missing_block( - wallet: &mut T, -) -> Result, Error> -where - T: WalletBackend, - K: Keychain, -{ - let mut wallet_outputs: HashMap = HashMap::new(); - let keychain = wallet.keychain().clone(); - let unspents = wallet.iter().filter(|x| { - x.root_key_id == keychain.root_key_id() - && x.block.is_none() - && x.status == OutputStatus::Unspent - }); - for out in unspents { - let commit = keychain.commit_with_key_index(out.value, out.n_child)?; - wallet_outputs.insert(commit, out.key_id.clone()); - } - Ok(wallet_outputs) -} - /// Apply refreshed API output data to the wallet pub fn apply_api_outputs( wallet: &mut T, @@ -329,20 +257,16 @@ where { // Now acquire the wallet lock and write the new output. let mut batch = wallet.batch()?; - { - batch.save(OutputData { - root_key_id: root_key_id.clone(), - key_id: key_id.clone(), - n_child: derivation, - value: reward(block_fees.fees), - status: OutputStatus::Unconfirmed, - height: height, - lock_height: lock_height, - is_coinbase: true, - block: None, - merkle_proof: None, - })?; - } + batch.save(OutputData { + root_key_id: root_key_id.clone(), + key_id: key_id.clone(), + n_child: derivation, + value: reward(block_fees.fees), + status: OutputStatus::Unconfirmed, + height: height, + lock_height: lock_height, + is_coinbase: true, + }); batch.commit()?; } diff --git a/wallet/src/libwallet/types.rs b/wallet/src/libwallet/types.rs index 1bdaac243..2197bfe19 100644 --- a/wallet/src/libwallet/types.rs +++ b/wallet/src/libwallet/types.rs @@ -24,7 +24,6 @@ use serde_json; use failure::ResultExt; use core::core::hash::Hash; -use core::core::merkle_proof::MerkleProof; use core::ser; use keychain::{Identifier, Keychain}; @@ -150,22 +149,6 @@ pub trait WalletClient { ), Error, >; - - /// Get any missing block hashes from node - fn get_missing_block_hashes_from_node( - &self, - height: u64, - wallet_outputs: Vec, - ) -> Result< - ( - HashMap, - HashMap, - ), - Error, - >; - - /// create merkle proof for a commit from a node at the current height - fn create_merkle_proof(&self, commit: &str) -> Result; } /// Information about an output that's being tracked by the wallet. Must be @@ -190,10 +173,6 @@ pub struct OutputData { pub lock_height: u64, /// Is this a coinbase output? Is it subject to coinbase locktime? pub is_coinbase: bool, - /// Hash of the block this output originated from. - pub block: Option, - /// Merkle proof - pub merkle_proof: Option, } impl ser::Writeable for OutputData { @@ -239,14 +218,6 @@ impl OutputData { return false; } else if self.status == OutputStatus::Unconfirmed && self.is_coinbase { return false; - } else if self.is_coinbase && self.block.is_none() { - // if we do not have a block hash for coinbase output we cannot spent it - // block index got compacted before we refreshed our wallet? - return false; - } else if self.is_coinbase && self.merkle_proof.is_none() { - // if we do not have a Merkle proof for coinbase output we cannot spent it - // block index got compacted before we refreshed our wallet? - return false; } else if self.lock_height > current_height { return false; } else if self.status == OutputStatus::Unspent @@ -304,53 +275,6 @@ impl fmt::Display for OutputStatus { } } -/// Wrapper for a merkle proof -#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] -pub struct MerkleProofWrapper(pub MerkleProof); - -impl MerkleProofWrapper { - /// Create - pub fn merkle_proof(&self) -> MerkleProof { - self.0.clone() - } -} - -impl serde::ser::Serialize for MerkleProofWrapper { - fn serialize(&self, serializer: S) -> Result - where - S: serde::ser::Serializer, - { - serializer.serialize_str(&self.0.to_hex()) - } -} - -impl<'de> serde::de::Deserialize<'de> for MerkleProofWrapper { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - deserializer.deserialize_str(MerkleProofWrapperVisitor) - } -} - -struct MerkleProofWrapperVisitor; - -impl<'de> serde::de::Visitor<'de> for MerkleProofWrapperVisitor { - type Value = MerkleProofWrapper; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a merkle proof") - } - - fn visit_str(self, s: &str) -> Result - where - E: serde::de::Error, - { - let merkle_proof = MerkleProof::from_hex(s).unwrap(); - Ok(MerkleProofWrapper(merkle_proof)) - } -} - /// Block Identifier #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] pub struct BlockIdentifier(pub Hash); diff --git a/wallet/src/lmdb_wallet.rs b/wallet/src/lmdb_wallet.rs index b24d12f12..0547bccf4 100644 --- a/wallet/src/lmdb_wallet.rs +++ b/wallet/src/lmdb_wallet.rs @@ -251,28 +251,4 @@ impl WalletClient for LMDBBackend { .context(ErrorKind::Node)?; Ok(res) } - - /// Get any missing block hashes from node - fn get_missing_block_hashes_from_node( - &self, - height: u64, - wallet_outputs: Vec, - ) -> Result< - ( - HashMap, - HashMap, - ), - Error, - > { - let res = - client::get_missing_block_hashes_from_node(self.node_url(), height, wallet_outputs) - .context(ErrorKind::Node)?; - Ok(res) - } - - /// retrieve merkle proof for a commit from a node - fn create_merkle_proof(&self, commit: &str) -> Result { - let res = client::create_merkle_proof(self.node_url(), commit).context(ErrorKind::Node)?; - Ok(res) - } } diff --git a/wallet/tests/common/mod.rs b/wallet/tests/common/mod.rs index 325c4347b..d9c8bb144 100644 --- a/wallet/tests/common/mod.rs +++ b/wallet/tests/common/mod.rs @@ -31,7 +31,7 @@ use keychain::ExtKeychain; use wallet::WalletConfig; use wallet::file_wallet::FileWallet; use wallet::libwallet::internal::updater; -use wallet::libwallet::types::{BlockFees, BlockIdentifier, MerkleProofWrapper, OutputStatus, +use wallet::libwallet::types::{BlockFees, BlockIdentifier, OutputStatus, WalletBackend}; use wallet::libwallet::{Error, ErrorKind}; @@ -176,13 +176,7 @@ where } }; add_block_with_reward(chain, txs, coinbase_tx.clone()); - // build merkle proof and block identifier and save in wallet - let output_id = OutputIdentifier::from_output(&coinbase_tx.0.clone()); - let m_proof = chain.get_merkle_proof(&output_id, &chain.head_header().unwrap()); - let block_id = Some(BlockIdentifier(chain.head_header().unwrap().hash())); - let mut output = wallet.get(&fees.key_id.unwrap()).unwrap(); - output.block = block_id; - output.merkle_proof = Some(MerkleProofWrapper(m_proof.unwrap())); + let output = wallet.get(&fees.key_id.unwrap()).unwrap(); let mut batch = wallet.batch().unwrap(); batch.save(output).unwrap(); batch.commit().unwrap();