mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-08 12:21:09 +03:00
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:
parent
d0f8d325f2
commit
1df409fa69
21 changed files with 61 additions and 443 deletions
|
@ -39,8 +39,6 @@ const HEADER_HEAD_PREFIX: u8 = 'I' as u8;
|
||||||
const SYNC_HEAD_PREFIX: u8 = 's' as u8;
|
const SYNC_HEAD_PREFIX: u8 = 's' as u8;
|
||||||
const HEADER_HEIGHT_PREFIX: u8 = '8' as u8;
|
const HEADER_HEIGHT_PREFIX: u8 = '8' as u8;
|
||||||
const COMMIT_POS_PREFIX: u8 = 'c' 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;
|
const BLOCK_INPUT_BITMAP_PREFIX: u8 = 'B' as u8;
|
||||||
|
|
||||||
/// All chain-related database operations
|
/// All chain-related database operations
|
||||||
|
|
|
@ -564,34 +564,37 @@ impl<'a> Extension<'a> {
|
||||||
inputs: &Vec<Input>,
|
inputs: &Vec<Input>,
|
||||||
height: u64,
|
height: u64,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
for input in inputs {
|
// Find the greatest output pos of any coinbase
|
||||||
if input.features.contains(OutputFeatures::COINBASE_OUTPUT) {
|
// outputs we are attempting to spend.
|
||||||
self.verify_maturity_via_merkle_proof(input, height)?;
|
let pos = inputs
|
||||||
}
|
.iter()
|
||||||
}
|
.filter(|x| x.features.contains(OutputFeatures::COINBASE_OUTPUT))
|
||||||
Ok(())
|
.filter_map(|x| self.commit_index.get_output_pos(&x.commitment()).ok())
|
||||||
}
|
.max()
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
fn verify_maturity_via_merkle_proof(&self, input: &Input, height: u64) -> Result<(), Error> {
|
if pos > 0 {
|
||||||
let header = self.commit_index.get_block_header(&input.block_hash())?;
|
// If we have not yet reached 1,000 blocks then
|
||||||
|
// we can fail immediately as coinbase cannot be mature.
|
||||||
// Check that the height indicates it has matured sufficiently
|
if height < global::coinbase_maturity() {
|
||||||
// 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);
|
return Err(Error::ImmatureCoinbase);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need the MMR pos to verify the Merkle proof
|
// Find the "cutoff" pos in the output MMR based on the
|
||||||
let pos = self.get_output_pos(&input.commitment())?;
|
// 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;
|
||||||
|
|
||||||
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)
|
// 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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a new set of blocks on top the existing sum trees. Blocks are
|
/// Apply a new set of blocks on top the existing sum trees. Blocks are
|
||||||
|
|
|
@ -18,13 +18,12 @@ use std::{error, fmt, io};
|
||||||
|
|
||||||
use util::secp;
|
use util::secp;
|
||||||
use util::secp::pedersen::Commitment;
|
use util::secp::pedersen::Commitment;
|
||||||
use util::secp_static;
|
|
||||||
|
|
||||||
use core::core::committed;
|
use core::core::committed;
|
||||||
use core::core::hash::{Hash, Hashed};
|
use core::core::hash::{Hash, Hashed};
|
||||||
use core::core::target::Difficulty;
|
use core::core::target::Difficulty;
|
||||||
use core::core::{block, transaction, Block, BlockHeader};
|
use core::core::{block, transaction, Block, BlockHeader};
|
||||||
use core::ser::{self, Readable, Reader, Writeable, Writer};
|
use core::ser;
|
||||||
use grin_store as store;
|
use grin_store as store;
|
||||||
use keychain;
|
use keychain;
|
||||||
|
|
||||||
|
|
|
@ -263,8 +263,6 @@ fn spend_in_fork_and_compact() {
|
||||||
vec![
|
vec![
|
||||||
build::coinbase_input(
|
build::coinbase_input(
|
||||||
consensus::REWARD,
|
consensus::REWARD,
|
||||||
block_hash,
|
|
||||||
merkle_proof,
|
|
||||||
kc.derive_key_id(2).unwrap(),
|
kc.derive_key_id(2).unwrap(),
|
||||||
),
|
),
|
||||||
build::output(consensus::REWARD - 20000, kc.derive_key_id(30).unwrap()),
|
build::output(consensus::REWARD - 20000, kc.derive_key_id(30).unwrap()),
|
||||||
|
|
|
@ -106,7 +106,7 @@ fn test_coinbase_maturity() {
|
||||||
// this is not a valid tx as the coinbase output cannot be spent yet
|
// this is not a valid tx as the coinbase output cannot be spent yet
|
||||||
let coinbase_txn = build::transaction(
|
let coinbase_txn = build::transaction(
|
||||||
vec![
|
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::output(amount - 2, key_id2.clone()),
|
||||||
build::with_fee(2),
|
build::with_fee(2),
|
||||||
],
|
],
|
||||||
|
|
|
@ -81,8 +81,6 @@ fn test_some_raw_txs() {
|
||||||
vec![
|
vec![
|
||||||
build::coinbase_input(
|
build::coinbase_input(
|
||||||
coinbase_reward,
|
coinbase_reward,
|
||||||
block.hash(),
|
|
||||||
MerkleProof::default(),
|
|
||||||
key_id1.clone(),
|
key_id1.clone(),
|
||||||
),
|
),
|
||||||
build::output(100, key_id2.clone()),
|
build::output(100, key_id2.clone()),
|
||||||
|
@ -97,8 +95,6 @@ fn test_some_raw_txs() {
|
||||||
vec![
|
vec![
|
||||||
build::coinbase_input(
|
build::coinbase_input(
|
||||||
coinbase_reward,
|
coinbase_reward,
|
||||||
block.hash(),
|
|
||||||
MerkleProof::default(),
|
|
||||||
key_id1.clone(),
|
key_id1.clone(),
|
||||||
),
|
),
|
||||||
build::output(100, key_id4.clone()),
|
build::output(100, key_id4.clone()),
|
||||||
|
|
|
@ -17,19 +17,17 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io::Cursor;
|
|
||||||
use std::{error, fmt};
|
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::secp::{self, Message, Signature};
|
||||||
use util::{kernel_sig_msg, static_secp_instance};
|
use util::{kernel_sig_msg, static_secp_instance};
|
||||||
|
|
||||||
use consensus::{self, VerifySortOrder};
|
use consensus::{self, VerifySortOrder};
|
||||||
use core::hash::{Hash, Hashed, ZERO_HASH};
|
use core::hash::Hashed;
|
||||||
use core::merkle_proof::MerkleProof;
|
|
||||||
use core::{committed, Committed};
|
use core::{committed, Committed};
|
||||||
use keychain::{self, BlindingFactor};
|
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};
|
WriteableSorted, Writer};
|
||||||
use util;
|
use util;
|
||||||
|
|
||||||
|
@ -441,22 +439,13 @@ impl Transaction {
|
||||||
Transaction::weight(
|
Transaction::weight(
|
||||||
self.inputs.len(),
|
self.inputs.len(),
|
||||||
self.outputs.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
|
/// 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 =
|
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 {
|
if tx_weight < 1 {
|
||||||
tx_weight = 1;
|
tx_weight = 1;
|
||||||
}
|
}
|
||||||
|
@ -636,13 +625,6 @@ pub struct Input {
|
||||||
pub features: OutputFeatures,
|
pub features: OutputFeatures,
|
||||||
/// The commit referencing the output being spent.
|
/// The commit referencing the output being spent.
|
||||||
pub commit: Commitment,
|
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);
|
hashable_ord!(Input);
|
||||||
|
@ -663,17 +645,6 @@ impl Writeable for Input {
|
||||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
|
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
|
||||||
writer.write_u8(self.features.bits())?;
|
writer.write_u8(self.features.bits())?;
|
||||||
self.commit.write(writer)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -687,13 +658,7 @@ impl Readable for Input {
|
||||||
|
|
||||||
let commit = Commitment::read(reader)?;
|
let commit = Commitment::read(reader)?;
|
||||||
|
|
||||||
if features.contains(OutputFeatures::COINBASE_OUTPUT) {
|
Ok(Input::new(features, commit))
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -707,14 +672,10 @@ impl Input {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
features: OutputFeatures,
|
features: OutputFeatures,
|
||||||
commit: Commitment,
|
commit: Commitment,
|
||||||
block_hash: Option<Hash>,
|
|
||||||
merkle_proof: Option<MerkleProof>,
|
|
||||||
) -> Input {
|
) -> Input {
|
||||||
Input {
|
Input {
|
||||||
features,
|
features,
|
||||||
commit,
|
commit,
|
||||||
block_hash,
|
|
||||||
merkle_proof,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -725,21 +686,6 @@ impl Input {
|
||||||
pub fn commitment(&self) -> Commitment {
|
pub fn commitment(&self) -> Commitment {
|
||||||
self.commit
|
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! {
|
bitflags! {
|
||||||
|
@ -920,6 +866,7 @@ impl Readable for OutputIdentifier {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use core::hash::Hash;
|
||||||
use core::id::{ShortId, ShortIdentifiable};
|
use core::id::{ShortId, ShortIdentifiable};
|
||||||
use keychain::{ExtKeychain, Keychain};
|
use keychain::{ExtKeychain, Keychain};
|
||||||
use util::secp;
|
use util::secp;
|
||||||
|
@ -991,8 +938,6 @@ mod test {
|
||||||
let input = Input {
|
let input = Input {
|
||||||
features: OutputFeatures::DEFAULT_OUTPUT,
|
features: OutputFeatures::DEFAULT_OUTPUT,
|
||||||
commit: commit,
|
commit: commit,
|
||||||
block_hash: None,
|
|
||||||
merkle_proof: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let block_hash = Hash::from_hex(
|
let block_hash = Hash::from_hex(
|
||||||
|
@ -1009,8 +954,6 @@ mod test {
|
||||||
let input = Input {
|
let input = Input {
|
||||||
features: OutputFeatures::COINBASE_OUTPUT,
|
features: OutputFeatures::COINBASE_OUTPUT,
|
||||||
commit: commit,
|
commit: commit,
|
||||||
block_hash: None,
|
|
||||||
merkle_proof: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let short_id = input.short_id(&block_hash, nonce);
|
let short_id = input.short_id(&block_hash, nonce);
|
||||||
|
|
|
@ -126,8 +126,6 @@ where
|
||||||
let key_id = keychain.derive_key_id(header.height as u32).unwrap();
|
let key_id = keychain.derive_key_id(header.height as u32).unwrap();
|
||||||
tx_elements.push(libtx::build::coinbase_input(
|
tx_elements.push(libtx::build::coinbase_input(
|
||||||
coinbase_reward,
|
coinbase_reward,
|
||||||
header.hash(),
|
|
||||||
MerkleProof::default(),
|
|
||||||
key_id,
|
key_id,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ use client;
|
||||||
use libtx::slate::Slate;
|
use libtx::slate::Slate;
|
||||||
use libwallet;
|
use libwallet;
|
||||||
use libwallet::types::{
|
use libwallet::types::{
|
||||||
BlockFees, BlockIdentifier, CbData, MerkleProofWrapper, OutputData, TxWrapper, WalletBackend,
|
BlockFees, BlockIdentifier, CbData, OutputData, TxWrapper, WalletBackend,
|
||||||
WalletClient, WalletDetails, WalletOutputBatch,
|
WalletClient, WalletDetails, WalletOutputBatch,
|
||||||
};
|
};
|
||||||
use types::{WalletConfig, WalletSeed};
|
use types::{WalletConfig, WalletSeed};
|
||||||
|
@ -400,31 +400,6 @@ impl<K> WalletClient for FileWallet<K> {
|
||||||
.context(libwallet::ErrorKind::Node)?;
|
.context(libwallet::ErrorKind::Node)?;
|
||||||
Ok(res)
|
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>
|
impl<K> FileWallet<K>
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
use util::{kernel_sig_msg, secp};
|
use util::{kernel_sig_msg, secp};
|
||||||
|
|
||||||
use core::core::hash::Hash;
|
use core::core::hash::Hash;
|
||||||
use core::core::merkle_proof::MerkleProof;
|
|
||||||
use core::core::{Input, Output, OutputFeatures, Transaction, TxKernel};
|
use core::core::{Input, Output, OutputFeatures, Transaction, TxKernel};
|
||||||
use keychain::{self, BlindSum, BlindingFactor, Identifier, Keychain};
|
use keychain::{self, BlindSum, BlindingFactor, Identifier, Keychain};
|
||||||
use libtx::{aggsig, proof};
|
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>(
|
fn build_input<K>(
|
||||||
value: u64,
|
value: u64,
|
||||||
features: OutputFeatures,
|
features: OutputFeatures,
|
||||||
block_hash: Option<Hash>,
|
|
||||||
merkle_proof: Option<MerkleProof>,
|
|
||||||
key_id: Identifier,
|
key_id: Identifier,
|
||||||
) -> Box<Append<K>>
|
) -> Box<Append<K>>
|
||||||
where
|
where
|
||||||
|
@ -62,7 +59,7 @@ where
|
||||||
Box::new(
|
Box::new(
|
||||||
move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
|
move |build, (tx, kern, sum)| -> (Transaction, TxKernel, BlindSum) {
|
||||||
let commit = build.keychain.commit(value, &key_id).unwrap();
|
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()))
|
(tx.with_input(input), kern, sum.sub_key_id(key_id.clone()))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -78,15 +75,12 @@ where
|
||||||
LOGGER,
|
LOGGER,
|
||||||
"Building input (spending regular output): {}, {}", value, key_id
|
"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.
|
/// Adds a coinbase input spending a coinbase output.
|
||||||
/// We will use the block hash to verify coinbase maturity.
|
|
||||||
pub fn coinbase_input<K>(
|
pub fn coinbase_input<K>(
|
||||||
value: u64,
|
value: u64,
|
||||||
block_hash: Hash,
|
|
||||||
merkle_proof: MerkleProof,
|
|
||||||
key_id: Identifier,
|
key_id: Identifier,
|
||||||
) -> Box<Append<K>>
|
) -> Box<Append<K>>
|
||||||
where
|
where
|
||||||
|
@ -96,13 +90,7 @@ where
|
||||||
LOGGER,
|
LOGGER,
|
||||||
"Building input (spending coinbase): {}, {}", value, key_id
|
"Building input (spending coinbase): {}, {}", value, key_id
|
||||||
);
|
);
|
||||||
build_input(
|
build_input(value, OutputFeatures::COINBASE_OUTPUT, key_id)
|
||||||
value,
|
|
||||||
OutputFeatures::COINBASE_OUTPUT,
|
|
||||||
Some(block_hash),
|
|
||||||
Some(merkle_proof),
|
|
||||||
key_id,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds an output with the provided value and key identifier from the
|
/// Adds an output with the provided value and key identifier from the
|
||||||
|
|
|
@ -36,11 +36,11 @@ pub use libtx::error::{Error, ErrorKind};
|
||||||
const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN;
|
const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN;
|
||||||
|
|
||||||
/// Transaction fee calculation
|
/// 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 {
|
let use_base_fee = match base_fee {
|
||||||
Some(bf) => bf,
|
Some(bf) => bf,
|
||||||
None => DEFAULT_BASE_FEE,
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,7 +264,6 @@ impl Slate {
|
||||||
let fee = tx_fee(
|
let fee = tx_fee(
|
||||||
self.tx.inputs.len(),
|
self.tx.inputs.len(),
|
||||||
self.tx.outputs.len(),
|
self.tx.outputs.len(),
|
||||||
self.tx.input_proofs_count(),
|
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
if fee > self.tx.fee() {
|
if fee > self.tx.fee() {
|
||||||
|
|
|
@ -108,7 +108,8 @@ where
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(
|
error!(
|
||||||
LOGGER,
|
LOGGER,
|
||||||
"Communication with receiver failed on SenderInitiation send. Aborting transaction"
|
"Communication with receiver failed on SenderInitiation send. Aborting transaction {:?}",
|
||||||
|
e,
|
||||||
);
|
);
|
||||||
return Err(e)?;
|
return Err(e)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,6 @@ struct OutputResult {
|
||||||
///
|
///
|
||||||
pub is_coinbase: bool,
|
pub is_coinbase: bool,
|
||||||
///
|
///
|
||||||
pub merkle_proof: Option<MerkleProofWrapper>,
|
|
||||||
///
|
|
||||||
pub blinding: SecretKey,
|
pub blinding: SecretKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,13 +73,8 @@ where
|
||||||
"Output found: {:?}, amount: {:?}", commit, info.value
|
"Output found: {:?}, amount: {:?}", commit, info.value
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut merkle_proof = None;
|
|
||||||
let commit_str = util::to_hex(commit.as_ref().to_vec());
|
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 height = current_chain_height;
|
||||||
let lock_height = if *is_coinbase {
|
let lock_height = if *is_coinbase {
|
||||||
height + global::coinbase_maturity()
|
height + global::coinbase_maturity()
|
||||||
|
@ -97,7 +90,6 @@ where
|
||||||
height: height,
|
height: height,
|
||||||
lock_height: lock_height,
|
lock_height: lock_height,
|
||||||
is_coinbase: *is_coinbase,
|
is_coinbase: *is_coinbase,
|
||||||
merkle_proof: merkle_proof,
|
|
||||||
blinding: info.blinding,
|
blinding: info.blinding,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -219,8 +211,6 @@ where
|
||||||
height: output.height,
|
height: output.height,
|
||||||
lock_height: output.lock_height,
|
lock_height: output.lock_height,
|
||||||
is_coinbase: output.is_coinbase,
|
is_coinbase: output.is_coinbase,
|
||||||
block: None,
|
|
||||||
merkle_proof: output.merkle_proof,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
|
|
|
@ -109,8 +109,6 @@ where
|
||||||
height: current_height,
|
height: current_height,
|
||||||
lock_height: 0,
|
lock_height: 0,
|
||||||
is_coinbase: false,
|
is_coinbase: false,
|
||||||
block: None,
|
|
||||||
merkle_proof: None,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
batch.commit()?;
|
batch.commit()?;
|
||||||
|
@ -174,8 +172,6 @@ where
|
||||||
height: height,
|
height: height,
|
||||||
lock_height: 0,
|
lock_height: 0,
|
||||||
is_coinbase: false,
|
is_coinbase: false,
|
||||||
block: None,
|
|
||||||
merkle_proof: None,
|
|
||||||
});
|
});
|
||||||
batch.commit()?;
|
batch.commit()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -238,7 +234,7 @@ where
|
||||||
// sender
|
// sender
|
||||||
let mut fee;
|
let mut fee;
|
||||||
// First attempt to spend without change
|
// 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 total: u64 = coins.iter().map(|c| c.value).sum();
|
||||||
let mut amount_with_fee = amount + fee;
|
let mut amount_with_fee = amount + fee;
|
||||||
|
|
||||||
|
@ -251,7 +247,7 @@ where
|
||||||
|
|
||||||
// Check if we need to use a change address
|
// Check if we need to use a change address
|
||||||
if total > amount_with_fee {
|
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;
|
amount_with_fee = amount + fee;
|
||||||
|
|
||||||
// Here check if we have enough outputs for the amount including fee otherwise
|
// Here check if we have enough outputs for the amount including fee otherwise
|
||||||
|
@ -274,7 +270,7 @@ where
|
||||||
max_outputs,
|
max_outputs,
|
||||||
selection_strategy_is_use_all,
|
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();
|
total = coins.iter().map(|c| c.value).sum();
|
||||||
amount_with_fee = amount + fee;
|
amount_with_fee = amount + fee;
|
||||||
}
|
}
|
||||||
|
@ -290,11 +286,6 @@ where
|
||||||
Ok((parts, coins, change, change_derivation, amount, fee))
|
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
|
/// Selects inputs and change for a transaction
|
||||||
pub fn inputs_and_change<T, K>(
|
pub fn inputs_and_change<T, K>(
|
||||||
coins: &Vec<OutputData>,
|
coins: &Vec<OutputData>,
|
||||||
|
@ -322,14 +313,8 @@ where
|
||||||
for coin in coins {
|
for coin in coins {
|
||||||
let key_id = wallet.keychain().derive_key_id(coin.n_child)?;
|
let key_id = wallet.keychain().derive_key_id(coin.n_child)?;
|
||||||
if coin.is_coinbase {
|
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(
|
parts.push(build::coinbase_input(
|
||||||
coin.value,
|
coin.value,
|
||||||
block.unwrap().hash(),
|
|
||||||
merkle_proof,
|
|
||||||
key_id,
|
key_id,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -162,7 +162,7 @@ where
|
||||||
|
|
||||||
debug!(LOGGER, "selected some coins - {}", coins.len());
|
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)?;
|
let (mut parts, _, _) = selection::inputs_and_change(&coins, wallet, amount, fee)?;
|
||||||
|
|
||||||
//TODO: If we end up using this, create change output here
|
//TODO: If we end up using this, create change output here
|
||||||
|
|
|
@ -65,55 +65,6 @@ where
|
||||||
{
|
{
|
||||||
let height = wallet.get_chain_height()?;
|
let height = wallet.get_chain_height()?;
|
||||||
refresh_output_state(wallet, 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,29 +90,6 @@ where
|
||||||
Ok(wallet_outputs)
|
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
|
/// Apply refreshed API output data to the wallet
|
||||||
pub fn apply_api_outputs<T, K>(
|
pub fn apply_api_outputs<T, K>(
|
||||||
wallet: &mut T,
|
wallet: &mut T,
|
||||||
|
@ -329,7 +257,6 @@ 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 {
|
batch.save(OutputData {
|
||||||
root_key_id: root_key_id.clone(),
|
root_key_id: root_key_id.clone(),
|
||||||
key_id: key_id.clone(),
|
key_id: key_id.clone(),
|
||||||
|
@ -339,10 +266,7 @@ where
|
||||||
height: height,
|
height: height,
|
||||||
lock_height: lock_height,
|
lock_height: lock_height,
|
||||||
is_coinbase: true,
|
is_coinbase: true,
|
||||||
block: None,
|
});
|
||||||
merkle_proof: None,
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
batch.commit()?;
|
batch.commit()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ use serde_json;
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
|
|
||||||
use core::core::hash::Hash;
|
use core::core::hash::Hash;
|
||||||
use core::core::merkle_proof::MerkleProof;
|
|
||||||
use core::ser;
|
use core::ser;
|
||||||
|
|
||||||
use keychain::{Identifier, Keychain};
|
use keychain::{Identifier, Keychain};
|
||||||
|
@ -150,22 +149,6 @@ pub trait WalletClient {
|
||||||
),
|
),
|
||||||
Error,
|
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
|
/// Information about an output that's being tracked by the wallet. Must be
|
||||||
|
@ -190,10 +173,6 @@ pub struct OutputData {
|
||||||
pub lock_height: u64,
|
pub lock_height: u64,
|
||||||
/// Is this a coinbase output? Is it subject to coinbase locktime?
|
/// Is this a coinbase output? Is it subject to coinbase locktime?
|
||||||
pub is_coinbase: bool,
|
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 {
|
impl ser::Writeable for OutputData {
|
||||||
|
@ -239,14 +218,6 @@ impl OutputData {
|
||||||
return false;
|
return false;
|
||||||
} else if self.status == OutputStatus::Unconfirmed && self.is_coinbase {
|
} else if self.status == OutputStatus::Unconfirmed && self.is_coinbase {
|
||||||
return false;
|
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 {
|
} else if self.lock_height > current_height {
|
||||||
return false;
|
return false;
|
||||||
} else if self.status == OutputStatus::Unspent
|
} 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
|
/// Block Identifier
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
pub struct BlockIdentifier(pub Hash);
|
pub struct BlockIdentifier(pub Hash);
|
||||||
|
|
|
@ -251,28 +251,4 @@ impl<K> WalletClient for LMDBBackend<K> {
|
||||||
.context(ErrorKind::Node)?;
|
.context(ErrorKind::Node)?;
|
||||||
Ok(res)
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ use keychain::ExtKeychain;
|
||||||
use wallet::WalletConfig;
|
use wallet::WalletConfig;
|
||||||
use wallet::file_wallet::FileWallet;
|
use wallet::file_wallet::FileWallet;
|
||||||
use wallet::libwallet::internal::updater;
|
use wallet::libwallet::internal::updater;
|
||||||
use wallet::libwallet::types::{BlockFees, BlockIdentifier, MerkleProofWrapper, OutputStatus,
|
use wallet::libwallet::types::{BlockFees, BlockIdentifier, OutputStatus,
|
||||||
WalletBackend};
|
WalletBackend};
|
||||||
use wallet::libwallet::{Error, ErrorKind};
|
use wallet::libwallet::{Error, ErrorKind};
|
||||||
|
|
||||||
|
@ -176,13 +176,7 @@ where
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
add_block_with_reward(chain, txs, coinbase_tx.clone());
|
add_block_with_reward(chain, txs, coinbase_tx.clone());
|
||||||
// build merkle proof and block identifier and save in wallet
|
let output = wallet.get(&fees.key_id.unwrap()).unwrap();
|
||||||
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 mut batch = wallet.batch().unwrap();
|
let mut batch = wallet.batch().unwrap();
|
||||||
batch.save(output).unwrap();
|
batch.save(output).unwrap();
|
||||||
batch.commit().unwrap();
|
batch.commit().unwrap();
|
||||||
|
|
Loading…
Reference in a new issue