Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: verify a old root and all incremental items after it #35

Merged
merged 2 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion src/mmr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@

use crate::borrow::Cow;
use crate::collections::VecDeque;
use crate::helper::{get_peak_map, get_peaks, parent_offset, pos_height_in_tree, sibling_offset};
use crate::helper::{
get_peak_map, get_peaks, leaf_index_to_mmr_size, leaf_index_to_pos, parent_offset,
pos_height_in_tree, sibling_offset,
};
use crate::mmr_store::{MMRBatch, MMRStoreReadOps, MMRStoreWriteOps};
use crate::vec;
use crate::vec::Vec;
Expand Down Expand Up @@ -291,6 +294,63 @@ impl<T: Clone + PartialEq, M: Merge<Item = T>> MerkleProof<T, M> {
self.calculate_root(leaves)
.map(|calculated_root| calculated_root == root)
}

/// Verifies a old root and all incremental leaves.
///
/// If this method returns `true`, it means the following assertion are true:
/// - The old root could be generated in the history of the current MMR.
/// - All incremental leaves are on the current MMR.
/// - The MMR, which could generate the old root, appends all incremental leaves, becomes the
/// current MMR.
pub fn verify_incremental(&self, root: T, prev_root: T, incremental: Vec<T>) -> Result<bool> {
let current_leaves_count = get_peak_map(self.mmr_size);
if current_leaves_count <= incremental.len() as u64 {
return Err(Error::CorruptedProof);
}
// Test if previous root is correct.
let prev_leaves_count = current_leaves_count - incremental.len() as u64;
let prev_peaks_positions = {
let prev_index = prev_leaves_count - 1;
let prev_mmr_size = leaf_index_to_mmr_size(prev_index);
let prev_peaks_positions = get_peaks(prev_mmr_size);
if prev_peaks_positions.len() != self.proof.len() {
return Err(Error::CorruptedProof);
}
prev_peaks_positions
};
let current_peaks_positions = get_peaks(self.mmr_size);

let mut reverse_index = prev_peaks_positions.len();
for (i, position) in prev_peaks_positions.iter().enumerate() {
if *position < current_peaks_positions[i] {
reverse_index = i;
break;
}
}
if reverse_index == prev_peaks_positions.len() {
reverse_index = prev_peaks_positions.len() - 1;
}
quake marked this conversation as resolved.
Show resolved Hide resolved
let mut prev_peaks: Vec<_> = self.proof_items().to_vec();
let mut reverse_peaks = prev_peaks.split_off(reverse_index);
reverse_peaks.reverse();
prev_peaks.extend(reverse_peaks);

let calculated_prev_root = bagging_peaks_hashes::<T, M>(prev_peaks)?;
if calculated_prev_root != prev_root {
return Ok(false);
}

// Test if incremental leaves are correct.
let leaves = incremental
.into_iter()
.enumerate()
.map(|(index, leaf)| {
let pos = leaf_index_to_pos(prev_leaves_count + index as u64);
(pos, leaf)
})
.collect();
self.verify(root, leaves)
}
}

fn calculate_peak_root<'a, T: 'a, M: Merge<Item = T>, I: Iterator<Item = &'a T>>(
Expand Down
1 change: 1 addition & 0 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod test_accumulate_headers;
mod test_helper;
mod test_incremental;
mod test_mmr;
mod test_sequence;

Expand Down
51 changes: 51 additions & 0 deletions src/tests/test_incremental.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use proptest::proptest;

use super::{MergeNumberHash, NumberHash};
use crate::util::{MemMMR, MemStore};

proptest! {
#[test]
fn test_incremental(start in 1u32..500, steps in 1usize..50, turns in 10usize..20) {
test_incremental_with_params(start, steps, turns);
}
}

fn test_incremental_with_params(start: u32, steps: usize, turns: usize) {
let store = MemStore::default();
let mut mmr = MemMMR::<_, MergeNumberHash>::new(0, &store);

let mut curr = 0;

let _positions: Vec<u64> = (0u32..start)
.map(|_| {
let pos = mmr.push(NumberHash::from(curr)).unwrap();
curr += 1;
pos
})
.collect();
mmr.commit().expect("commit changes");

for turn in 0..turns {
let prev_root = mmr.get_root().expect("get root");
let (positions, leaves) = (0..steps).fold(
(Vec::new(), Vec::new()),
|(mut positions, mut leaves), _| {
let leaf = NumberHash::from(curr);
let pos = mmr.push(leaf.clone()).unwrap();
curr += 1;
positions.push(pos);
leaves.push(leaf);
(positions, leaves)
},
);
mmr.commit().expect("commit changes");
let proof = mmr.gen_proof(positions).expect("gen proof");
let root = mmr.get_root().expect("get root");
let result = proof.verify_incremental(root, prev_root, leaves).unwrap();
assert!(
result,
"start: {}, steps: {}, turn: {}, curr: {}",
start, steps, turn, curr
);
}
}