From a32742717849084156348aaf96f417f36d35399d Mon Sep 17 00:00:00 2001 From: John Tromp Date: Mon, 26 Mar 2018 04:26:11 +0200 Subject: [PATCH] 0-based positions, bag peaks right-to-left with mmr size as index (#863) * 0-based positions, bag peaks right-to-left with mmr size as index * fix remaining instances of hash_with_index * fix pmmr test --- chain/src/txhashset.rs | 6 +-- core/src/core/pmmr.rs | 108 ++++++++++++++++++++++------------------- store/tests/pmmr.rs | 36 +++++++------- 3 files changed, 79 insertions(+), 71 deletions(-) diff --git a/chain/src/txhashset.rs b/chain/src/txhashset.rs index e67c44d3e..41844affd 100644 --- a/chain/src/txhashset.rs +++ b/chain/src/txhashset.rs @@ -146,7 +146,7 @@ impl TxHashSet { let output_pmmr: PMMR = PMMR::at(&mut self.output_pmmr_h.backend, self.output_pmmr_h.last_pos); if let Some(hash) = output_pmmr.get_hash(pos) { - if hash == output_id.hash_with_index(pos) { + if hash == output_id.hash_with_index(pos-1) { Ok(hash) } else { Err(Error::TxHashSetErr(format!("txhashset hash mismatch"))) @@ -386,7 +386,7 @@ impl<'a> Extension<'a> { let commit = input.commitment(); let pos_res = self.get_output_pos(&commit); if let Ok(pos) = pos_res { - let output_id_hash = OutputIdentifier::from_input(input).hash_with_index(pos); + 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 @@ -398,7 +398,7 @@ impl<'a> Extension<'a> { || output_id_hash != read_elem .expect("no output at position") - .hash_with_index(pos) + .hash_with_index(pos-1) { return Err(Error::TxHashSetErr(format!("output pmmr hash mismatch"))); } diff --git a/core/src/core/pmmr.rs b/core/src/core/pmmr.rs index 7ff642c8e..40fe41e8b 100644 --- a/core/src/core/pmmr.rs +++ b/core/src/core/pmmr.rs @@ -37,7 +37,7 @@ use std::clone::Clone; use std::marker::PhantomData; -use core::hash::{Hash, Hashed}; +use core::hash::{Hash}; use ser; use ser::{Readable, Reader, Writeable, Writer}; use ser::{PMMRIndexHashable, PMMRable}; @@ -111,6 +111,8 @@ pub struct MerkleProof { pub root: Hash, /// The hash of the element in the tree we care about pub node: Hash, + /// The size of the full Merkle tree + pub mmr_size: u64, /// The full list of peak hashes in the MMR pub peaks: Vec, /// The sibling (hash, pos) along the path of the tree @@ -124,6 +126,7 @@ impl Writeable for MerkleProof { writer, [write_fixed_bytes, &self.root], [write_fixed_bytes, &self.node], + [write_u64, self.mmr_size], [write_u64, self.peaks.len() as u64], [write_u64, self.path.len() as u64] ); @@ -140,7 +143,7 @@ impl Readable for MerkleProof { let root = Hash::read(reader)?; let node = Hash::read(reader)?; - let (peaks_len, path_len) = ser_multiread!(reader, read_u64, read_u64); + let (mmr_size, peaks_len, path_len) = ser_multiread!(reader, read_u64, read_u64, read_u64); if peaks_len > MAX_PEAKS || path_len > MAX_PATH { return Err(ser::Error::CorruptedData); @@ -161,6 +164,7 @@ impl Readable for MerkleProof { Ok(MerkleProof { root, node, + mmr_size, peaks, path, }) @@ -180,6 +184,7 @@ impl MerkleProof { MerkleProof { root: Hash::default(), node: Hash::default(), + mmr_size: 0, peaks: vec![], path: vec![], } @@ -215,10 +220,10 @@ impl MerkleProof { } let mut bagged = None; - for peak in self.peaks.iter().cloned() { - bagged = match (bagged, peak) { - (None, rhs) => Some(rhs), - (Some(lhs), rhs) => Some(lhs.hash_with(rhs)), + for peak in self.peaks.iter().rev() { + bagged = match bagged { + None => Some(*peak), + Some(rhs) => Some((*peak,rhs).hash_with_index(self.mmr_size)), } } return bagged == Some(self.root); @@ -231,14 +236,15 @@ impl MerkleProof { // hash our node and sibling together (noting left/right position of the // sibling) let parent = if is_left_sibling(sibling_pos) { - (sibling, self.node).hash_with_index(parent_pos) + (sibling, self.node).hash_with_index(parent_pos-1) } else { - (self.node, sibling).hash_with_index(parent_pos) + (self.node, sibling).hash_with_index(parent_pos-1) }; let proof = MerkleProof { root: self.root, node: parent, + mmr_size: self.mmr_size, peaks: self.peaks.clone(), path, }; @@ -307,10 +313,10 @@ where /// tree and "bags" them to get a single peak. pub fn root(&self) -> Hash { let mut res = None; - for peak in self.peaks() { - res = match (res, peak) { - (None, rhash) => Some(rhash), - (Some(lhash), rhash) => Some(lhash.hash_with(rhash)), + for peak in self.peaks().iter().rev() { + res = match res { + None => Some(*peak), + Some(rhash) => Some((*peak,rhash).hash_with_index(self.unpruned_size())), } } res.expect("no root, invalid tree") @@ -332,6 +338,8 @@ where let node = self.get_hash(pos) .ok_or(format!("no element at pos {}", pos))?; + let mmr_size = self.unpruned_size(); + let family_branch = family_branch(pos, self.last_pos); let path = family_branch @@ -350,6 +358,7 @@ where let proof = MerkleProof { root, node, + mmr_size, peaks, path, }; @@ -361,7 +370,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_with_index(elmt_pos); + let mut current_hash = elmt.hash_with_index(elmt_pos-1); let mut to_append = vec![(current_hash, Some(elmt))]; let mut height = 0; @@ -381,7 +390,7 @@ where height += 1; pos += 1; - current_hash = (left_hash, current_hash).hash_with_index(pos); + current_hash = (left_hash, current_hash).hash_with_index(pos-1); to_append.push((current_hash.clone(), None)); } @@ -531,7 +540,7 @@ where if let Some(right_child_hs) = self.get_from_file(right_pos) { // hash the two child nodes together with parent_pos and compare let (parent_pos, _) = family(left_pos); - if (left_child_hs, right_child_hs).hash_with_index(parent_pos) != hash { + if (left_child_hs, right_child_hs).hash_with_index(parent_pos-1) != hash { return Err(format!( "Invalid MMR, hash of parent at {} does \ not match children.", @@ -1426,78 +1435,77 @@ mod test { // one element pmmr.push(elems[0]).unwrap(); pmmr.dump(false); - let pos_1 = elems[0].hash_with_index(1); - assert_eq!(pmmr.peaks(), vec![pos_1]); - assert_eq!(pmmr.root(), pos_1); + let pos_0 = elems[0].hash_with_index(0); + assert_eq!(pmmr.peaks(), vec![pos_0]); + assert_eq!(pmmr.root(), pos_0); assert_eq!(pmmr.unpruned_size(), 1); // two elements pmmr.push(elems[1]).unwrap(); pmmr.dump(false); - let pos_2 = elems[1].hash_with_index(2); - let pos_3 = (pos_1, pos_2).hash_with_index(3); - assert_eq!(pmmr.peaks(), vec![pos_3]); - assert_eq!(pmmr.root(), pos_3); + let pos_1 = elems[1].hash_with_index(1); + let pos_2 = (pos_0, pos_1).hash_with_index(2); + assert_eq!(pmmr.peaks(), vec![pos_2]); + assert_eq!(pmmr.root(), pos_2); assert_eq!(pmmr.unpruned_size(), 3); // three elements pmmr.push(elems[2]).unwrap(); pmmr.dump(false); - let pos_4 = elems[2].hash_with_index(4); - assert_eq!(pmmr.peaks(), vec![pos_3, pos_4]); - let root = pos_3 + pos_4; - assert_eq!(pmmr.root(), pos_3 + pos_4); + let pos_3 = elems[2].hash_with_index(3); + assert_eq!(pmmr.peaks(), vec![pos_2, pos_3]); + assert_eq!(pmmr.root(), (pos_2, pos_3).hash_with_index(4)); assert_eq!(pmmr.unpruned_size(), 4); // four elements pmmr.push(elems[3]).unwrap(); pmmr.dump(false); - let pos_5 = elems[3].hash_with_index(5); - let pos_6 = (pos_4, pos_5).hash_with_index(6); - let pos_7 = (pos_3, pos_6).hash_with_index(7); - assert_eq!(pmmr.peaks(), vec![pos_7]); - assert_eq!(pmmr.root(), pos_7); + let pos_4 = elems[3].hash_with_index(4); + let pos_5 = (pos_3, pos_4).hash_with_index(5); + let pos_6 = (pos_2, pos_5).hash_with_index(6); + assert_eq!(pmmr.peaks(), vec![pos_6]); + assert_eq!(pmmr.root(), pos_6); assert_eq!(pmmr.unpruned_size(), 7); // five elements pmmr.push(elems[4]).unwrap(); pmmr.dump(false); - let pos_8 = elems[4].hash_with_index(8); - assert_eq!(pmmr.peaks(), vec![pos_7, pos_8]); - assert_eq!(pmmr.root(), pos_7 + pos_8); + let pos_7 = elems[4].hash_with_index(7); + assert_eq!(pmmr.peaks(), vec![pos_6, pos_7]); + assert_eq!(pmmr.root(), (pos_6, pos_7).hash_with_index(8)); assert_eq!(pmmr.unpruned_size(), 8); // six elements pmmr.push(elems[5]).unwrap(); - let pos_9 = elems[5].hash_with_index(9); - let pos_10 = (pos_8, pos_9).hash_with_index(10); - assert_eq!(pmmr.peaks(), vec![pos_7, pos_10]); - assert_eq!(pmmr.root(), pos_7 + pos_10); + let pos_8 = elems[5].hash_with_index(8); + let pos_9 = (pos_7, pos_8).hash_with_index(9); + assert_eq!(pmmr.peaks(), vec![pos_6, pos_9]); + assert_eq!(pmmr.root(), (pos_6, pos_9).hash_with_index(10)); assert_eq!(pmmr.unpruned_size(), 10); // seven elements pmmr.push(elems[6]).unwrap(); - let pos_11 = elems[6].hash_with_index(11); - assert_eq!(pmmr.peaks(), vec![pos_7, pos_10, pos_11]); - assert_eq!(pmmr.root(), pos_7 + pos_10 + pos_11); + let pos_10 = elems[6].hash_with_index(10); + assert_eq!(pmmr.peaks(), vec![pos_6, pos_9, pos_10]); + assert_eq!(pmmr.root(), (pos_6, (pos_9, pos_10).hash_with_index(11)).hash_with_index(11)); assert_eq!(pmmr.unpruned_size(), 11); // 001001200100123 // eight elements pmmr.push(elems[7]).unwrap(); - let pos_12 = elems[7].hash_with_index(12); - let pos_13 = (pos_11, pos_12).hash_with_index(13); - let pos_14 = (pos_10, pos_13).hash_with_index(14); - let pos_15 = (pos_7, pos_14).hash_with_index(15); - assert_eq!(pmmr.peaks(), vec![pos_15]); - assert_eq!(pmmr.root(), pos_15); + let pos_11 = elems[7].hash_with_index(11); + let pos_12 = (pos_10, pos_11).hash_with_index(12); + let pos_13 = (pos_9, pos_12).hash_with_index(13); + let pos_14 = (pos_6, pos_13).hash_with_index(14); + assert_eq!(pmmr.peaks(), vec![pos_14]); + assert_eq!(pmmr.root(), pos_14); assert_eq!(pmmr.unpruned_size(), 15); // nine elements pmmr.push(elems[8]).unwrap(); - let pos_16 = elems[8].hash_with_index(16); - assert_eq!(pmmr.peaks(), vec![pos_15, pos_16]); - assert_eq!(pmmr.root(), pos_15 + pos_16); + let pos_15 = elems[8].hash_with_index(15); + assert_eq!(pmmr.peaks(), vec![pos_14, pos_15]); + assert_eq!(pmmr.root(), (pos_14, pos_15).hash_with_index(16)); assert_eq!(pmmr.unpruned_size(), 16); } diff --git a/store/tests/pmmr.rs b/store/tests/pmmr.rs index 32b5c3595..80240eddb 100644 --- a/store/tests/pmmr.rs +++ b/store/tests/pmmr.rs @@ -38,35 +38,35 @@ fn pmmr_append() { backend.sync().unwrap(); // check the resulting backend store and the computation of the root - let node_hash = elems[0].hash_with_index(1); + let node_hash = elems[0].hash_with_index(0); assert_eq!(backend.get_hash(1).unwrap(), node_hash); // 0010012001001230 - let pos_1 = elems[0].hash_with_index(1); - let pos_2 = elems[1].hash_with_index(2); - let pos_3 = (pos_1, pos_2).hash_with_index(3); + let pos_0 = elems[0].hash_with_index(0); + let pos_1 = elems[1].hash_with_index(1); + let pos_2 = (pos_0, pos_1).hash_with_index(2); - let pos_4 = elems[2].hash_with_index(4); - let pos_5 = elems[3].hash_with_index(5); - let pos_6 = (pos_4, pos_5).hash_with_index(6); - let pos_7 = (pos_3, pos_6).hash_with_index(7); + let pos_3 = elems[2].hash_with_index(3); + let pos_4 = elems[3].hash_with_index(4); + let pos_5 = (pos_3, pos_4).hash_with_index(5); + let pos_6 = (pos_2, pos_5).hash_with_index(6); - let pos_8 = elems[4].hash_with_index(8); - let pos_9 = elems[5].hash_with_index(9); - let pos_10 = (pos_8, pos_9).hash_with_index(10); + let pos_7 = elems[4].hash_with_index(7); + let pos_8 = elems[5].hash_with_index(8); + let pos_9 = (pos_7, pos_8).hash_with_index(9); - let pos_11 = elems[6].hash_with_index(11); - let pos_12 = elems[7].hash_with_index(12); - let pos_13 = (pos_11, pos_12).hash_with_index(13); - let pos_14 = (pos_10, pos_13).hash_with_index(14); - let pos_15 = (pos_7, pos_14).hash_with_index(15); + let pos_10 = elems[6].hash_with_index(10); + let pos_11 = elems[7].hash_with_index(11); + let pos_12 = (pos_10, pos_11).hash_with_index(12); + let pos_13 = (pos_9, pos_12).hash_with_index(13); + let pos_14 = (pos_6, pos_13).hash_with_index(14); - let pos_16 = elems[8].hash_with_index(16); + let pos_15 = elems[8].hash_with_index(15); { let pmmr: PMMR = PMMR::at(&mut backend, mmr_size); - assert_eq!(pmmr.root(), pos_15 + pos_16); + assert_eq!(pmmr.root(), (pos_14, pos_15).hash_with_index(16)); } teardown(data_dir);