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
This commit is contained in:
Antioch Peverell 2018-06-28 21:56:07 -04:00 committed by GitHub
parent d0f8d325f2
commit 1df409fa69
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 61 additions and 443 deletions

View file

@ -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

View file

@ -564,36 +564,39 @@ impl<'a> Extension<'a> {
inputs: &Vec<Input>,
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.

View file

@ -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;

View file

@ -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()),

View file

@ -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),
],

View file

@ -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()),

View file

@ -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<Hash>,
/// 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<MerkleProof>,
}
hashable_ord!(Input);
@ -663,17 +645,6 @@ impl Writeable for Input {
fn write<W: Writer>(&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<Hash>,
merkle_proof: Option<MerkleProof>,
) -> 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);

View file

@ -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,
));
}

View file

@ -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<pedersen::Commitment>,
) -> Result<
(
HashMap<pedersen::Commitment, (u64, BlockIdentifier)>,
HashMap<pedersen::Commitment, MerkleProofWrapper>,
),
Error,
> {
let id_params: Vec<String> = 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<pedersen::Commitment, (u64, BlockIdentifier)> = HashMap::new();
let mut api_merkle_proofs: HashMap<pedersen::Commitment, MerkleProofWrapper> = 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::<Vec<api::BlockOutputs>>(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<MerkleProofWrapper, Error> {
let url = format!("{}/v1/txhashset/merkleproof?id={}", addr, commit);
match api::client::get::<api::OutputPrintable>(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)?
}
}
}

View file

@ -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<K> WalletClient for FileWallet<K> {
.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<pedersen::Commitment>,
) -> Result<
(
HashMap<pedersen::Commitment, (u64, BlockIdentifier)>,
HashMap<pedersen::Commitment, MerkleProofWrapper>,
),
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<MerkleProofWrapper, libwallet::Error> {
let res = client::create_merkle_proof(self.node_url(), commit)
.context(libwallet::ErrorKind::Node)?;
Ok(res)
}
}
impl<K> FileWallet<K>

View file

@ -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<K: Keychain> = for<'a> Fn(&'a mut Context<K>, (Transaction, TxKe
fn build_input<K>(
value: u64,
features: OutputFeatures,
block_hash: Option<Hash>,
merkle_proof: Option<MerkleProof>,
key_id: Identifier,
) -> Box<Append<K>>
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<K>(
value: u64,
block_hash: Hash,
merkle_proof: MerkleProof,
key_id: Identifier,
) -> Box<Append<K>>
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

View file

@ -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>) -> u64 {
pub fn tx_fee(input_len: usize, output_len: usize, base_fee: Option<u64>) -> 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
}

View file

@ -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() {

View file

@ -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)?;
}

View file

@ -38,8 +38,6 @@ struct OutputResult {
///
pub is_coinbase: bool,
///
pub merkle_proof: Option<MerkleProofWrapper>,
///
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!(

View file

@ -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<OutputData>) -> usize {
coins.iter().filter(|c| c.merkle_proof.is_some()).count()
}
/// Selects inputs and change for a transaction
pub fn inputs_and_change<T, K>(
coins: &Vec<OutputData>,
@ -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 {

View file

@ -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

View file

@ -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<T, K>(wallet: &mut T, height: u64) -> Result<(), Error>
where
T: WalletBackend<K> + 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<T, K>(
wallet: &mut T,
) -> Result<HashMap<pedersen::Commitment, Identifier>, Error>
where
T: WalletBackend<K>,
K: Keychain,
{
let mut wallet_outputs: HashMap<pedersen::Commitment, Identifier> = 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<T, K>(
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()?;
}

View file

@ -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<pedersen::Commitment>,
) -> Result<
(
HashMap<pedersen::Commitment, (u64, BlockIdentifier)>,
HashMap<pedersen::Commitment, MerkleProofWrapper>,
),
Error,
>;
/// create merkle proof for a commit from a node at the current height
fn create_merkle_proof(&self, commit: &str) -> Result<MerkleProofWrapper, Error>;
}
/// 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<BlockIdentifier>,
/// Merkle proof
pub merkle_proof: Option<MerkleProofWrapper>,
}
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str(&self.0.to_hex())
}
}
impl<'de> serde::de::Deserialize<'de> for MerkleProofWrapper {
fn deserialize<D>(deserializer: D) -> Result<MerkleProofWrapper, D::Error>
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<E>(self, s: &str) -> Result<Self::Value, E>
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);

View file

@ -251,28 +251,4 @@ impl<K> WalletClient for LMDBBackend<K> {
.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<pedersen::Commitment>,
) -> Result<
(
HashMap<pedersen::Commitment, (u64, BlockIdentifier)>,
HashMap<pedersen::Commitment, MerkleProofWrapper>,
),
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<MerkleProofWrapper, Error> {
let res = client::create_merkle_proof(self.node_url(), commit).context(ErrorKind::Node)?;
Ok(res)
}
}

View file

@ -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();