diff --git a/core/src/core/pmmr/backend.rs b/core/src/core/pmmr/backend.rs index 6118a8b2e..60c194a0c 100644 --- a/core/src/core/pmmr/backend.rs +++ b/core/src/core/pmmr/backend.rs @@ -46,6 +46,11 @@ pub trait Backend { /// (ignoring the remove log). fn get_from_file(&self, position: u64) -> Option; + /// Get hash for peak pos. + /// Optimized for reading peak hashes rather than arbitrary pos hashes. + /// Peaks can be assumed to not be compacted. + fn get_peak_from_file(&self, position: u64) -> Option; + /// Get a Data Element by original insertion position /// (ignoring the remove log). fn get_data_from_file(&self, position: u64) -> Option; diff --git a/core/src/core/pmmr/pmmr.rs b/core/src/core/pmmr/pmmr.rs index 082c18223..5492a24c6 100644 --- a/core/src/core/pmmr/pmmr.rs +++ b/core/src/core/pmmr/pmmr.rs @@ -39,6 +39,11 @@ pub trait ReadablePMMR { /// Get the hash from the underlying MMR file (ignores the remove log). fn get_from_file(&self, pos: u64) -> Option; + /// Get the hash for the provided peak pos. + /// Optimized for reading peak hashes rather than arbitrary pos hashes. + /// Peaks can be assumed to not be compacted. + fn get_peak_from_file(&self, pos: u64) -> Option; + /// Get the data element at provided position in the MMR (ignores the remove log). fn get_data_from_file(&self, pos: u64) -> Option; @@ -84,11 +89,7 @@ pub trait ReadablePMMR { 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) - }) + .filter_map(move |pi| self.get_peak_from_file(pi)) .collect() } @@ -98,7 +99,7 @@ pub trait ReadablePMMR { let mut res = peaks(self.unpruned_size()) .into_iter() .filter(|&x| x < peak_pos) - .filter_map(|x| self.get_from_file(x)) + .filter_map(|x| self.get_peak_from_file(x)) .collect::>(); if let Some(rhs) = rhs { res.push(rhs); @@ -227,7 +228,7 @@ where let left_sibling = pos + 1 - 2 * peak; let left_hash = self .backend - .get_from_file(left_sibling) + .get_peak_from_file(left_sibling) .ok_or("missing left sibling in tree, should not have been pruned")?; peak *= 2; pos += 1; @@ -414,6 +415,14 @@ where } } + fn get_peak_from_file(&self, pos: u64) -> Option { + if pos > self.last_pos { + None + } else { + self.backend.get_peak_from_file(pos) + } + } + fn get_data_from_file(&self, pos: u64) -> Option { if pos > self.last_pos { None diff --git a/core/src/core/pmmr/readonly_pmmr.rs b/core/src/core/pmmr/readonly_pmmr.rs index 536c6736d..a27c898ad 100644 --- a/core/src/core/pmmr/readonly_pmmr.rs +++ b/core/src/core/pmmr/readonly_pmmr.rs @@ -148,6 +148,14 @@ where } } + fn get_peak_from_file(&self, pos: u64) -> Option { + if pos > self.last_pos { + None + } else { + self.backend.get_peak_from_file(pos) + } + } + fn get_data_from_file(&self, pos: u64) -> Option { if pos > self.last_pos { None diff --git a/core/src/core/pmmr/vec_backend.rs b/core/src/core/pmmr/vec_backend.rs index 00fcad55d..e9d444e14 100644 --- a/core/src/core/pmmr/vec_backend.rs +++ b/core/src/core/pmmr/vec_backend.rs @@ -64,6 +64,10 @@ impl Backend for VecBackend { self.hashes.get(idx).cloned() } + fn get_peak_from_file(&self, position: u64) -> Option { + self.get_from_file(position) + } + fn get_data_from_file(&self, position: u64) -> Option { if let Some(data) = &self.data { let idx = usize::try_from(pmmr::n_leaves(position).saturating_sub(1)) diff --git a/store/src/pmmr.rs b/store/src/pmmr.rs index 6063e3e85..803d9ac45 100644 --- a/store/src/pmmr.rs +++ b/store/src/pmmr.rs @@ -94,6 +94,11 @@ impl Backend for PMMRBackend { self.hash_file.read(position - shift) } + fn get_peak_from_file(&self, position: u64) -> Option { + let shift = self.prune_list.get_shift(position); + self.hash_file.read(position - shift) + } + fn get_data_from_file(&self, position: u64) -> Option { if !pmmr::is_leaf(position) { return None; @@ -284,11 +289,15 @@ impl PMMRBackend { self.prune_list.is_pruned_root(pos) } + // Check if pos is pruned but not a pruned root itself. + // Checking for pruned root is faster so we do this check first. + // We can do a fast initial check as well - + // if its in the current leaf_set then we know it is not compacted. fn is_compacted(&self, pos: u64) -> bool { if self.leaf_set.includes(pos) { return false; } - self.is_pruned(pos) && !self.is_pruned_root(pos) + !self.is_pruned_root(pos) && self.is_pruned(pos) } /// Number of hashes in the PMMR stored by this backend. Only produces the