Skip to content

Commit

Permalink
optimization for reading peak hashes from backend file (#3575)
Browse files Browse the repository at this point in the history
  • Loading branch information
antiochp committed Feb 24, 2021
1 parent 7487ffd commit adddff9
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 8 deletions.
5 changes: 5 additions & 0 deletions core/src/core/pmmr/backend.rs
Expand Up @@ -46,6 +46,11 @@ pub trait Backend<T: PMMRable> {
/// (ignoring the remove log).
fn get_from_file(&self, position: u64) -> Option<Hash>;

/// 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<Hash>;

/// Get a Data Element by original insertion position
/// (ignoring the remove log).
fn get_data_from_file(&self, position: u64) -> Option<T::E>;
Expand Down
23 changes: 16 additions & 7 deletions core/src/core/pmmr/pmmr.rs
Expand Up @@ -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<Hash>;

/// 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<Hash>;

/// Get the data element at provided position in the MMR (ignores the remove log).
fn get_data_from_file(&self, pos: u64) -> Option<Self::Item>;

Expand Down Expand Up @@ -84,11 +89,7 @@ pub trait ReadablePMMR {
fn peaks(&self) -> Vec<Hash> {
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()
}

Expand All @@ -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::<Vec<_>>();
if let Some(rhs) = rhs {
res.push(rhs);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -414,6 +415,14 @@ where
}
}

fn get_peak_from_file(&self, pos: u64) -> Option<Hash> {
if pos > self.last_pos {
None
} else {
self.backend.get_peak_from_file(pos)
}
}

fn get_data_from_file(&self, pos: u64) -> Option<Self::Item> {
if pos > self.last_pos {
None
Expand Down
8 changes: 8 additions & 0 deletions core/src/core/pmmr/readonly_pmmr.rs
Expand Up @@ -148,6 +148,14 @@ where
}
}

fn get_peak_from_file(&self, pos: u64) -> Option<Hash> {
if pos > self.last_pos {
None
} else {
self.backend.get_peak_from_file(pos)
}
}

fn get_data_from_file(&self, pos: u64) -> Option<Self::Item> {
if pos > self.last_pos {
None
Expand Down
4 changes: 4 additions & 0 deletions core/src/core/pmmr/vec_backend.rs
Expand Up @@ -64,6 +64,10 @@ impl<T: PMMRable> Backend<T> for VecBackend<T> {
self.hashes.get(idx).cloned()
}

fn get_peak_from_file(&self, position: u64) -> Option<Hash> {
self.get_from_file(position)
}

fn get_data_from_file(&self, position: u64) -> Option<T::E> {
if let Some(data) = &self.data {
let idx = usize::try_from(pmmr::n_leaves(position).saturating_sub(1))
Expand Down
11 changes: 10 additions & 1 deletion store/src/pmmr.rs
Expand Up @@ -94,6 +94,11 @@ impl<T: PMMRable> Backend<T> for PMMRBackend<T> {
self.hash_file.read(position - shift)
}

fn get_peak_from_file(&self, position: u64) -> Option<Hash> {
let shift = self.prune_list.get_shift(position);
self.hash_file.read(position - shift)
}

fn get_data_from_file(&self, position: u64) -> Option<T::E> {
if !pmmr::is_leaf(position) {
return None;
Expand Down Expand Up @@ -284,11 +289,15 @@ impl<T: PMMRable> PMMRBackend<T> {
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
Expand Down

0 comments on commit adddff9

Please sign in to comment.