From 0aec8b533b2ac45614150132b8f40dbac475539a Mon Sep 17 00:00:00 2001 From: jaspervdm Date: Tue, 29 Sep 2020 15:57:33 +0200 Subject: [PATCH] Refactor PMMR read methods into trait (#3454) --- chain/src/txhashset/bitmap_accumulator.rs | 6 +- chain/src/txhashset/txhashset.rs | 8 +- chain/src/txhashset/utxo_view.rs | 2 +- core/src/core/pmmr/pmmr.rs | 309 ++++++++++++---------- core/src/core/pmmr/readonly_pmmr.rs | 150 +++++------ core/tests/merkle_proof.rs | 6 +- core/tests/pmmr.rs | 20 +- core/tests/vec_backend.rs | 2 +- store/tests/pmmr.rs | 2 +- 9 files changed, 255 insertions(+), 250 deletions(-) diff --git a/chain/src/txhashset/bitmap_accumulator.rs b/chain/src/txhashset/bitmap_accumulator.rs index 49b4727165..f27ab1a47a 100644 --- a/chain/src/txhashset/bitmap_accumulator.rs +++ b/chain/src/txhashset/bitmap_accumulator.rs @@ -19,7 +19,7 @@ use bit_vec::BitVec; use croaring::Bitmap; use crate::core::core::hash::{DefaultHashable, Hash}; -use crate::core::core::pmmr::{self, ReadonlyPMMR, VecBackend, PMMR}; +use crate::core::core::pmmr::{self, ReadablePMMR, ReadonlyPMMR, VecBackend, PMMR}; use crate::core::ser::{self, PMMRable, Readable, Reader, Writeable, Writer}; use crate::error::{Error, ErrorKind}; @@ -176,7 +176,9 @@ impl BitmapAccumulator { /// The root hash of the bitmap accumulator MMR. pub fn root(&self) -> Hash { - ReadonlyPMMR::at(&self.backend, self.backend.size()).root() + ReadonlyPMMR::at(&self.backend, self.backend.size()) + .root() + .expect("no root, invalid tree") } } diff --git a/chain/src/txhashset/txhashset.rs b/chain/src/txhashset/txhashset.rs index 2e99ac1135..17bbe0558a 100644 --- a/chain/src/txhashset/txhashset.rs +++ b/chain/src/txhashset/txhashset.rs @@ -19,7 +19,7 @@ use crate::core::consensus::WEEK_HEIGHT; use crate::core::core::committed::Committed; use crate::core::core::hash::{Hash, Hashed}; use crate::core::core::merkle_proof::MerkleProof; -use crate::core::core::pmmr::{self, Backend, ReadonlyPMMR, RewindablePMMR, PMMR}; +use crate::core::core::pmmr::{self, Backend, ReadablePMMR, ReadonlyPMMR, RewindablePMMR, PMMR}; use crate::core::core::{Block, BlockHeader, KernelFeatures, Output, OutputIdentifier, TxKernel}; use crate::core::global; use crate::core::ser::{PMMRable, ProtocolVersion}; @@ -369,11 +369,11 @@ impl TxHashSet { TxHashSetRoots { output_roots: OutputRoots { - pmmr_root: output_pmmr.root(), + pmmr_root: output_pmmr.root().expect("no root, invalid tree"), bitmap_root: self.bitmap_accumulator.root(), }, - rproof_root: rproof_pmmr.root(), - kernel_root: kernel_pmmr.root(), + rproof_root: rproof_pmmr.root().expect("no root, invalid tree"), + kernel_root: kernel_pmmr.root().expect("no root, invalid tree"), } } diff --git a/chain/src/txhashset/utxo_view.rs b/chain/src/txhashset/utxo_view.rs index b963f3a4d7..3eb2e40c65 100644 --- a/chain/src/txhashset/utxo_view.rs +++ b/chain/src/txhashset/utxo_view.rs @@ -15,7 +15,7 @@ //! Lightweight readonly view into output MMR for convenience. use crate::core::core::hash::{Hash, Hashed}; -use crate::core::core::pmmr::{self, ReadonlyPMMR}; +use crate::core::core::pmmr::{self, ReadablePMMR, ReadonlyPMMR}; use crate::core::core::{Block, BlockHeader, Inputs, Output, OutputIdentifier, Transaction}; use crate::core::global; use crate::error::{Error, ErrorKind}; diff --git a/core/src/core/pmmr/pmmr.rs b/core/src/core/pmmr/pmmr.rs index 52e6e5b96c..86a37bbdf2 100644 --- a/core/src/core/pmmr/pmmr.rs +++ b/core/src/core/pmmr/pmmr.rs @@ -26,122 +26,95 @@ use crate::ser::{PMMRIndexHashable, PMMRable}; /// 64 bits all ones: 0b11111111...1 const ALL_ONES: u64 = u64::MAX; -/// Prunable Merkle Mountain Range implementation. All positions within the tree -/// start at 1 as they're postorder tree traversal positions rather than array -/// indices. -/// -/// Heavily relies on navigation operations within a binary tree. In particular, -/// all the implementation needs to keep track of the MMR structure is how far -/// we are in the sequence of nodes making up the MMR. -pub struct PMMR<'a, T, B> -where - T: PMMRable, - B: Backend, -{ - /// The last position in the PMMR - pub last_pos: u64, - backend: &'a mut B, - // only needed to parameterise Backend - _marker: marker::PhantomData, -} +/// Trait with common methods for reading from a PMMR +pub trait ReadablePMMR { + /// Leaf type + type Item; -impl<'a, T, B> PMMR<'a, T, B> -where - T: PMMRable, - B: 'a + Backend, -{ - /// Build a new prunable Merkle Mountain Range using the provided backend. - pub fn new(backend: &'a mut B) -> PMMR<'_, T, B> { - PMMR { - backend, - last_pos: 0, - _marker: marker::PhantomData, - } - } + /// Get the hash at provided position in the MMR. + fn get_hash(&self, pos: u64) -> Option; - /// Build a new prunable Merkle Mountain Range pre-initialized until - /// last_pos with the provided backend. - pub fn at(backend: &'a mut B, last_pos: u64) -> PMMR<'_, T, B> { - PMMR { - backend, - last_pos, - _marker: marker::PhantomData, - } - } + /// Get the data element at provided position in the MMR. + fn get_data(&self, pos: u64) -> Option; - /// Build a "readonly" view of this PMMR. - pub fn readonly_pmmr(&self) -> ReadonlyPMMR<'_, T, B> { - ReadonlyPMMR::at(&self.backend, self.last_pos) - } + /// Get the hash from the underlying MMR file (ignores the remove log). + fn get_from_file(&self, pos: u64) -> Option; - /// Iterator over current (unpruned, unremoved) leaf positions. - pub fn leaf_pos_iter(&self) -> impl Iterator + '_ { - self.backend.leaf_pos_iter() - } + /// Total size of the tree, including intermediary nodes and ignoring any pruning. + fn unpruned_size(&self) -> u64; - /// Number of leafs in the MMR - pub fn n_unpruned_leaves(&self) -> u64 { - self.backend.n_unpruned_leaves() - } + /// Iterator over current (unpruned, unremoved) leaf positions. + fn leaf_pos_iter(&self) -> Box + '_>; /// Iterator over current (unpruned, unremoved) leaf insertion indices. - pub fn leaf_idx_iter(&self, from_idx: u64) -> impl Iterator + '_ { - self.backend.leaf_idx_iter(from_idx) - } - - /// Returns a vec of the peaks of this MMR. - pub fn peaks(&self) -> impl DoubleEndedIterator + '_ { - let peaks_pos = peaks(self.last_pos); - peaks_pos.into_iter().filter_map(move |pi| { - // here we want to get from underlying hash file - // as the pos *may* have been "removed" - self.backend.get_from_file(pi) - }) - } + fn leaf_idx_iter(&self, from_idx: u64) -> Box + '_>; - fn peak_path(&self, peak_pos: u64) -> Vec { - let rhs = self.bag_the_rhs(peak_pos); - let mut res = peaks(self.last_pos) - .into_iter() - .filter(|x| *x < peak_pos) - .filter_map(|x| self.backend.get_from_file(x)) - .collect::>(); - if let Some(rhs) = rhs { - res.push(rhs); - } - res.reverse(); + /// Number of leaves in the MMR + fn n_unpruned_leaves(&self) -> u64; - res + /// Is the MMR empty? + fn is_empty(&self) -> bool { + self.unpruned_size() == 0 } /// Takes a single peak position and hashes together /// all the peaks to the right of this peak (if any). /// If this return a hash then this is our peaks sibling. /// If none then the sibling of our peak is the peak to the left. - pub fn bag_the_rhs(&self, peak_pos: u64) -> Option { - let rhs = peaks(self.last_pos) + fn bag_the_rhs(&self, peak_pos: u64) -> Option { + let last_pos = self.unpruned_size(); + let rhs = peaks(last_pos) .into_iter() - .filter(|x| *x > peak_pos) - .filter_map(|x| self.backend.get_from_file(x)); + .filter(|&x| x > peak_pos) + .filter_map(|x| self.get_from_file(x)); let mut res = None; for peak in rhs.rev() { res = match res { None => Some(peak), - Some(rhash) => Some((peak, rhash).hash_with_index(self.unpruned_size())), + Some(rhash) => Some((peak, rhash).hash_with_index(last_pos)), } } res } + /// Returns a vec of the peaks of this MMR. + fn peaks(&self) -> Vec { + peaks(self.unpruned_size()) + .into_iter() + .filter_map(move |pi| { + // here we want to get from underlying hash file + // as the pos *may* have been "removed" + self.get_from_file(pi) + }) + .collect() + } + + /// Hashes of the peaks excluding `peak_pos`, where the rhs is bagged together + fn peak_path(&self, peak_pos: u64) -> Vec { + let rhs = self.bag_the_rhs(peak_pos); + let mut res = peaks(self.unpruned_size()) + .into_iter() + .filter(|&x| x < peak_pos) + .filter_map(|x| self.get_from_file(x)) + .collect::>(); + if let Some(rhs) = rhs { + res.push(rhs); + } + res.reverse(); + + res + } + /// Computes the root of the MMR. Find all the peaks in the current /// tree and "bags" them to get a single peak. - pub fn root(&self) -> Result { + fn root(&self) -> Result { if self.is_empty() { return Ok(ZERO_HASH); } let mut res = None; - for peak in self.peaks().rev() { + let peaks = self.peaks(); + for peak in peaks.into_iter().rev() { res = match res { None => Some(peak), Some(rhash) => Some((peak, rhash).hash_with_index(self.unpruned_size())), @@ -151,8 +124,9 @@ where } /// Build a Merkle proof for the element at the given position. - pub fn merkle_proof(&self, pos: u64) -> Result { - debug!("merkle_proof {}, last_pos {}", pos, self.last_pos); + fn merkle_proof(&self, pos: u64) -> Result { + let last_pos = self.unpruned_size(); + debug!("merkle_proof {}, last_pos {}", pos, last_pos); // check this pos is actually a leaf in the MMR if !is_leaf(pos) { @@ -163,9 +137,7 @@ where self.get_hash(pos) .ok_or_else(|| format!("no element at pos {}", pos))?; - let mmr_size = self.unpruned_size(); - - let family_branch = family_branch(pos, self.last_pos); + let family_branch = family_branch(pos, last_pos); let mut path = family_branch .iter() @@ -179,7 +151,59 @@ where path.append(&mut self.peak_path(peak_pos)); - Ok(MerkleProof { mmr_size, path }) + Ok(MerkleProof { + mmr_size: last_pos, + path, + }) + } +} + +/// Prunable Merkle Mountain Range implementation. All positions within the tree +/// start at 1 as they're postorder tree traversal positions rather than array +/// indices. +/// +/// Heavily relies on navigation operations within a binary tree. In particular, +/// all the implementation needs to keep track of the MMR structure is how far +/// we are in the sequence of nodes making up the MMR. +pub struct PMMR<'a, T, B> +where + T: PMMRable, + B: Backend, +{ + /// The last position in the PMMR + pub last_pos: u64, + backend: &'a mut B, + // only needed to parameterise Backend + _marker: marker::PhantomData, +} + +impl<'a, T, B> PMMR<'a, T, B> +where + T: PMMRable, + B: 'a + Backend, +{ + /// Build a new prunable Merkle Mountain Range using the provided backend. + pub fn new(backend: &'a mut B) -> PMMR<'_, T, B> { + PMMR { + backend, + last_pos: 0, + _marker: marker::PhantomData, + } + } + + /// Build a new prunable Merkle Mountain Range pre-initialized until + /// last_pos with the provided backend. + pub fn at(backend: &'a mut B, last_pos: u64) -> PMMR<'_, T, B> { + PMMR { + backend, + last_pos, + _marker: marker::PhantomData, + } + } + + /// Build a "readonly" view of this PMMR. + pub fn readonly_pmmr(&self) -> ReadonlyPMMR<'_, T, B> { + ReadonlyPMMR::at(&self.backend, self.last_pos) } /// Push a new element into the MMR. Computes new related peaks at @@ -258,43 +282,6 @@ where Ok(true) } - /// Get the hash at provided position in the MMR. - pub fn get_hash(&self, pos: u64) -> Option { - if pos > self.last_pos { - None - } else if is_leaf(pos) { - // If we are a leaf then get hash from the backend. - self.backend.get_hash(pos) - } else { - // If we are not a leaf get hash ignoring the remove log. - self.backend.get_from_file(pos) - } - } - - /// Get the data element at provided position in the MMR. - pub fn get_data(&self, pos: u64) -> Option { - if pos > self.last_pos { - // If we are beyond the rhs of the MMR return None. - None - } else if is_leaf(pos) { - // If we are a leaf then get data from the backend. - self.backend.get_data(pos) - } else { - // If we are not a leaf then return None as only leaves have data. - None - } - } - - /// Get the hash from the underlying MMR file - /// (ignores the remove log). - fn get_from_file(&self, pos: u64) -> Option { - if pos > self.last_pos { - None - } else { - self.backend.get_from_file(pos) - } - } - /// Walks all unpruned nodes in the MMR and revalidate all parent hashes pub fn validate(&self) -> Result<(), String> { // iterate on all parent nodes @@ -323,17 +310,6 @@ where Ok(()) } - /// Is the MMR empty? - pub fn is_empty(&self) -> bool { - self.last_pos == 0 - } - - /// Total size of the tree, including intermediary nodes and ignoring any - /// pruning. - pub fn unpruned_size(&self) -> u64 { - self.last_pos - } - /// Debugging utility to print information about the MMRs. Short version /// only prints the last 8 nodes. pub fn dump(&self, short: bool) { @@ -396,6 +372,63 @@ where } } +impl<'a, T, B> ReadablePMMR for PMMR<'a, T, B> +where + T: PMMRable, + B: 'a + Backend, +{ + type Item = T::E; + + fn get_hash(&self, pos: u64) -> Option { + if pos > self.last_pos { + None + } else if is_leaf(pos) { + // If we are a leaf then get hash from the backend. + self.backend.get_hash(pos) + } else { + // If we are not a leaf get hash ignoring the remove log. + self.backend.get_from_file(pos) + } + } + + fn get_data(&self, pos: u64) -> Option { + if pos > self.last_pos { + // If we are beyond the rhs of the MMR return None. + None + } else if is_leaf(pos) { + // If we are a leaf then get data from the backend. + self.backend.get_data(pos) + } else { + // If we are not a leaf then return None as only leaves have data. + None + } + } + + fn get_from_file(&self, pos: u64) -> Option { + if pos > self.last_pos { + None + } else { + self.backend.get_from_file(pos) + } + } + + fn unpruned_size(&self) -> u64 { + self.last_pos + } + + fn leaf_pos_iter(&self) -> Box + '_> { + self.backend.leaf_pos_iter() + } + + fn leaf_idx_iter(&self, from_idx: u64) -> Box + '_> { + self.backend.leaf_idx_iter(from_idx) + } + + fn n_unpruned_leaves(&self) -> u64 { + self.backend.n_unpruned_leaves() + } +} + /// Gets the postorder traversal index of all peaks in a MMR given its size. /// Starts with the top peak, which is always on the left /// side of the range, and navigates toward lower siblings toward the right diff --git a/core/src/core/pmmr/readonly_pmmr.rs b/core/src/core/pmmr/readonly_pmmr.rs index de2f24495f..bc2f0752e9 100644 --- a/core/src/core/pmmr/readonly_pmmr.rs +++ b/core/src/core/pmmr/readonly_pmmr.rs @@ -16,10 +16,10 @@ use std::marker; -use crate::core::hash::{Hash, ZERO_HASH}; -use crate::core::pmmr::pmmr::{bintree_rightmost, peaks}; +use crate::core::hash::Hash; +use crate::core::pmmr::pmmr::{bintree_rightmost, ReadablePMMR}; use crate::core::pmmr::{is_leaf, Backend}; -use crate::ser::{PMMRIndexHashable, PMMRable}; +use crate::ser::PMMRable; /// Readonly view of a PMMR. pub struct ReadonlyPMMR<'a, T, B> @@ -59,93 +59,6 @@ where } } - /// Get the data element at provided position in the MMR. - pub fn get_data(&self, pos: u64) -> Option { - if pos > self.last_pos { - // If we are beyond the rhs of the MMR return None. - None - } else if is_leaf(pos) { - // If we are a leaf then get data from the backend. - self.backend.get_data(pos) - } else { - // If we are not a leaf then return None as only leaves have data. - None - } - } - - /// Get the hash at provided position in the MMR. - pub fn get_hash(&self, pos: u64) -> Option { - if pos > self.last_pos { - None - } else if is_leaf(pos) { - // If we are a leaf then get hash from the backend. - self.backend.get_hash(pos) - } else { - // If we are not a leaf get hash ignoring the remove log. - self.backend.get_from_file(pos) - } - } - - /// Get the hash from the underlying MMR file, ignoring the leafset. - /// Some entries may have been removed from the leafset but not yet pruned from the file. - pub fn get_from_file(&self, pos: u64) -> Option { - if pos > self.last_pos { - None - } else { - self.backend.get_from_file(pos) - } - } - - /// Iterator over current (unpruned, unremoved) leaf positions. - pub fn leaf_pos_iter(&self) -> impl Iterator + '_ { - self.backend.leaf_pos_iter() - } - - /// Iterator over current (unpruned, unremoved) leaf insertion indices. - pub fn leaf_idx_iter(&self, from_idx: u64) -> impl Iterator + '_ { - self.backend.leaf_idx_iter(from_idx) - } - - /// Is the MMR empty? - pub fn is_empty(&self) -> bool { - self.last_pos == 0 - } - - /// Computes the root of the MMR. Find all the peaks in the current - /// tree and "bags" them to get a single peak. - pub fn root(&self) -> Hash { - if self.is_empty() { - return ZERO_HASH; - } - let mut res = None; - 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") - } - - /// Returns a vec of the peaks of this MMR. - pub fn peaks(&self) -> Vec { - let peaks_pos = peaks(self.last_pos); - peaks_pos - .into_iter() - .filter_map(|pi| { - // here we want to get from underlying hash file - // as the pos *may* have been "removed" - self.backend.get_from_file(pi) - }) - .collect() - } - - /// Total size of the tree, including intermediary nodes and ignoring any - /// pruning. - pub fn unpruned_size(&self) -> u64 { - self.last_pos - } - /// Helper function which returns un-pruned nodes from the insertion index /// forward /// returns last pmmr index returned along with data @@ -194,3 +107,60 @@ where return_vec } } + +impl<'a, T, B> ReadablePMMR for ReadonlyPMMR<'a, T, B> +where + T: PMMRable, + B: 'a + Backend, +{ + type Item = T::E; + + fn get_hash(&self, pos: u64) -> Option { + if pos > self.last_pos { + None + } else if is_leaf(pos) { + // If we are a leaf then get hash from the backend. + self.backend.get_hash(pos) + } else { + // If we are not a leaf get hash ignoring the remove log. + self.backend.get_from_file(pos) + } + } + + fn get_data(&self, pos: u64) -> Option { + if pos > self.last_pos { + // If we are beyond the rhs of the MMR return None. + None + } else if is_leaf(pos) { + // If we are a leaf then get data from the backend. + self.backend.get_data(pos) + } else { + // If we are not a leaf then return None as only leaves have data. + None + } + } + + fn get_from_file(&self, pos: u64) -> Option { + if pos > self.last_pos { + None + } else { + self.backend.get_from_file(pos) + } + } + + fn unpruned_size(&self) -> u64 { + self.last_pos + } + + fn leaf_pos_iter(&self) -> Box + '_> { + self.backend.leaf_pos_iter() + } + + fn leaf_idx_iter(&self, from_idx: u64) -> Box + '_> { + self.backend.leaf_idx_iter(from_idx) + } + + fn n_unpruned_leaves(&self) -> u64 { + self.backend.n_unpruned_leaves() + } +} diff --git a/core/tests/merkle_proof.rs b/core/tests/merkle_proof.rs index dfcf3432c7..f0e6d6bf8c 100644 --- a/core/tests/merkle_proof.rs +++ b/core/tests/merkle_proof.rs @@ -15,7 +15,7 @@ mod common; use self::core::core::merkle_proof::MerkleProof; -use self::core::core::pmmr::{VecBackend, PMMR}; +use self::core::core::pmmr::{ReadablePMMR, VecBackend, PMMR}; use self::core::ser::{self, PMMRIndexHashable}; use crate::common::TestElem; use grin_core as core; @@ -90,7 +90,7 @@ fn pmmr_merkle_proof() { assert_eq!(pmmr.get_hash(3).unwrap(), pos_2); assert_eq!(pmmr.root().unwrap(), pos_2); - assert_eq!(pmmr.peaks().collect::>(), [pos_2]); + assert_eq!(pmmr.peaks(), vec![pos_2]); // single peak, path with single sibling let proof = pmmr.merkle_proof(1).unwrap(); @@ -107,7 +107,7 @@ fn pmmr_merkle_proof() { assert_eq!(pmmr.get_hash(4).unwrap(), pos_3); assert_eq!(pmmr.root().unwrap(), (pos_2, pos_3).hash_with_index(4)); - assert_eq!(pmmr.peaks().collect::>(), [pos_2, pos_3]); + assert_eq!(pmmr.peaks(), vec![pos_2, pos_3]); let proof = pmmr.merkle_proof(1).unwrap(); assert_eq!(proof.path, vec![pos_1, pos_3]); diff --git a/core/tests/pmmr.rs b/core/tests/pmmr.rs index 1e02ea135d..c903a2dd50 100644 --- a/core/tests/pmmr.rs +++ b/core/tests/pmmr.rs @@ -15,7 +15,7 @@ mod common; use self::core::core::hash::Hash; -use self::core::core::pmmr::{self, VecBackend, PMMR}; +use self::core::core::pmmr::{self, ReadablePMMR, VecBackend, PMMR}; use self::core::ser::PMMRIndexHashable; use crate::common::TestElem; use chrono::prelude::Utc; @@ -278,7 +278,7 @@ fn pmmr_push_root() { pmmr.push(&elems[0]).unwrap(); pmmr.dump(false); let pos_0 = elems[0].hash_with_index(0); - assert_eq!(pmmr.peaks().collect::>(), vec![pos_0]); + assert_eq!(pmmr.peaks(), vec![pos_0]); assert_eq!(pmmr.root().unwrap(), pos_0); assert_eq!(pmmr.unpruned_size(), 1); @@ -287,7 +287,7 @@ fn pmmr_push_root() { pmmr.dump(false); let pos_1 = elems[1].hash_with_index(1); let pos_2 = (pos_0, pos_1).hash_with_index(2); - assert_eq!(pmmr.peaks().collect::>(), vec![pos_2]); + assert_eq!(pmmr.peaks(), vec![pos_2]); assert_eq!(pmmr.root().unwrap(), pos_2); assert_eq!(pmmr.unpruned_size(), 3); @@ -295,7 +295,7 @@ fn pmmr_push_root() { pmmr.push(&elems[2]).unwrap(); pmmr.dump(false); let pos_3 = elems[2].hash_with_index(3); - assert_eq!(pmmr.peaks().collect::>(), vec![pos_2, pos_3]); + assert_eq!(pmmr.peaks(), vec![pos_2, pos_3]); assert_eq!(pmmr.root().unwrap(), (pos_2, pos_3).hash_with_index(4)); assert_eq!(pmmr.unpruned_size(), 4); @@ -305,7 +305,7 @@ fn pmmr_push_root() { 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().collect::>(), vec![pos_6]); + assert_eq!(pmmr.peaks(), vec![pos_6]); assert_eq!(pmmr.root().unwrap(), pos_6); assert_eq!(pmmr.unpruned_size(), 7); @@ -313,7 +313,7 @@ fn pmmr_push_root() { pmmr.push(&elems[4]).unwrap(); pmmr.dump(false); let pos_7 = elems[4].hash_with_index(7); - assert_eq!(pmmr.peaks().collect::>(), vec![pos_6, pos_7]); + assert_eq!(pmmr.peaks(), vec![pos_6, pos_7]); assert_eq!(pmmr.root().unwrap(), (pos_6, pos_7).hash_with_index(8)); assert_eq!(pmmr.unpruned_size(), 8); @@ -321,14 +321,14 @@ fn pmmr_push_root() { pmmr.push(&elems[5]).unwrap(); let pos_8 = elems[5].hash_with_index(8); let pos_9 = (pos_7, pos_8).hash_with_index(9); - assert_eq!(pmmr.peaks().collect::>(), vec![pos_6, pos_9]); + assert_eq!(pmmr.peaks(), vec![pos_6, pos_9]); assert_eq!(pmmr.root().unwrap(), (pos_6, pos_9).hash_with_index(10)); assert_eq!(pmmr.unpruned_size(), 10); // seven elements pmmr.push(&elems[6]).unwrap(); let pos_10 = elems[6].hash_with_index(10); - assert_eq!(pmmr.peaks().collect::>(), vec![pos_6, pos_9, pos_10]); + assert_eq!(pmmr.peaks(), vec![pos_6, pos_9, pos_10]); assert_eq!( pmmr.root().unwrap(), (pos_6, (pos_9, pos_10).hash_with_index(11)).hash_with_index(11) @@ -342,14 +342,14 @@ fn pmmr_push_root() { 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().collect::>(), vec![pos_14]); + assert_eq!(pmmr.peaks(), vec![pos_14]); assert_eq!(pmmr.root().unwrap(), pos_14); assert_eq!(pmmr.unpruned_size(), 15); // nine elements pmmr.push(&elems[8]).unwrap(); let pos_15 = elems[8].hash_with_index(15); - assert_eq!(pmmr.peaks().collect::>(), vec![pos_14, pos_15]); + assert_eq!(pmmr.peaks(), vec![pos_14, pos_15]); assert_eq!(pmmr.root().unwrap(), (pos_14, pos_15).hash_with_index(16)); assert_eq!(pmmr.unpruned_size(), 16); } diff --git a/core/tests/vec_backend.rs b/core/tests/vec_backend.rs index d0d313cd95..4ac4419df4 100644 --- a/core/tests/vec_backend.rs +++ b/core/tests/vec_backend.rs @@ -14,7 +14,7 @@ mod common; -use self::core::core::pmmr::{VecBackend, PMMR}; +use self::core::core::pmmr::{ReadablePMMR, VecBackend, PMMR}; use crate::common::TestElem; use grin_core as core; diff --git a/store/tests/pmmr.rs b/store/tests/pmmr.rs index 7e4e9bbe01..1f16a29415 100644 --- a/store/tests/pmmr.rs +++ b/store/tests/pmmr.rs @@ -22,7 +22,7 @@ use chrono::prelude::Utc; use croaring::Bitmap; use crate::core::core::hash::DefaultHashable; -use crate::core::core::pmmr::{Backend, PMMR}; +use crate::core::core::pmmr::{Backend, ReadablePMMR, PMMR}; use crate::core::ser::{ Error, PMMRIndexHashable, PMMRable, ProtocolVersion, Readable, Reader, Writeable, Writer, };