diff --git a/src/mmr.rs b/src/mmr.rs index 91efb85..fcd32c4 100644 --- a/src/mmr.rs +++ b/src/mmr.rs @@ -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; @@ -291,6 +294,60 @@ impl> MerkleProof { 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) -> Result { + 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() - 1; + for (i, position) in prev_peaks_positions.iter().enumerate() { + if *position < current_peaks_positions[i] { + reverse_index = i; + break; + } + } + 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::(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, I: Iterator>( diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 5248702..81fb485 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,5 +1,6 @@ mod test_accumulate_headers; mod test_helper; +mod test_incremental; mod test_mmr; mod test_sequence; diff --git a/src/tests/test_incremental.rs b/src/tests/test_incremental.rs new file mode 100644 index 0000000..e47b172 --- /dev/null +++ b/src/tests/test_incremental.rs @@ -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 = (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 + ); + } +}