From 90f844d382b5a614308801521626c8fff81f5fce Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Mon, 5 Mar 2018 15:05:42 +0000 Subject: [PATCH] Include index as part of pmmr element hash (#748) * storing index as part of pmmr element hash * factor out hash_with_index into generic trait impl * tighten up trait bounds for PMMRable --- chain/src/sumtree.rs | 8 ++++---- core/src/core/pmmr.rs | 28 +++++++++++++++------------- core/src/core/transaction.rs | 9 +++++++++ core/src/ser.rs | 16 ++++++++++++++-- store/tests/pmmr.rs | 11 ++++++----- 5 files changed, 48 insertions(+), 24 deletions(-) diff --git a/chain/src/sumtree.rs b/chain/src/sumtree.rs index a4f1d91a8..831869418 100644 --- a/chain/src/sumtree.rs +++ b/chain/src/sumtree.rs @@ -30,7 +30,7 @@ use core::core::{Block, BlockHeader, Input, Output, OutputFeatures, OutputIdenti OutputStoreable, TxKernel}; use core::core::pmmr::{self, MerkleProof, PMMR}; use core::core::hash::{Hash, Hashed}; -use core::ser::{self, PMMRable}; +use core::ser::{self, PMMRable, PMMRIndexHashable}; use grin_store; use grin_store::pmmr::{PMMRBackend, PMMRFileMetadata}; @@ -141,7 +141,7 @@ impl SumTrees { let output_pmmr: PMMR = PMMR::at(&mut self.utxo_pmmr_h.backend, self.utxo_pmmr_h.last_pos); if let Some((hash, _)) = output_pmmr.get(pos, false) { - if hash == output_id.hash() { + if hash == output_id.hash_with_index(pos) { Ok(hash) } else { Err(Error::SumTreeErr(format!("sumtree hash mismatch"))) @@ -348,14 +348,14 @@ impl<'a> Extension<'a> { fn apply_input(&mut self, input: &Input, height: u64) -> Result<(), Error> { let commit = input.commitment(); let pos_res = self.get_output_pos(&commit); - let output_id_hash = OutputIdentifier::from_input(input).hash(); if let Ok(pos) = pos_res { + let output_id_hash = OutputIdentifier::from_input(input).hash_with_index(pos); if let Some((read_hash, read_elem)) = self.utxo_pmmr.get(pos, true) { // 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... if output_id_hash != read_hash - || output_id_hash != read_elem.expect("no output at position").hash() + || output_id_hash != read_elem.expect("no output at position").hash_with_index(pos) { return Err(Error::SumTreeErr(format!("output pmmr hash mismatch"))); } diff --git a/core/src/core/pmmr.rs b/core/src/core/pmmr.rs index 7d805c799..9247f0940 100644 --- a/core/src/core/pmmr.rs +++ b/core/src/core/pmmr.rs @@ -40,7 +40,7 @@ use std::marker::PhantomData; use core::hash::{Hash, Hashed}; use ser; use ser::{Readable, Reader, Writeable, Writer}; -use ser::PMMRable; +use ser::{PMMRable, PMMRIndexHashable}; use util; use util::LOGGER; @@ -360,7 +360,7 @@ where /// the same time if applicable. pub fn push(&mut self, elmt: T) -> Result { let elmt_pos = self.last_pos + 1; - let mut current_hash = elmt.hash(); + let mut current_hash = elmt.hash_with_index(elmt_pos); let mut to_append = vec![(current_hash, Some(elmt))]; let mut height = 0; let mut pos = elmt_pos; @@ -917,6 +917,7 @@ mod test { use ser::{Error, Readable, Writeable}; use core::{Reader, Writer}; use core::hash::Hash; + use ser::{PMMRable, PMMRIndexHashable}; /// Simple MMR backend implementation based on a Vector. Pruning does not /// compact the Vec itself. @@ -1169,7 +1170,7 @@ mod test { fn len() -> usize { 16 } - } +} impl Writeable for TestElem { fn write(&self, writer: &mut W) -> Result<(), Error> { @@ -1305,28 +1306,28 @@ mod test { // one element pmmr.push(elems[0]).unwrap(); - let node_hash = elems[0].hash(); - assert_eq!(pmmr.root(), node_hash,); + let node_hash = elems[0].hash_with_index(1); + assert_eq!(pmmr.root(), node_hash); assert_eq!(pmmr.unpruned_size(), 1); pmmr.dump(false); // two elements pmmr.push(elems[1]).unwrap(); - let sum2 = elems[0].hash() + elems[1].hash(); + let sum2 = elems[0].hash_with_index(1) + elems[1].hash_with_index(2); pmmr.dump(false); assert_eq!(pmmr.root(), sum2); assert_eq!(pmmr.unpruned_size(), 3); // three elements pmmr.push(elems[2]).unwrap(); - let sum3 = sum2 + elems[2].hash(); + let sum3 = sum2 + elems[2].hash_with_index(4); pmmr.dump(false); assert_eq!(pmmr.root(), sum3); assert_eq!(pmmr.unpruned_size(), 4); // four elements pmmr.push(elems[3]).unwrap(); - let sum_one = elems[2].hash() + elems[3].hash(); + let sum_one = elems[2].hash_with_index(4) + elems[3].hash_with_index(5); let sum4 = sum2 + sum_one; pmmr.dump(false); assert_eq!(pmmr.root(), sum4); @@ -1334,33 +1335,34 @@ mod test { // five elements pmmr.push(elems[4]).unwrap(); - let sum3 = sum4 + elems[4].hash(); + let sum3 = sum4 + elems[4].hash_with_index(8); pmmr.dump(false); assert_eq!(pmmr.root(), sum3); assert_eq!(pmmr.unpruned_size(), 8); // six elements pmmr.push(elems[5]).unwrap(); - let sum6 = sum4 + (elems[4].hash() + elems[5].hash()); + let sum6 = sum4 + (elems[4].hash_with_index(8) + elems[5].hash_with_index(9)); assert_eq!(pmmr.root(), sum6.clone()); assert_eq!(pmmr.unpruned_size(), 10); // seven elements pmmr.push(elems[6]).unwrap(); - let sum7 = sum6 + elems[6].hash(); + let sum7 = sum6 + elems[6].hash_with_index(11); assert_eq!(pmmr.root(), sum7); assert_eq!(pmmr.unpruned_size(), 11); // eight elements pmmr.push(elems[7]).unwrap(); let sum8 = - sum4 + ((elems[4].hash() + elems[5].hash()) + (elems[6].hash() + elems[7].hash())); + sum4 + ((elems[4].hash_with_index(8) + elems[5].hash_with_index(9)) + + (elems[6].hash_with_index(11) + elems[7].hash_with_index(12))); assert_eq!(pmmr.root(), sum8); assert_eq!(pmmr.unpruned_size(), 15); // nine elements pmmr.push(elems[8]).unwrap(); - let sum9 = sum8 + elems[8].hash(); + let sum9 = sum8 + elems[8].hash_with_index(16); assert_eq!(pmmr.root(), sum9); assert_eq!(pmmr.unpruned_size(), 16); } diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 85ab85cc0..be55ba36a 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -938,6 +938,8 @@ pub struct OutputIdentifier { pub commit: Commitment, } + + impl OutputIdentifier { /// Build a new output_identifier. pub fn new(features: OutputFeatures, commit: &Commitment) -> OutputIdentifier { @@ -973,6 +975,13 @@ impl OutputIdentifier { } } +/// Ensure this is implemented to centralize hashing with indexes +impl PMMRable for OutputIdentifier { + fn len() -> usize { + 1 + secp::constants::PEDERSEN_COMMITMENT_SIZE + SWITCH_COMMIT_HASH_SIZE + } +} + impl Writeable for OutputIdentifier { fn write(&self, writer: &mut W) -> Result<(), ser::Error> { writer.write_u8(self.features.bits())?; diff --git a/core/src/ser.rs b/core/src/ser.rs index cce9418a8..31003b604 100644 --- a/core/src/ser.rs +++ b/core/src/ser.rs @@ -25,7 +25,7 @@ use byteorder::{BigEndian, ByteOrder, ReadBytesExt}; use keychain::{BlindingFactor, Identifier, IDENTIFIER_SIZE}; use consensus; use consensus::VerifySortOrder; -use core::hash::Hashed; +use core::hash::{Hashed, Hash}; use core::transaction::{SwitchCommitHash, SWITCH_COMMIT_HASH_SIZE}; use util::secp::pedersen::Commitment; use util::secp::pedersen::RangeProof; @@ -549,11 +549,23 @@ impl Writeable for [u8; 4] { } /// Trait for types that can serialize and report their size -pub trait PMMRable: Readable + Writeable + Hashed + Clone { +pub trait PMMRable: Readable + Writeable + Clone { /// Length in bytes fn len() -> usize; } +/// Generic trait to ensure PMMR elements can be hashed with an index +pub trait PMMRIndexHashable { + /// Hash with a given index + fn hash_with_index(&self, index: u64) -> Hash; +} + +impl PMMRIndexHashable for T { + fn hash_with_index(&self, index: u64) -> Hash { + (index, self).hash() + } +} + /// Useful marker trait on types that can be sized byte slices pub trait AsFixedBytes: Sized + AsRef<[u8]> { /// The length in bytes diff --git a/store/tests/pmmr.rs b/store/tests/pmmr.rs index 5b554d723..a2f8a5962 100644 --- a/store/tests/pmmr.rs +++ b/store/tests/pmmr.rs @@ -37,13 +37,14 @@ fn pmmr_append() { backend.sync().unwrap(); // check the resulting backend store and the computation of the root - let node_hash = elems[0].hash(); + let node_hash = elems[0].hash_with_index(1); assert_eq!(backend.get(1, false).expect("").0, node_hash); - let sum2 = elems[0].hash() + elems[1].hash(); - let sum4 = sum2 + (elems[2].hash() + elems[3].hash()); - let sum8 = sum4 + ((elems[4].hash() + elems[5].hash()) + (elems[6].hash() + elems[7].hash())); - let sum9 = sum8 + elems[8].hash(); + let sum2 = elems[0].hash_with_index(1) + elems[1].hash_with_index(2); + let sum4 = sum2 + (elems[2].hash_with_index(4) + elems[3].hash_with_index(5)); + let sum8 = sum4 + ((elems[4].hash_with_index(8) + elems[5].hash_with_index(9)) + + (elems[6].hash_with_index(11) + elems[7].hash_with_index(12))); + let sum9 = sum8 + elems[8].hash_with_index(16); { let pmmr: PMMR = PMMR::at(&mut backend, mmr_size);