18 Commits

Author SHA1 Message Date
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
Quake Wang bb196e8b96 Merge pull request #17 from yangby-cryptape/pr/release-v0.4.0
chore: bump version to v0.4.0
2022-05-30 12:55:22 +09:00
Boyu Yang bf24bf0b93 chore: bump version to v0.4.0 2022-05-30 11:07:29 +08:00
Boyu Yang 8b8adae74c Merge pull request #16 from yangby-cryptape/pr/check-before-merge
feat: check nodes (or peaks) before merge them
2022-05-30 11:00:47 +08:00
Boyu Yang be20646ac8 feat: check nodes (or peaks) before merge them 2022-05-30 10:50:40 +08:00
ian a037309912 Merge pull request #15 from yangby-cryptape/pr/should-it-keep-the-order
feat: add a trait method to control how to merge peaks
2022-04-01 16:15:36 +08:00
Boyu Yang 0b37e46871 feat: add a trait method to control how to merge peaks 2022-03-23 18:35:42 +08:00
jjy 09092d0039 Merge pull request #13 from nervosnetwork/release-v0.3.2
Release v0.3.2
2021-08-23 19:54:10 +08:00
jjy caa2a4fdce chore: upgrade version to v0.3.2 2021-08-23 19:47:57 +08:00
jjy f0925ef6bd fix: fix warning messages 2021-08-23 19:47:39 +08:00
jjy 13d50d12d9 Merge pull request #11 from darwinia-network/main
Public `helper`
2021-07-15 16:52:40 +08:00
Xavier Lau b16216f90e Public helper 2021-06-23 19:39:40 +08:00
jjy 9f9a95c73e Update README.md 2021-01-21 15:04:52 +08:00
13 changed files with 154 additions and 43 deletions
+6 -6
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "ckb-merkle-mountain-range" name = "ckb-merkle-mountain-range"
version = "0.3.1" version = "0.5.0"
authors = ["Nervos Core Dev <dev@nervos.org>"] authors = ["Nervos Core Dev <dev@nervos.org>"]
edition = "2018" edition = "2018"
license = "MIT" license = "MIT"
@@ -12,15 +12,15 @@ default = ["std"]
std = [] std = []
[dependencies] [dependencies]
cfg-if = "0.1" cfg-if = "1.0"
[dev-dependencies] [dev-dependencies]
faster-hex = "0.3" faster-hex = "0.6.1"
criterion = "0.3" criterion = "0.3"
rand = "0.6" rand = "0.8.5"
proptest = "0.9.4" proptest = "1.0.0"
lazy_static = "1.3.0" lazy_static = "1.3.0"
bytes = "0.4" bytes = "1.2.0"
blake2b-rs = "0.1.4" blake2b-rs = "0.1.4"
[[bench]] [[bench]]
+1 -3
View File
@@ -3,8 +3,6 @@
A generalized merkle mountain range implementation. 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 ## Features
* Leaves accumulation * Leaves accumulation
@@ -38,7 +36,7 @@ For example, we insert a leaf to the example MMR:
3. insert parent node to position `20`. 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. 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`. 5. insert new node to next position `21`.
6. the node `20` have no left sibling, complete the insertion. 6. the node `21` have no left sibling, complete the insertion.
Example MMR after insertion of a new leaf: Example MMR after insertion of a new leaf:
+2 -2
View File
@@ -10,7 +10,7 @@ fn bench(c: &mut Criterion) {
c.bench_function("left_index_to_pos", |b| { c.bench_function("left_index_to_pos", |b| {
let mut rng = thread_rng(); let mut rng = thread_rng();
b.iter(|| { 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); leaf_index_to_pos(leaf_index);
}); });
}); });
@@ -18,7 +18,7 @@ fn bench(c: &mut Criterion) {
c.bench_function("left_index_to_mmr_size", |b| { c.bench_function("left_index_to_mmr_size", |b| {
let mut rng = thread_rng(); let mut rng = thread_rng();
b.iter(|| { 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); leaf_index_to_mmr_size(leaf_index);
}); });
}); });
+12 -10
View File
@@ -1,7 +1,7 @@
#[macro_use] #[macro_use]
extern crate criterion; extern crate criterion;
use criterion::Criterion; use criterion::{BenchmarkId, Criterion};
use bytes::Bytes; use bytes::Bytes;
use ckb_merkle_mountain_range::{util::MemStore, Error, MMRStore, Merge, Result, MMR}; use ckb_merkle_mountain_range::{util::MemStore, Error, MMRStore, Merge, Result, MMR};
@@ -31,13 +31,13 @@ struct MergeNumberHash;
impl Merge for MergeNumberHash { impl Merge for MergeNumberHash {
type Item = NumberHash; type Item = NumberHash;
fn merge(lhs: &Self::Item, rhs: &Self::Item) -> Self::Item { fn merge(lhs: &Self::Item, rhs: &Self::Item) -> Result<Self::Item> {
let mut hasher = new_blake2b(); let mut hasher = new_blake2b();
let mut hash = [0u8; 32]; let mut hash = [0u8; 32];
hasher.update(&lhs.0); hasher.update(&lhs.0);
hasher.update(&rhs.0); hasher.update(&rhs.0);
hasher.finalize(&mut hash); hasher.finalize(&mut hash);
NumberHash(hash.to_vec().into()) Ok(NumberHash(hash.to_vec().into()))
} }
} }
@@ -53,13 +53,15 @@ fn prepare_mmr(count: u32) -> (u64, MemStore<NumberHash>, Vec<u64>) {
} }
fn bench(c: &mut Criterion) { fn bench(c: &mut Criterion) {
c.bench_function_over_inputs( {
"MMR insert", let mut group = c.benchmark_group("MMR insertion");
|b, &&size| { let inputs = [10_000, 100_000, 100_0000];
b.iter(|| prepare_mmr(size)); for input in inputs.iter() {
}, group.bench_with_input(BenchmarkId::new("times", input), &input, |b, &&size| {
&[10_000, 100_000, 100_0000], b.iter(|| prepare_mmr(size));
); });
}
}
c.bench_function("MMR gen proof", |b| { c.bench_function("MMR gen proof", |b| {
let (mmr_size, store, positions) = prepare_mmr(100_0000); let (mmr_size, store, positions) = prepare_mmr(100_0000);
+4
View File
@@ -9,6 +9,9 @@ pub enum Error {
CorruptedProof, CorruptedProof,
/// The leaves is an empty list, or beyond the mmr range /// The leaves is an empty list, or beyond the mmr range
GenProofForInvalidLeaves, GenProofForInvalidLeaves,
/// The two nodes couldn't merge into one.
MergeError(crate::string::String),
} }
impl core::fmt::Display for Error { impl core::fmt::Display for Error {
@@ -20,6 +23,7 @@ impl core::fmt::Display for Error {
StoreError(msg) => write!(f, "Store error {}", msg)?, StoreError(msg) => write!(f, "Store error {}", msg)?,
CorruptedProof => write!(f, "Corrupted proof")?, CorruptedProof => write!(f, "Corrupted proof")?,
GenProofForInvalidLeaves => write!(f, "Generate proof ofr invalid leaves")?, GenProofForInvalidLeaves => write!(f, "Generate proof ofr invalid leaves")?,
MergeError(msg) => write!(f, "Merge error {}", msg)?,
} }
Ok(()) Ok(())
} }
+1 -1
View File
@@ -1,7 +1,7 @@
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
mod error; mod error;
mod helper; pub mod helper;
mod merge; mod merge;
mod mmr; mod mmr;
mod mmr_store; mod mmr_store;
+8 -1
View File
@@ -1,4 +1,11 @@
use crate::Result;
pub trait Merge { pub trait Merge {
type Item; type Item;
fn merge(left: &Self::Item, right: &Self::Item) -> Self::Item;
fn merge(left: &Self::Item, right: &Self::Item) -> Result<Self::Item>;
fn merge_peaks(peak1: &Self::Item, peak2: &Self::Item) -> Result<Self::Item> {
Self::merge(peak1, peak2)
}
} }
+6 -5
View File
@@ -14,7 +14,8 @@ use crate::{Error, Merge, Result};
use core::fmt::Debug; use core::fmt::Debug;
use core::marker::PhantomData; use core::marker::PhantomData;
pub struct MMR<T, M, S: MMRStore<T>> { #[allow(clippy::upper_case_acronyms)]
pub struct MMR<T, M, S> {
mmr_size: u64, mmr_size: u64,
batch: MMRBatch<T, S>, batch: MMRBatch<T, S>,
merge: PhantomData<M>, merge: PhantomData<M>,
@@ -62,7 +63,7 @@ impl<'a, T: Clone + PartialEq + Debug, M: Merge<Item = T>, S: MMRStore<T>> MMR<T
let right_pos = left_pos + sibling_offset(height); let right_pos = left_pos + sibling_offset(height);
let left_elem = self.find_elem(left_pos, &elems)?; let left_elem = self.find_elem(left_pos, &elems)?;
let right_elem = self.find_elem(right_pos, &elems)?; let right_elem = self.find_elem(right_pos, &elems)?;
let parent_elem = M::merge(&left_elem, &right_elem); let parent_elem = M::merge(&left_elem, &right_elem)?;
elems.push(parent_elem); elems.push(parent_elem);
height += 1 height += 1
} }
@@ -95,7 +96,7 @@ impl<'a, T: Clone + PartialEq + Debug, M: Merge<Item = T>, S: MMRStore<T>> MMR<T
while rhs_peaks.len() > 1 { while rhs_peaks.len() > 1 {
let right_peak = rhs_peaks.pop().expect("pop"); let right_peak = rhs_peaks.pop().expect("pop");
let left_peak = rhs_peaks.pop().expect("pop"); let left_peak = rhs_peaks.pop().expect("pop");
rhs_peaks.push(M::merge(&right_peak, &left_peak)); rhs_peaks.push(M::merge_peaks(&right_peak, &left_peak)?);
} }
Ok(rhs_peaks.pop()) Ok(rhs_peaks.pop())
} }
@@ -319,7 +320,7 @@ fn calculate_peak_root<
M::merge(&sibling_item, &item) M::merge(&sibling_item, &item)
} else { } else {
M::merge(&item, &sibling_item) M::merge(&item, &sibling_item)
}; }?;
if parent_pos < peak_pos { if parent_pos < peak_pos {
queue.push_back((parent_pos, parent_item, height + 1)); queue.push_back((parent_pos, parent_item, height + 1));
@@ -393,7 +394,7 @@ fn bagging_peaks_hashes<'a, T: 'a + PartialEq + Debug + Clone, M: Merge<Item = T
while peaks_hashes.len() > 1 { while peaks_hashes.len() > 1 {
let right_peak = peaks_hashes.pop().expect("pop"); let right_peak = peaks_hashes.pop().expect("pop");
let left_peak = peaks_hashes.pop().expect("pop"); let left_peak = peaks_hashes.pop().expect("pop");
peaks_hashes.push(M::merge(&right_peak, &left_peak)); peaks_hashes.push(M::merge_peaks(&right_peak, &left_peak)?);
} }
peaks_hashes.pop().ok_or(Error::CorruptedProof) peaks_hashes.pop().ok_or(Error::CorruptedProof)
} }
+1 -1
View File
@@ -1,7 +1,7 @@
use crate::{vec::Vec, Result}; use crate::{vec::Vec, Result};
#[derive(Default)] #[derive(Default)]
pub struct MMRBatch<Elem, Store: MMRStore<Elem>> { pub struct MMRBatch<Elem, Store> {
memory_batch: Vec<(u64, Vec<Elem>)>, memory_batch: Vec<(u64, Vec<Elem>)>,
store: Store, store: Store,
} }
+4 -3
View File
@@ -1,8 +1,9 @@
mod test_accumulate_headers; mod test_accumulate_headers;
mod test_helper; mod test_helper;
mod test_mmr; mod test_mmr;
mod test_sequence;
use crate::Merge; use crate::{Merge, Result};
use blake2b_rs::{Blake2b, Blake2bBuilder}; use blake2b_rs::{Blake2b, Blake2bBuilder};
use bytes::Bytes; use bytes::Bytes;
@@ -26,12 +27,12 @@ struct MergeNumberHash;
impl Merge for MergeNumberHash { impl Merge for MergeNumberHash {
type Item = NumberHash; type Item = NumberHash;
fn merge(lhs: &Self::Item, rhs: &Self::Item) -> Self::Item { fn merge(lhs: &Self::Item, rhs: &Self::Item) -> Result<Self::Item> {
let mut hasher = new_blake2b(); let mut hasher = new_blake2b();
let mut hash = [0u8; 32]; let mut hash = [0u8; 32];
hasher.update(&lhs.0); hasher.update(&lhs.0);
hasher.update(&rhs.0); hasher.update(&rhs.0);
hasher.finalize(&mut hash); hasher.finalize(&mut hash);
NumberHash(hash.to_vec().into()) Ok(NumberHash(hash.to_vec().into()))
} }
} }
+9 -9
View File
@@ -1,7 +1,7 @@
use super::new_blake2b; use super::new_blake2b;
use crate::{leaf_index_to_pos, util::MemStore, MMRStore, Merge, MerkleProof, Result, MMR}; use crate::{leaf_index_to_pos, util::MemStore, MMRStore, Merge, MerkleProof, Result, MMR};
use bytes::Bytes; use bytes::{Bytes, BytesMut};
use std::fmt::{self, Debug}; use std::fmt;
#[derive(Clone)] #[derive(Clone)]
struct Header { struct Header {
@@ -42,9 +42,9 @@ struct HashWithTD {
impl HashWithTD { impl HashWithTD {
fn serialize(&self) -> Bytes { 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.extend(&self.td.to_le_bytes());
data data.into()
} }
fn deserialize(mut data: Bytes) -> Self { 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 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,
"HashWithTD {{ hash: {}, td: {} }}", "HashWithTD {{ hash: {}, td: {} }}",
faster_hex::hex_string(&self.hash).unwrap(), faster_hex::hex_string(&self.hash),
self.td self.td
) )
} }
@@ -72,17 +72,17 @@ struct MergeHashWithTD;
impl Merge for MergeHashWithTD { impl Merge for MergeHashWithTD {
type Item = HashWithTD; type Item = HashWithTD;
fn merge(lhs: &Self::Item, rhs: &Self::Item) -> Self::Item { fn merge(lhs: &Self::Item, rhs: &Self::Item) -> Result<Self::Item> {
let mut hasher = new_blake2b(); let mut hasher = new_blake2b();
let mut hash = [0u8; 32]; let mut hash = [0u8; 32];
hasher.update(&lhs.serialize()); hasher.update(&lhs.serialize());
hasher.update(&rhs.serialize()); hasher.update(&rhs.serialize());
hasher.finalize(&mut hash); hasher.finalize(&mut hash);
let td = lhs.td + rhs.td; let td = lhs.td + rhs.td;
HashWithTD { Ok(HashWithTD {
hash: hash.to_vec().into(), hash: hash.to_vec().into(),
td, td,
} })
} }
} }
+2 -2
View File
@@ -64,7 +64,7 @@ fn test_mmr_root() {
mmr.push(NumberHash::from(i)).unwrap(); mmr.push(NumberHash::from(i)).unwrap();
}); });
let root = mmr.get_root().expect("get root"); 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!( assert_eq!(
"f6794677f37a57df6a5ec36ce61036e43a36c1a009d05c81c9aa685dde1fd6e3", "f6794677f37a57df6a5ec36ce61036e43a36c1a009d05c81c9aa685dde1fd6e3",
hex_root hex_root
@@ -154,7 +154,7 @@ proptest! {
let mut leaves: Vec<u32> = (0..count).collect(); let mut leaves: Vec<u32> = (0..count).collect();
let mut rng = thread_rng(); let mut rng = thread_rng();
leaves.shuffle(&mut 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); leaves.truncate(leaves_count as usize);
test_mmr(count, leaves); test_mmr(count, leaves);
} }
+98
View File
@@ -0,0 +1,98 @@
use std::fmt;
use proptest::proptest;
use rand::{prelude::*, thread_rng};
use crate::{util::MemStore, Merge, Result, MMR};
#[derive(Eq, PartialEq, Clone, Default)]
struct NumberRange {
start: u32,
end: u32,
}
struct MergeNumberRange;
impl fmt::Debug for NumberRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "NumberRange({}, {})", self.start, self.end)
}
}
impl fmt::Debug for MergeNumberRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MergeNumberRange")
}
}
impl From<u32> for NumberRange {
fn from(num: u32) -> Self {
Self {
start: num,
end: num,
}
}
}
impl NumberRange {
fn is_normalized(&self) -> bool {
self.start <= self.end
}
}
impl Merge for MergeNumberRange {
type Item = NumberRange;
fn merge(lhs: &Self::Item, rhs: &Self::Item) -> Result<Self::Item> {
Ok(Self::Item {
start: lhs.start,
end: rhs.end,
})
}
fn merge_peaks(lhs: &Self::Item, rhs: &Self::Item) -> Result<Self::Item> {
Self::merge(rhs, lhs)
}
}
fn test_sequence_sub_func(count: u32, proof_elem: Vec<u32>) {
let store = MemStore::default();
let mut mmr = MMR::<_, MergeNumberRange, _>::new(0, &store);
let positions = (0..count)
.map(|i| mmr.push(NumberRange::from(i)).expect("push"))
.collect::<Vec<_>>();
let root = mmr.get_root().expect("get_root");
assert!(root.is_normalized());
let proof = mmr
.gen_proof(
proof_elem
.iter()
.map(|elem| positions[*elem as usize])
.collect(),
)
.expect("gen_proof");
for item in proof.proof_items() {
assert!(item.is_normalized())
}
mmr.commit().expect("commit");
let result = proof
.verify(
root,
proof_elem
.iter()
.map(|elem| (positions[*elem as usize], NumberRange::from(*elem)))
.collect(),
)
.expect("verify");
assert!(result);
}
proptest! {
#[test]
fn test_sequence(count in 10u32..500u32) {
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);
leaves.truncate(leaves_count as usize);
test_sequence_sub_func(count, leaves);
}
}