47 Commits

Author SHA1 Message Date
Quake Wang c0c9263122 Merge pull request #45 from eval-exec/exec/bump-0.7.0
bump version to 0.7.0
2025-03-11 13:43:10 +09:00
Eval EXEC 0ab3dac108 bump version to 0.7.0 2025-03-11 12:23:05 +08:00
zhangsoledad 25c5d823a0 Merge pull request #42 from eval-exec/exec/upgrade-rust-2024
Rust Toolchain: Upgrade to rust edition 2024
2025-03-11 04:01:34 +00:00
Eval EXEC 5c70a2da40 cargo: set MSRV to 1.85.0, since we use rust 2024 edition 2025-03-11 11:05:32 +08:00
Quake Wang ea53d5cd45 Merge pull request #44 from eval-exec/exec/bump-0.6.1
bump version to 0.6.1
2025-03-11 12:02:53 +09:00
Eval EXEC ac82e869e3 execute cargo fmt --all 2025-03-11 10:56:54 +08:00
Eval EXEC 6099da18fa rust-toolchain: upgrade rust 2024 2025-03-11 10:56:53 +08:00
Eval EXEC ee499a9ef0 clippy: Using mem::take is faster as it avoids the allocation: https://rust-lang.github.io/rust-clippy/master/index.html#drain_collect
cargo clippy  --all --all-features --all-targets
warning: you seem to be trying to move all elements into a new `Vec`
   --> src/mmr.rs:501:5
    |
501 |     v.drain(..).collect()
    |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(v)`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#drain_collect
    = note: `#[warn(clippy::drain_collect)]` on by default

Signed-off-by: Eval EXEC <execvy@gmail.com>
2025-03-11 10:55:43 +08:00
Eval EXEC 6e2e9450a2 rust-toolchain: upgrade rust edition to 2021
Signed-off-by: Eval EXEC <execvy@gmail.com>
2025-03-11 10:55:42 +08:00
Eval EXEC dcf3ff85db bump version to 0.6.1 2025-03-11 10:48:30 +08:00
Quake Wang e049986f9c Merge pull request #43 from eval-exec/exec/msrv
cargo: set MSRV to 1.60.0
2025-03-11 11:41:26 +09:00
Eval EXEC 29afe7ad15 cargo: set MSRV to 1.60.0 2025-03-10 16:29:41 +08:00
Quake Wang 5c33c7af57 Merge pull request #41 from dlachaume/fix-typo
fix: typo in error message for invalid leaves
2025-03-04 11:26:47 +09:00
Damien Lachaume becf6e3430 fix: typo in error message for invalid leaves 2025-03-03 15:11:25 +01:00
Quake Wang 20c1987d8d Merge pull request #40 from Lederstrumpf/Lederstrumpf-add-license
Add explicit MIT license
2024-12-11 18:29:13 +09:00
Robert Hambrock 246da2661d Create LICENSE 2024-09-11 11:23:07 +02:00
jjy 9e77d3ef81 Merge pull request #37 from yangby-cryptape/release/v0.6.0
chore: bump version to v0.6.0
2023-07-19 12:52:14 +00:00
Boyu Yang 4d3d4fa0c5 chore: bump version to v0.6.0 2023-07-18 15:25:53 +08:00
jjy e68de73d6c Merge pull request #35 from yangby-cryptape/pr/verify-incremental
feat: verify a old root and all incremental items after it
2023-07-18 06:47:35 +00:00
Quake Wang 34a6cbb8d2 chore: reverse peaks if needed 2023-07-18 08:21:26 +09:00
Boyu Yang 91e624f60f feat: verify a old root and all incremental items after it 2023-07-16 02:24:15 +08:00
Quake Wang 8206b586c3 Merge pull request #30 from quake/quake/remove-clone
perf: remove clone in calculate_peak_root fn
2023-03-13 01:13:44 +00:00
quake 38b2619f1b perf: remove clone in calculate_peak_root fn 2023-03-10 17:43:03 +09:00
jjy cb541632a3 Merge pull request #29 from quake/quake/mmr-commit
refactor: use mut ref in mmr#commit
2023-03-07 01:05:33 +00:00
Quake Wang 6494cd2dda chore: fix comment typo 2023-03-03 11:29:24 +08:00
quake c5bfa3d2a5 perf: tweak peak related calculation 2023-03-03 11:29:24 +08:00
quake fef46e0d06 refactor: use mut ref in mmr#commit 2023-03-02 16:05:23 +09:00
Quake Wang 7a831abae2 Merge pull request #26 from quake/quake/split-store-trait
refactor: split MMRStore trait
2023-02-23 10:06:09 +09:00
quake 61c85c47c9 refactor: split MMRStore trait 2023-02-21 19:35:57 +09:00
Quake Wang 7f47ea585f Merge pull request #25 from yangby-cryptape/pr/release-v0.5.2
chore: bump version to v0.5.2
2022-10-20 15:53:20 +09:00
Boyu Yang fbb525c76b chore: bump version to v0.5.2
- chore(deps): bump all dependencies to their latest versions
- chore: fix a clippy error
2022-10-20 10:26:55 +08:00
Quake Wang dfa58c4b43 Merge pull request from GHSA-vj82-c7x7-rfgc
mmr batch proof verification vulnerability fixes
2022-09-28 14:08:32 +09:00
Robert Hambrock 8e333a2329 strictly generate and verify proofs for leaves 2022-09-28 04:51:54 +02:00
Quake Wang 122e6e4e46 Merge pull request #24 from yangby-cryptape/pr/release-v0.5.1
chore: bump version to v0.5.1
2022-09-05 21:49:02 +09:00
Boyu Yang 8f649d99c9 chore: bump version to v0.5.1 2022-09-05 18:16:25 +08:00
Boyu Yang 88515cbb70 Merge pull request #22 from yangby-cryptape/pr/fix-readme
fix: grammar, typos and mistakes in the README.md
2022-09-05 18:10:57 +08:00
Boyu Yang 195250e045 fix: grammar, typos and mistakes in the README.md 2022-09-01 14:14:26 +08:00
Boyu Yang 36c9ad89ee Merge pull request #23 from nervosnetwork/quake/dedup-leaves
fix: should dedup leaves when generating proof
2022-09-01 10:25:24 +08:00
quake 7f17ffb33a fix: should dedup leaves when generating proof 2022-08-31 19:55:34 +09:00
Quake Wang be4f439efd Merge pull request #21 from rootulp/patch-1
fix: grammar
2022-08-17 09:08:01 +09:00
Rootul Patel cdd40cb4a8 fix: grammar 2022-08-16 15:18:45 -04:00
Quake Wang fb7605c6da Merge pull request #20 from yangby-cryptape/pr/release-v0.5.0
chore: bump version to v0.5.0
2022-08-02 12:19:56 +09:00
Boyu Yang 92f32c6751 chore: bump version to v0.5.0 2022-08-02 10:20:38 +08:00
Quake Wang 0e3b02c38f Merge pull request #19 from zhangsoledad/zhangsoledad/remove-trait-bounds-on-struct
chore: remove trait bounds on struct
2022-08-02 09:07:35 +09:00
zhangsoledad d6cfbe6454 chore: remove trait bounds on struct 2022-08-01 19:48:32 +08:00
Quake Wang c8ae8dad9d Merge pull request #18 from koushiro/update-deps
Update some dependencies
2022-07-22 16:58:13 +09:00
koushiro 7796272361 Update some dependencies
Signed-off-by: koushiro <koushiro.cqx@gmail.com>
2022-07-22 12:26:56 +08:00
17 changed files with 525 additions and 235 deletions
+11 -10
View File
@@ -1,8 +1,9 @@
[package]
name = "ckb-merkle-mountain-range"
version = "0.4.0"
version = "0.7.0"
authors = ["Nervos Core Dev <dev@nervos.org>"]
edition = "2018"
edition = "2024"
rust-version = "1.85.0"
license = "MIT"
description = "A generalized merkle mountain range implementation"
repository = "https://github.com/nervosnetwork/merkle-mountain-range"
@@ -12,16 +13,16 @@ default = ["std"]
std = []
[dependencies]
cfg-if = "0.1"
cfg-if = "1.0"
[dev-dependencies]
faster-hex = "0.3"
criterion = "0.3"
rand = "0.6"
proptest = "0.9.4"
lazy_static = "1.3.0"
bytes = "0.4"
blake2b-rs = "0.1.4"
faster-hex = "0.8.0"
criterion = "0.5.1"
rand = "0.8.5"
proptest = "1.2.0"
lazy_static = "1.4.0"
bytes = "1.4.0"
blake2b-rs = "0.2.0"
[[bench]]
name = "mmr_benchmark"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 nervosnetwork
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+9 -10
View File
@@ -3,8 +3,6 @@
A generalized merkle mountain range implementation.
**Notice** this library is not stable yet, API and proof format may changes. Make sure you know what you do before using this library.
## Features
* Leaves accumulation
@@ -26,7 +24,8 @@ A generalized merkle mountain range implementation.
```
In MMR, we use the insertion order to reference leaves and nodes.
we inserting a new leaf to MMR by the following:
We insert a new leaf to MMR by the following:
1. insert leaf or node to next position.
2. if the current position has a left sibling, we merge the left and right nodes to produce a new parent node, then go back to step 1 to insert the node.
@@ -36,9 +35,9 @@ For example, we insert a leaf to the example MMR:
1. insert leaf to next position: `19`.
2. now check the left sibling `18` and calculate parent node: `merge(mmr[18], mmr[19])`.
3. insert parent node to position `20`.
4. the node `20` also has a left sibling `17`, calculate parent node: `merge(mmr[17], mmr[20])`.
4. since the node `20` also has a left sibling `17`, calculate parent node: `merge(mmr[17], mmr[20])`.
5. insert new node to next position `21`.
6. the node `21` have no left sibling, complete the insertion.
6. since the node `21` have no left sibling, complete the insertion.
Example MMR after insertion of a new leaf:
@@ -56,17 +55,17 @@ Example MMR after insertion of a new leaf:
An MMR is constructed by one or more sub merkle trees (or mountains). Each sub merkle tree's root is a peak in MMR, we calculate the MMR root by bagging these peaks from right to left.
For example, we have a MMR with 3 peaks: `14, 17, 18`, we bagging thses peaks from right to left to get the root: `merge(merge(mmr[18], mmr[17]), mmr[14])`.
For example, in the 11 leaf MMR we have 3 peaks: `14, 17, 18`, we bag these peaks from right to left to get the root: `merge(mmr[14], merge(mmr[17], mmr[18]))`.
## Merkle proof
The merkle proof is an array of hashes constructed by the follows parts:
The merkle proof is an array of hashes constructed with the following parts:
1. A merkle proof from the leaf's sibling to the peak that contains the leaf.
2. A hash that bagging all right-hand side peaks, skip this part if no right-hand peaks.
3. Hashes of all left-hand peaks, the from right to left, skip this part if no left-hand peaks.
2. A hash that bags all right-hand side peaks, skip this part if no right-hand peaks.
3. Hashes of all left-hand peaks from right to left, skip this part if no left-hand peaks.
We can reconstruct the merkle root from the proofs. Pre calculate the peaks positions from the size of MMR may help us do the bagging.
We can reconstruct the merkle root from the proofs. Pre-calculating the peak positions from the size of MMR may help us do the bagging.
## References
+3 -3
View File
@@ -4,13 +4,13 @@ use criterion::Criterion;
use ckb_merkle_mountain_range::{leaf_index_to_mmr_size, leaf_index_to_pos};
use rand::{thread_rng, Rng};
use rand::{Rng, thread_rng};
fn bench(c: &mut Criterion) {
c.bench_function("left_index_to_pos", |b| {
let mut rng = thread_rng();
b.iter(|| {
let leaf_index = rng.gen_range(50_000_000_000, 70_000_000_000);
let leaf_index = rng.gen_range(50_000_000_000..70_000_000_000);
leaf_index_to_pos(leaf_index);
});
});
@@ -18,7 +18,7 @@ fn bench(c: &mut Criterion) {
c.bench_function("left_index_to_mmr_size", |b| {
let mut rng = thread_rng();
b.iter(|| {
let leaf_index = rng.gen_range(50_000_000_000, 70_000_000_000);
let leaf_index = rng.gen_range(50_000_000_000..70_000_000_000);
leaf_index_to_mmr_size(leaf_index);
});
});
+1 -1
View File
@@ -4,7 +4,7 @@ extern crate criterion;
use criterion::{BenchmarkId, Criterion};
use bytes::Bytes;
use ckb_merkle_mountain_range::{util::MemStore, Error, MMRStore, Merge, Result, MMR};
use ckb_merkle_mountain_range::{Error, MMR, MMRStoreReadOps, Merge, Result, util::MemStore};
use rand::{seq::SliceRandom, thread_rng};
use std::convert::TryFrom;
+4 -1
View File
@@ -7,6 +7,8 @@ pub enum Error {
StoreError(crate::string::String),
/// proof items is not enough to build a tree
CorruptedProof,
/// tried to verify proof of a non-leaf
NodeProofsNotSupported,
/// The leaves is an empty list, or beyond the mmr range
GenProofForInvalidLeaves,
@@ -22,7 +24,8 @@ impl core::fmt::Display for Error {
InconsistentStore => write!(f, "Inconsistent store")?,
StoreError(msg) => write!(f, "Store error {}", msg)?,
CorruptedProof => write!(f, "Corrupted proof")?,
GenProofForInvalidLeaves => write!(f, "Generate proof ofr invalid leaves")?,
NodeProofsNotSupported => write!(f, "Tried to verify membership of a non-leaf")?,
GenProofForInvalidLeaves => write!(f, "Generate proof for invalid leaves")?,
MergeError(msg) => write!(f, "Merge error {}", msg)?,
}
Ok(())
+76 -53
View File
@@ -1,3 +1,4 @@
use crate::vec;
use crate::vec::Vec;
pub fn leaf_index_to_pos(index: u64) -> u64 {
@@ -15,75 +16,97 @@ pub fn leaf_index_to_mmr_size(index: u64) -> u64 {
2 * leaves_count - peak_count
}
pub fn pos_height_in_tree(mut pos: u64) -> u32 {
pos += 1;
fn all_ones(num: u64) -> bool {
num != 0 && num.count_zeros() == num.leading_zeros()
}
fn jump_left(pos: u64) -> u64 {
let bit_length = 64 - pos.leading_zeros();
let most_significant_bits = 1 << (bit_length - 1);
pos - (most_significant_bits - 1)
pub fn pos_height_in_tree(mut pos: u64) -> u8 {
if pos == 0 {
return 0;
}
while !all_ones(pos) {
pos = jump_left(pos)
let mut peak_size = u64::MAX >> pos.leading_zeros();
while peak_size > 0 {
if pos >= peak_size {
pos -= peak_size;
}
peak_size >>= 1;
}
64 - pos.leading_zeros() - 1
pos as u8
}
pub fn parent_offset(height: u32) -> u64 {
pub fn parent_offset(height: u8) -> u64 {
2 << height
}
pub fn sibling_offset(height: u32) -> u64 {
pub fn sibling_offset(height: u8) -> u64 {
(2 << height) - 1
}
pub fn get_peaks(mmr_size: u64) -> Vec<u64> {
let mut pos_s = Vec::new();
let (mut height, mut pos) = left_peak_height_pos(mmr_size);
pos_s.push(pos);
while height > 0 {
let peak = match get_right_peak(height, pos, mmr_size) {
Some(peak) => peak,
None => break,
};
height = peak.0;
pos = peak.1;
pos_s.push(pos);
/// Returns the height of the peaks in the mmr, presented by a bitmap.
/// for example, for a mmr with 11 leaves, the mmr_size is 19, it will return 0b1011.
/// 0b1011 indicates that the left peaks are at height 0, 1 and 3.
/// 14
/// / \
/// 6 13
/// / \ / \
/// 2 5 9 12 17
/// / \ / \ / \ / \ / \
/// 0 1 3 4 7 8 10 11 15 16 18
///
/// please note that when the mmr_size is invalid, it will return the bitmap of the last valid mmr.
/// in the below example, the mmr_size is 6, but it's not a valid mmr, it will return 0b11.
/// 2 5
/// / \ / \
/// 0 1 3 4
pub fn get_peak_map(mmr_size: u64) -> u64 {
if mmr_size == 0 {
return 0;
}
pos_s
}
fn get_right_peak(mut height: u32, mut pos: u64, mmr_size: u64) -> Option<(u32, u64)> {
// move to right sibling pos
pos += sibling_offset(height);
// loop until we find a pos in mmr
while pos > mmr_size - 1 {
if height == 0 {
return None;
let mut pos = mmr_size;
let mut peak_size = u64::MAX >> pos.leading_zeros();
let mut peak_map = 0;
while peak_size > 0 {
peak_map <<= 1;
if pos >= peak_size {
pos -= peak_size;
peak_map |= 1;
}
// move to left child
pos -= parent_offset(height - 1);
height -= 1;
peak_size >>= 1;
}
Some((height, pos))
peak_map
}
fn get_peak_pos_by_height(height: u32) -> u64 {
(1 << (height + 1)) - 2
}
fn left_peak_height_pos(mmr_size: u64) -> (u32, u64) {
let mut height = 1;
let mut prev_pos = 0;
let mut pos = get_peak_pos_by_height(height);
while pos < mmr_size {
height += 1;
prev_pos = pos;
pos = get_peak_pos_by_height(height);
/// Returns the pos of the peaks in the mmr.
/// for example, for a mmr with 11 leaves, the mmr_size is 19, it will return [14, 17, 18].
/// 14
/// / \
/// 6 13
/// / \ / \
/// 2 5 9 12 17
/// / \ / \ / \ / \ / \
/// 0 1 3 4 7 8 10 11 15 16 18
///
/// please note that when the mmr_size is invalid, it will return the peaks of the last valid mmr.
/// in the below example, the mmr_size is 6, but it's not a valid mmr, it will return [2, 3].
/// 2 5
/// / \ / \
/// 0 1 3 4
pub fn get_peaks(mmr_size: u64) -> Vec<u64> {
if mmr_size == 0 {
return vec![];
}
(height - 1, prev_pos)
let leading_zeros = mmr_size.leading_zeros();
let mut pos = mmr_size;
let mut peak_size = u64::MAX >> leading_zeros;
let mut peaks = Vec::with_capacity(64 - leading_zeros as usize);
let mut peaks_sum = 0;
while peak_size > 0 {
if pos >= peak_size {
pos -= peak_size;
peaks.push(peaks_sum + peak_size - 1);
peaks_sum += peak_size;
}
peak_size >>= 1;
}
peaks
}
+2 -2
View File
@@ -12,8 +12,8 @@ pub mod util;
pub use error::{Error, Result};
pub use helper::{leaf_index_to_mmr_size, leaf_index_to_pos};
pub use merge::Merge;
pub use mmr::{MerkleProof, MMR};
pub use mmr_store::MMRStore;
pub use mmr::{MMR, MerkleProof};
pub use mmr_store::{MMRStoreReadOps, MMRStoreWriteOps};
cfg_if::cfg_if! {
if #[cfg(feature = "std")] {
+150 -74
View File
@@ -6,22 +6,26 @@
use crate::borrow::Cow;
use crate::collections::VecDeque;
use crate::helper::{get_peaks, parent_offset, pos_height_in_tree, sibling_offset};
use crate::mmr_store::{MMRBatch, MMRStore};
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;
use crate::{Error, Merge, Result};
use core::fmt::Debug;
use core::marker::PhantomData;
use core::mem;
#[allow(clippy::upper_case_acronyms)]
pub struct MMR<T, M, S: MMRStore<T>> {
pub struct MMR<T, M, S> {
mmr_size: u64,
batch: MMRBatch<T, S>,
merge: PhantomData<M>,
}
impl<'a, T: Clone + PartialEq + Debug, M: Merge<Item = T>, S: MMRStore<T>> MMR<T, M, S> {
impl<T, M, S> MMR<T, M, S> {
pub fn new(mmr_size: u64, store: S) -> Self {
MMR {
mmr_size,
@@ -30,16 +34,6 @@ impl<'a, T: Clone + PartialEq + Debug, M: Merge<Item = T>, S: MMRStore<T>> MMR<T
}
}
// find internal MMR elem, the pos must exists, otherwise a error will return
fn find_elem<'b>(&self, pos: u64, hashes: &'b [T]) -> Result<Cow<'b, T>> {
let pos_offset = pos.checked_sub(self.mmr_size);
if let Some(elem) = pos_offset.and_then(|i| hashes.get(i as usize)) {
return Ok(Cow::Borrowed(elem));
}
let elem = self.batch.get_elem(pos)?.ok_or(Error::InconsistentStore)?;
Ok(Cow::Owned(elem))
}
pub fn mmr_size(&self) -> u64 {
self.mmr_size
}
@@ -48,24 +42,41 @@ impl<'a, T: Clone + PartialEq + Debug, M: Merge<Item = T>, S: MMRStore<T>> MMR<T
self.mmr_size == 0
}
pub fn batch(&self) -> &MMRBatch<T, S> {
&self.batch
}
pub fn store(&self) -> &S {
self.batch.store()
}
}
impl<T: Clone + PartialEq, M: Merge<Item = T>, S: MMRStoreReadOps<T>> MMR<T, M, S> {
// find internal MMR elem, the pos must exists, otherwise a error will return
fn find_elem<'b>(&self, pos: u64, hashes: &'b [T]) -> Result<Cow<'b, T>> {
let pos_offset = pos.checked_sub(self.mmr_size);
if let Some(elem) = pos_offset.and_then(|i| hashes.get(i as usize)) {
return Ok(Cow::Borrowed(elem));
}
let elem = self.batch.get_elem(pos)?.ok_or(Error::InconsistentStore)?;
Ok(Cow::Owned(elem))
}
// push a element and return position
pub fn push(&mut self, elem: T) -> Result<u64> {
let mut elems: Vec<T> = Vec::new();
// position of new elem
let mut elems = vec![elem];
let elem_pos = self.mmr_size;
elems.push(elem);
let mut height = 0u32;
let mut pos = elem_pos;
// continue to merge tree node if next pos heigher than current
while pos_height_in_tree(pos + 1) > height {
let peak_map = get_peak_map(self.mmr_size);
let mut pos = self.mmr_size;
let mut peak = 1;
while (peak_map & peak) != 0 {
peak <<= 1;
pos += 1;
let left_pos = pos - parent_offset(height);
let right_pos = left_pos + sibling_offset(height);
let left_pos = pos - peak;
let left_elem = self.find_elem(left_pos, &elems)?;
let right_elem = self.find_elem(right_pos, &elems)?;
let parent_elem = M::merge(&left_elem, &right_elem)?;
let right_elem = elems.last().expect("checked");
let parent_elem = M::merge(&left_elem, right_elem)?;
elems.push(parent_elem);
height += 1
}
// store hashes
self.batch.append(elem_pos, elems);
@@ -127,12 +138,17 @@ impl<'a, T: Clone + PartialEq + Debug, M: Merge<Item = T>, S: MMRStore<T>> MMR<T
return Ok(());
}
let mut queue: VecDeque<_> = pos_list.into_iter().map(|pos| (pos, 0u32)).collect();
let mut queue: VecDeque<_> = pos_list.into_iter().map(|pos| (pos, 0)).collect();
// Generate sub-tree merkle proof for positions
while let Some((pos, height)) = queue.pop_front() {
debug_assert!(pos <= peak_pos);
if pos == peak_pos {
break;
if queue.is_empty() {
break;
} else {
return Err(Error::NodeProofsNotSupported);
}
}
// calculate sibling
@@ -177,8 +193,12 @@ impl<'a, T: Clone + PartialEq + Debug, M: Merge<Item = T>, S: MMRStore<T>> MMR<T
if self.mmr_size == 1 && pos_list == [0] {
return Ok(MerkleProof::new(self.mmr_size, Vec::new()));
}
// ensure positions is sorted
if pos_list.iter().any(|pos| pos_height_in_tree(*pos) > 0) {
return Err(Error::NodeProofsNotSupported);
}
// ensure positions are sorted and unique
pos_list.sort_unstable();
pos_list.dedup();
let peaks = get_peaks(self.mmr_size);
let mut proof: Vec<T> = Vec::new();
// generate merkle proof for each peaks
@@ -205,8 +225,10 @@ impl<'a, T: Clone + PartialEq + Debug, M: Merge<Item = T>, S: MMRStore<T>> MMR<T
Ok(MerkleProof::new(self.mmr_size, proof))
}
}
pub fn commit(self) -> Result<()> {
impl<T, M, S: MMRStoreWriteOps<T>> MMR<T, M, S> {
pub fn commit(&mut self) -> Result<()> {
self.batch.commit()
}
}
@@ -218,7 +240,7 @@ pub struct MerkleProof<T, M> {
merge: PhantomData<M>,
}
impl<T: PartialEq + Debug + Clone, M: Merge<Item = T>> MerkleProof<T, M> {
impl<T: Clone + PartialEq, M: Merge<Item = T>> MerkleProof<T, M> {
pub fn new(mmr_size: u64, proof: Vec<T>) -> Self {
MerkleProof {
mmr_size,
@@ -273,80 +295,141 @@ impl<T: PartialEq + Debug + Clone, 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() - 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::<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 + PartialEq + Debug + Clone,
M: Merge<Item = T>,
I: Iterator<Item = &'a T>,
>(
fn calculate_peak_root<'a, T: 'a, M: Merge<Item = T>, I: Iterator<Item = &'a T>>(
leaves: Vec<(u64, T)>,
peak_pos: u64,
proof_iter: &mut I,
) -> Result<T> {
debug_assert!(!leaves.is_empty(), "can't be empty");
// (position, hash, height)
let mut queue: VecDeque<_> = leaves
.into_iter()
.map(|(pos, item)| (pos, item, 0u32))
.map(|(pos, item)| (pos, item, 0))
.collect();
// calculate tree root from each items
while let Some((pos, item, height)) = queue.pop_front() {
if pos == peak_pos {
// return root
return Ok(item);
if queue.is_empty() {
// return root once queue is consumed
return Ok(item);
} else {
return Err(Error::CorruptedProof);
}
}
// calculate sibling
let next_height = pos_height_in_tree(pos + 1);
let (sib_pos, parent_pos) = {
let (parent_pos, parent_item) = {
let sibling_offset = sibling_offset(height);
if next_height > height {
// implies pos is right sibling
(pos - sibling_offset, pos + 1)
let sib_pos = pos - sibling_offset;
let parent_pos = pos + 1;
let parent_item = if Some(&sib_pos) == queue.front().map(|(pos, _, _)| pos) {
let sibling_item = queue.pop_front().map(|(_, item, _)| item).unwrap();
M::merge(&sibling_item, &item)?
} else {
let sibling_item = proof_iter.next().ok_or(Error::CorruptedProof)?;
M::merge(sibling_item, &item)?
};
(parent_pos, parent_item)
} else {
// pos is left sibling
(pos + sibling_offset, pos + parent_offset(height))
let sib_pos = pos + sibling_offset;
let parent_pos = pos + parent_offset(height);
let parent_item = if Some(&sib_pos) == queue.front().map(|(pos, _, _)| pos) {
let sibling_item = queue.pop_front().map(|(_, item, _)| item).unwrap();
M::merge(&item, &sibling_item)?
} else {
let sibling_item = proof_iter.next().ok_or(Error::CorruptedProof)?;
M::merge(&item, sibling_item)?
};
(parent_pos, parent_item)
}
};
let sibling_item = if Some(&sib_pos) == queue.front().map(|(pos, _, _)| pos) {
queue.pop_front().map(|(_, item, _)| item).unwrap()
} else {
proof_iter.next().ok_or(Error::CorruptedProof)?.clone()
};
let parent_item = if next_height > height {
M::merge(&sibling_item, &item)
if parent_pos <= peak_pos {
queue.push_back((parent_pos, parent_item, height + 1))
} else {
M::merge(&item, &sibling_item)
}?;
if parent_pos < peak_pos {
queue.push_back((parent_pos, parent_item, height + 1));
} else {
return Ok(parent_item);
return Err(Error::CorruptedProof);
}
}
Err(Error::CorruptedProof)
}
fn calculate_peaks_hashes<
'a,
T: 'a + PartialEq + Debug + Clone,
M: Merge<Item = T>,
I: Iterator<Item = &'a T>,
>(
fn calculate_peaks_hashes<'a, T: 'a + Clone, M: Merge<Item = T>, I: Iterator<Item = &'a T>>(
mut leaves: Vec<(u64, T)>,
mmr_size: u64,
mut proof_iter: I,
) -> Result<Vec<T>> {
if leaves.iter().any(|(pos, _)| pos_height_in_tree(*pos) > 0) {
return Err(Error::NodeProofsNotSupported);
}
// special handle the only 1 leaf MMR
if mmr_size == 1 && leaves.len() == 1 && leaves[0].0 == 0 {
return Ok(leaves.into_iter().map(|(_pos, item)| item).collect());
}
// sort items by position
// ensure leaves are sorted and unique
leaves.sort_by_key(|(pos, _)| *pos);
leaves.dedup_by(|a, b| a.0 == b.0);
let peaks = get_peaks(mmr_size);
let mut peaks_hashes: Vec<T> = Vec::with_capacity(peaks.len() + 1);
@@ -386,9 +469,7 @@ fn calculate_peaks_hashes<
Ok(peaks_hashes)
}
fn bagging_peaks_hashes<'a, T: 'a + PartialEq + Debug + Clone, M: Merge<Item = T>>(
mut peaks_hashes: Vec<T>,
) -> Result<T> {
fn bagging_peaks_hashes<T, M: Merge<Item = T>>(mut peaks_hashes: Vec<T>) -> Result<T> {
// bagging peaks
// bagging from right to left via hash(right, left).
while peaks_hashes.len() > 1 {
@@ -403,12 +484,7 @@ fn bagging_peaks_hashes<'a, T: 'a + PartialEq + Debug + Clone, M: Merge<Item = T
/// 1. sort items by position
/// 2. calculate root of each peak
/// 3. bagging peaks
fn calculate_root<
'a,
T: 'a + PartialEq + Debug + Clone,
M: Merge<Item = T>,
I: Iterator<Item = &'a T>,
>(
fn calculate_root<'a, T: 'a + Clone, M: Merge<Item = T>, I: Iterator<Item = &'a T>>(
leaves: Vec<(u64, T)>,
mmr_size: u64,
proof_iter: I,
@@ -423,5 +499,5 @@ fn take_while_vec<T, P: Fn(&T) -> bool>(v: &mut Vec<T>, p: P) -> Vec<T> {
return v.drain(..i).collect();
}
}
v.drain(..).collect()
mem::take(v)
}
+19 -12
View File
@@ -1,12 +1,12 @@
use crate::{vec::Vec, Result};
use crate::{Result, vec::Vec};
#[derive(Default)]
pub struct MMRBatch<Elem, Store: MMRStore<Elem>> {
pub struct MMRBatch<Elem, Store> {
memory_batch: Vec<(u64, Vec<Elem>)>,
store: Store,
}
impl<Elem: Clone, Store: MMRStore<Elem>> MMRBatch<Elem, Store> {
impl<Elem, Store> MMRBatch<Elem, Store> {
pub fn new(store: Store) -> Self {
MMRBatch {
memory_batch: Vec::new(),
@@ -18,6 +18,12 @@ impl<Elem: Clone, Store: MMRStore<Elem>> MMRBatch<Elem, Store> {
self.memory_batch.push((pos, elems));
}
pub fn store(&self) -> &Store {
&self.store
}
}
impl<Elem: Clone, Store: MMRStoreReadOps<Elem>> MMRBatch<Elem, Store> {
pub fn get_elem(&self, pos: u64) -> Result<Option<Elem>> {
for (start_pos, elems) in self.memory_batch.iter().rev() {
if pos < *start_pos {
@@ -30,20 +36,18 @@ impl<Elem: Clone, Store: MMRStore<Elem>> MMRBatch<Elem, Store> {
}
self.store.get_elem(pos)
}
}
pub fn commit(self) -> Result<()> {
let Self {
mut store,
memory_batch,
} = self;
for (pos, elems) in memory_batch {
store.append(pos, elems)?;
impl<Elem, Store: MMRStoreWriteOps<Elem>> MMRBatch<Elem, Store> {
pub fn commit(&mut self) -> Result<()> {
for (pos, elems) in self.memory_batch.drain(..) {
self.store.append(pos, elems)?;
}
Ok(())
}
}
impl<Elem, Store: MMRStore<Elem>> IntoIterator for MMRBatch<Elem, Store> {
impl<Elem, Store> IntoIterator for MMRBatch<Elem, Store> {
type Item = (u64, Vec<Elem>);
type IntoIter = crate::vec::IntoIter<Self::Item>;
@@ -52,7 +56,10 @@ impl<Elem, Store: MMRStore<Elem>> IntoIterator for MMRBatch<Elem, Store> {
}
}
pub trait MMRStore<Elem> {
pub trait MMRStoreReadOps<Elem> {
fn get_elem(&self, pos: u64) -> Result<Option<Elem>>;
}
pub trait MMRStoreWriteOps<Elem> {
fn append(&mut self, pos: u64, elems: Vec<Elem>) -> Result<()>;
}
+1
View File
@@ -1,5 +1,6 @@
mod test_accumulate_headers;
mod test_helper;
mod test_incremental;
mod test_mmr;
mod test_sequence;
+8 -8
View File
@@ -1,7 +1,7 @@
use super::new_blake2b;
use crate::{leaf_index_to_pos, util::MemStore, MMRStore, Merge, MerkleProof, Result, MMR};
use bytes::Bytes;
use std::fmt::{self, Debug};
use crate::{MMR, MMRStoreReadOps, Merge, MerkleProof, Result, leaf_index_to_pos, util::MemStore};
use bytes::{Bytes, BytesMut};
use std::fmt;
#[derive(Clone)]
struct Header {
@@ -42,9 +42,9 @@ struct HashWithTD {
impl HashWithTD {
fn serialize(&self) -> Bytes {
let mut data = self.hash.clone();
let mut data = BytesMut::from(self.hash.as_ref());
data.extend(&self.td.to_le_bytes());
data
data.into()
}
fn deserialize(mut data: Bytes) -> Self {
@@ -57,12 +57,12 @@ impl HashWithTD {
}
}
impl Debug for HashWithTD {
impl fmt::Debug for HashWithTD {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"HashWithTD {{ hash: {}, td: {} }}",
faster_hex::hex_string(&self.hash).unwrap(),
faster_hex::hex_string(&self.hash),
self.td
)
}
@@ -106,7 +106,7 @@ impl Prover {
let mut mmr = MMR::<_, MergeHashWithTD, _>::new(self.positions.len() as u64, &self.store);
// get previous element
let mut previous = if let Some(pos) = self.positions.last() {
MMRStore::<_>::get_elem(&&self.store, *pos)?.expect("exists")
mmr.store().get_elem(*pos)?.expect("exists")
} else {
let genesis = Header::default();
+30 -5
View File
@@ -1,9 +1,9 @@
use super::{MergeNumberHash, NumberHash};
use crate::{
helper::{get_peaks, pos_height_in_tree},
MMR,
helper::{get_peak_map, get_peaks, pos_height_in_tree},
leaf_index_to_mmr_size, leaf_index_to_pos,
util::MemStore,
MMR,
};
use lazy_static::lazy_static;
use proptest::prelude::*;
@@ -55,16 +55,41 @@ fn test_pos_height_in_tree() {
assert_eq!(pos_height_in_tree(7), 0);
}
#[test]
fn test_get_peak_map() {
assert_eq!(get_peak_map(0), 0b0);
assert_eq!(get_peak_map(1), 0b1);
assert_eq!(get_peak_map(3), 0b10);
assert_eq!(get_peak_map(4), 0b11);
// 5 and 6 are not valid mmr_size, it will return the bitmap of the last valid mmr (size 4)
assert_eq!(get_peak_map(5), 0b11);
assert_eq!(get_peak_map(6), 0b11);
assert_eq!(get_peak_map(7), 0b100);
assert_eq!(get_peak_map(8), 0b101);
// 9 is not valid mmr_size, it will return the bitmap of the last valid mmr (size 8)
assert_eq!(get_peak_map(9), 0b101);
assert_eq!(get_peak_map(15), 0b1000);
assert_eq!(get_peak_map(16), 0b1001);
assert_eq!(get_peak_map(18), 0b1010);
assert_eq!(get_peak_map(19), 0b1011);
}
#[test]
fn test_get_peaks() {
assert_eq!(get_peaks(0), vec![0]);
assert_eq!(get_peaks(0), vec![]);
assert_eq!(get_peaks(1), vec![0]);
assert_eq!(get_peaks(2), vec![0]);
assert_eq!(get_peaks(3), vec![2]);
assert_eq!(get_peaks(4), vec![2, 3]);
// 5 and 6 are not valid mmr_size, it will return the peaks of the last valid mmr (size 4)
assert_eq!(get_peaks(5), vec![2, 3]);
assert_eq!(get_peaks(6), vec![2, 5]);
assert_eq!(get_peaks(6), vec![2, 3]);
assert_eq!(get_peaks(7), vec![6]);
assert_eq!(get_peaks(8), vec![6, 7]);
// 9 is not valid mmr_size, it will return the peaks of the last valid mmr (size 8)
assert_eq!(get_peaks(9), vec![6, 7]);
assert_eq!(get_peaks(15), vec![14]);
assert_eq!(get_peaks(16), vec![14, 15]);
assert_eq!(get_peaks(18), vec![14, 17]);
assert_eq!(get_peaks(19), vec![14, 17, 18]);
}
+51
View File
@@ -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
);
}
}
+132 -7
View File
@@ -1,12 +1,17 @@
use super::{MergeNumberHash, NumberHash};
use crate::{leaf_index_to_mmr_size, util::MemStore, Error, MMR};
use crate::{
Error,
helper::pos_height_in_tree,
leaf_index_to_mmr_size,
util::{MemMMR, MemStore},
};
use faster_hex::hex_string;
use proptest::prelude::*;
use rand::{seq::SliceRandom, thread_rng};
fn test_mmr(count: u32, proof_elem: Vec<u32>) {
let store = MemStore::default();
let mut mmr = MMR::<_, MergeNumberHash, _>::new(0, &store);
let mut mmr = MemMMR::<_, MergeNumberHash>::new(0, &store);
let positions: Vec<u64> = (0u32..count)
.map(|i| mmr.push(NumberHash::from(i)).unwrap())
.collect();
@@ -34,7 +39,7 @@ fn test_mmr(count: u32, proof_elem: Vec<u32>) {
fn test_gen_new_root_from_proof(count: u32) {
let store = MemStore::default();
let mut mmr = MMR::<_, MergeNumberHash, _>::new(0, &store);
let mut mmr = MemMMR::<_, MergeNumberHash>::new(0, &store);
let positions: Vec<u64> = (0u32..count)
.map(|i| mmr.push(NumberHash::from(i)).unwrap())
.collect();
@@ -59,12 +64,12 @@ fn test_gen_new_root_from_proof(count: u32) {
#[test]
fn test_mmr_root() {
let store = MemStore::default();
let mut mmr = MMR::<_, MergeNumberHash, _>::new(0, &store);
let mut mmr = MemMMR::<_, MergeNumberHash>::new(0, &store);
(0u32..11).for_each(|i| {
mmr.push(NumberHash::from(i)).unwrap();
});
let root = mmr.get_root().expect("get root");
let hex_root = hex_string(&root.0).unwrap();
let hex_root = hex_string(&root.0);
assert_eq!(
"f6794677f37a57df6a5ec36ce61036e43a36c1a009d05c81c9aa685dde1fd6e3",
hex_root
@@ -74,7 +79,7 @@ fn test_mmr_root() {
#[test]
fn test_empty_mmr_root() {
let store = MemStore::<NumberHash>::default();
let mmr = MMR::<_, MergeNumberHash, _>::new(0, &store);
let mmr = MemMMR::<_, MergeNumberHash>::new(0, &store);
assert_eq!(Err(Error::GetRootOnEmpty), mmr.get_root());
}
@@ -140,6 +145,126 @@ fn test_gen_root_from_proof() {
test_gen_new_root_from_proof(11);
}
#[test]
fn test_gen_proof_with_duplicate_leaves() {
test_mmr(10, vec![5, 5]);
}
fn test_invalid_proof_verification(
leaf_count: u32,
positions_to_verify: Vec<u64>,
// positions of entries that should be tampered
tampered_positions: Vec<usize>,
// optionally handroll proof from these positions
handrolled_proof_positions: Option<Vec<u64>>,
) {
use crate::{Merge, MerkleProof};
use std::fmt::{Debug, Formatter};
// Simple item struct to allow debugging the contents of MMR nodes/peaks
#[derive(Clone, PartialEq)]
enum MyItem {
Number(u32),
Merged(Box<MyItem>, Box<MyItem>),
}
impl Debug for MyItem {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
MyItem::Number(x) => f.write_fmt(format_args!("{}", x)),
MyItem::Merged(a, b) => f.write_fmt(format_args!("Merged({:#?}, {:#?})", a, b)),
}
}
}
#[derive(Debug)]
struct MyMerge;
impl Merge for MyMerge {
type Item = MyItem;
fn merge(lhs: &Self::Item, rhs: &Self::Item) -> Result<Self::Item, crate::Error> {
Ok(MyItem::Merged(Box::new(lhs.clone()), Box::new(rhs.clone())))
}
}
let store = MemStore::default();
let mut mmr = MemMMR::<_, MyMerge>::new(0, &store);
let mut positions: Vec<u64> = Vec::new();
for i in 0u32..leaf_count {
let pos = mmr.push(MyItem::Number(i)).unwrap();
positions.push(pos);
}
let root = mmr.get_root().unwrap();
let entries_to_verify: Vec<(u64, MyItem)> = positions_to_verify
.iter()
.map(|pos| (*pos, mmr.batch().get_elem(*pos).unwrap().unwrap()))
.collect();
let mut tampered_entries_to_verify = entries_to_verify.clone();
tampered_positions.iter().for_each(|proof_pos| {
tampered_entries_to_verify[*proof_pos] = (
tampered_entries_to_verify[*proof_pos].0,
MyItem::Number(31337),
)
});
let handrolled_proof: Option<MerkleProof<MyItem, MyMerge>> =
handrolled_proof_positions.map(|handrolled_proof_positions| {
MerkleProof::new(
mmr.mmr_size(),
handrolled_proof_positions
.iter()
.map(|pos| mmr.batch().get_elem(*pos).unwrap().unwrap())
.collect(),
)
});
// verification should fail whenever trying to prove membership of a non-member
if let Some(handrolled_proof) = handrolled_proof {
let handrolled_proof_result =
handrolled_proof.verify(root.clone(), tampered_entries_to_verify.clone());
assert!(handrolled_proof_result.is_err() || !handrolled_proof_result.unwrap());
}
match mmr.gen_proof(positions_to_verify.clone()) {
Ok(proof) => {
assert!(proof.verify(root.clone(), entries_to_verify).unwrap());
assert!(!proof.verify(root, tampered_entries_to_verify).unwrap());
}
Err(Error::NodeProofsNotSupported) => {
// if couldn't generate proof, then it contained a non-leaf
assert!(
positions_to_verify
.iter()
.any(|pos| pos_height_in_tree(*pos) > 0)
);
}
Err(e) => panic!("Unexpected error: {}", e),
}
}
#[test]
fn test_generic_proofs() {
test_invalid_proof_verification(7, vec![5], vec![0], Some(vec![2, 9, 10]));
test_invalid_proof_verification(7, vec![1, 2], vec![0], Some(vec![5, 9, 10]));
test_invalid_proof_verification(7, vec![1, 5], vec![0], Some(vec![0, 9, 10]));
test_invalid_proof_verification(7, vec![1, 6], vec![0], Some(vec![0, 5, 9, 10]));
test_invalid_proof_verification(7, vec![5, 6], vec![0], Some(vec![2, 9, 10]));
test_invalid_proof_verification(7, vec![1, 5, 6], vec![0], Some(vec![0, 9, 10]));
test_invalid_proof_verification(7, vec![1, 5, 7], vec![0], Some(vec![0, 8, 10]));
test_invalid_proof_verification(7, vec![5, 6, 7], vec![0], Some(vec![2, 8, 10]));
test_invalid_proof_verification(7, vec![5, 6, 7, 8, 9, 10], vec![0], Some(vec![2]));
test_invalid_proof_verification(7, vec![1, 5, 7, 8, 9, 10], vec![0], Some(vec![0]));
test_invalid_proof_verification(7, vec![0, 1, 5, 7, 8, 9, 10], vec![0], Some(vec![]));
test_invalid_proof_verification(7, vec![0, 1, 5, 6, 7, 8, 9, 10], vec![0], Some(vec![]));
test_invalid_proof_verification(7, vec![0, 1, 2, 5, 6, 7, 8, 9, 10], vec![0], Some(vec![]));
test_invalid_proof_verification(7, vec![0, 1, 2, 3, 7, 8, 9, 10], vec![0], Some(vec![4]));
test_invalid_proof_verification(7, vec![0, 2, 3, 7, 8, 9, 10], vec![0], Some(vec![1, 4]));
test_invalid_proof_verification(7, vec![0, 3, 7, 8, 9, 10], vec![0], Some(vec![1, 4]));
test_invalid_proof_verification(7, vec![0, 2, 3, 7, 8, 9, 10], vec![0], Some(vec![1, 4]));
}
prop_compose! {
fn count_elem(count: u32)
(elem in 0..count)
@@ -154,7 +279,7 @@ proptest! {
let mut leaves: Vec<u32> = (0..count).collect();
let mut rng = thread_rng();
leaves.shuffle(&mut rng);
let leaves_count = rng.gen_range(1, count - 1);
let leaves_count = rng.gen_range(1..count - 1);
leaves.truncate(leaves_count as usize);
test_mmr(count, leaves);
}
+2 -2
View File
@@ -3,7 +3,7 @@ use std::fmt;
use proptest::proptest;
use rand::{prelude::*, thread_rng};
use crate::{util::MemStore, Merge, Result, MMR};
use crate::{MMR, Merge, Result, util::MemStore};
#[derive(Eq, PartialEq, Clone, Default)]
struct NumberRange {
@@ -91,7 +91,7 @@ proptest! {
let mut leaves: Vec<u32> = (0..count).collect();
let mut rng = thread_rng();
leaves.shuffle(&mut rng);
let leaves_count = rng.gen_range(1, count - 1);
let leaves_count = rng.gen_range(1..count - 1);
leaves.truncate(leaves_count as usize);
test_sequence_sub_func(count, leaves);
}
+5 -47
View File
@@ -1,8 +1,6 @@
use crate::collections::BTreeMap;
use crate::{vec::Vec, MMRStore, Merge, MerkleProof, Result, MMR};
use crate::{MMR, MMRStoreReadOps, MMRStoreWriteOps, Result, vec::Vec};
use core::cell::RefCell;
use core::fmt::Debug;
use core::marker::PhantomData;
#[derive(Clone)]
pub struct MemStore<T>(RefCell<BTreeMap<u64, T>>);
@@ -19,11 +17,13 @@ impl<T> MemStore<T> {
}
}
impl<T: Clone> MMRStore<T> for &MemStore<T> {
impl<T: Clone> MMRStoreReadOps<T> for &MemStore<T> {
fn get_elem(&self, pos: u64) -> Result<Option<T>> {
Ok(self.0.borrow().get(&pos).cloned())
}
}
impl<T> MMRStoreWriteOps<T> for &MemStore<T> {
fn append(&mut self, pos: u64, elems: Vec<T>) -> Result<()> {
let mut store = self.0.borrow_mut();
for (i, elem) in elems.into_iter().enumerate() {
@@ -33,46 +33,4 @@ impl<T: Clone> MMRStore<T> for &MemStore<T> {
}
}
pub struct MemMMR<T, M> {
store: MemStore<T>,
mmr_size: u64,
merge: PhantomData<M>,
}
impl<T: Clone + Debug + PartialEq, M: Merge<Item = T>> Default for MemMMR<T, M> {
fn default() -> Self {
Self::new(0, Default::default())
}
}
impl<T: Clone + Debug + PartialEq, M: Merge<Item = T>> MemMMR<T, M> {
pub fn new(mmr_size: u64, store: MemStore<T>) -> Self {
MemMMR {
mmr_size,
store,
merge: PhantomData,
}
}
pub fn store(&self) -> &MemStore<T> {
&self.store
}
pub fn get_root(&self) -> Result<T> {
let mmr = MMR::<T, M, &MemStore<T>>::new(self.mmr_size, &self.store);
mmr.get_root()
}
pub fn push(&mut self, elem: T) -> Result<u64> {
let mut mmr = MMR::<T, M, &MemStore<T>>::new(self.mmr_size, &self.store);
let pos = mmr.push(elem)?;
self.mmr_size = mmr.mmr_size();
mmr.commit()?;
Ok(pos)
}
pub fn gen_proof(&self, pos_list: Vec<u64>) -> Result<MerkleProof<T, M>> {
let mmr = MMR::<T, M, &MemStore<T>>::new(self.mmr_size, &self.store);
mmr.gen_proof(pos_list)
}
}
pub type MemMMR<'a, T, M> = MMR<T, M, &'a MemStore<T>>;