From 02b3bf4b59fd6c8fe912bd83e094d49ee20286cb Mon Sep 17 00:00:00 2001 From: Antioch Peverell Date: Wed, 5 Dec 2018 17:58:40 +0000 Subject: [PATCH] utxo_view hash compare (#2082) * utxo_view validate_input now checks the data in the output MMR * cleanup apply_input and validate_input --- chain/src/txhashset/txhashset.rs | 17 +++++------------ chain/src/txhashset/utxo_view.rs | 25 ++++++++++++++----------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/chain/src/txhashset/txhashset.rs b/chain/src/txhashset/txhashset.rs index ba123bc2..592fdb9b 100644 --- a/chain/src/txhashset/txhashset.rs +++ b/chain/src/txhashset/txhashset.rs @@ -910,16 +910,9 @@ impl<'a> Extension<'a> { let commit = input.commitment(); let pos_res = self.batch.get_output_pos(&commit); if let Ok(pos) = pos_res { - let output_id_hash = OutputIdentifier::from_input(input).hash_with_index(pos - 1); - if let Some(read_hash) = self.output_pmmr.get_hash(pos) { - // check hash from pmmr matches hash from input (or corresponding output) - // if not then the input is not being honest about - // what it is attempting to spend... - let read_elem = self.output_pmmr.get_data(pos); - let read_elem_hash = read_elem - .expect("no output at pos") - .hash_with_index(pos - 1); - if output_id_hash != read_hash || output_id_hash != read_elem_hash { + // First check this input corresponds to an existing entry in the output MMR. + if let Some(hash) = self.output_pmmr.get_hash(pos) { + if hash != input.hash_with_index(pos - 1) { return Err( ErrorKind::TxHashSetErr(format!("output pmmr hash mismatch")).into(), ); @@ -933,10 +926,10 @@ impl<'a> Extension<'a> { Ok(true) => { self.rproof_pmmr .prune(pos) - .map_err(|s| ErrorKind::TxHashSetErr(s))?; + .map_err(|e| ErrorKind::TxHashSetErr(e))?; } Ok(false) => return Err(ErrorKind::AlreadySpent(commit).into()), - Err(s) => return Err(ErrorKind::TxHashSetErr(s).into()), + Err(e) => return Err(ErrorKind::TxHashSetErr(e).into()), } } else { return Err(ErrorKind::AlreadySpent(commit).into()); diff --git a/chain/src/txhashset/utxo_view.rs b/chain/src/txhashset/utxo_view.rs index ceaf1353..f434931d 100644 --- a/chain/src/txhashset/utxo_view.rs +++ b/chain/src/txhashset/utxo_view.rs @@ -16,7 +16,7 @@ use core::core::pmmr::ReadonlyPMMR; use core::core::{Block, Input, Output, Transaction}; - +use core::ser::PMMRIndexHashable; use error::{Error, ErrorKind}; use grin_store::pmmr::PMMRBackend; use store::Batch; @@ -64,23 +64,26 @@ impl<'a> UTXOView<'a> { Ok(()) } + // Input is valid if it is spending an (unspent) output + // that currently exists in the output MMR. + // Compare the hash in the output MMR at the expected pos. fn validate_input(&self, input: &Input) -> Result<(), Error> { - let commit = input.commitment(); - let pos_res = self.batch.get_output_pos(&commit); - if let Ok(pos) = pos_res { - if let Some(_) = self.pmmr.get_data(pos) { - return Ok(()); + if let Ok(pos) = self.batch.get_output_pos(&input.commitment()) { + if let Some(hash) = self.pmmr.get_hash(pos) { + if hash == input.hash_with_index(pos - 1) { + return Ok(()); + } } } - Err(ErrorKind::AlreadySpent(commit).into()) + Err(ErrorKind::AlreadySpent(input.commitment()).into()) } + // Output is valid if it would not result in a duplicate commitment in the output MMR. fn validate_output(&self, output: &Output) -> Result<(), Error> { - let commit = output.commitment(); - if let Ok(pos) = self.batch.get_output_pos(&commit) { + if let Ok(pos) = self.batch.get_output_pos(&output.commitment()) { if let Some(out_mmr) = self.pmmr.get_data(pos) { - if out_mmr.commitment() == commit { - return Err(ErrorKind::DuplicateCommitment(commit).into()); + if out_mmr.commitment() == output.commitment() { + return Err(ErrorKind::DuplicateCommitment(output.commitment()).into()); } } }