#![cfg_attr(
all(feature = "std", feature = "digest"),
doc = concat!("```rust\n", include_str!("../examples/mmr_basic.rs"), "\n```")
)]
use crate::borrow::Cow;
use crate::collections::VecDeque;
use crate::error::UserError;
use crate::helper::{
get_peak_map, leaf_index_to_mmr_size, leaf_index_to_pos, parent_offset,
pos_height_in_tree, sibling_offset,
};
use crate::merge::Merge;
use crate::mmr_store::{MMRBatch, MMRStoreReadOps, MMRStoreWriteOps};
use crate::vec;
use crate::vec::Vec;
use crate::{Error, PeaksIter};
use core::fmt::Debug;
use core::marker::PhantomData;
use core::mem;
#[allow(clippy::upper_case_acronyms)]
pub struct MMR<M: Merge, S> {
mmr_size: u64,
batch: MMRBatch<M::Item, S>,
merge: PhantomData<M>,
}
impl<M: Merge, S> MMR<M, S> {
pub fn new(mmr_size: u64, store: S) -> Self {
MMR {
mmr_size,
batch: MMRBatch::new(store),
merge: PhantomData,
}
}
pub fn mmr_size(&self) -> u64 {
self.mmr_size
}
pub fn is_empty(&self) -> bool {
self.mmr_size == 0
}
pub fn batch(&self) -> &MMRBatch<M::Item, S> {
&self.batch
}
pub fn store(&self) -> &S {
self.batch.store()
}
}
impl<M: Merge, S: MMRStoreReadOps<M::Item>> MMR<M, S>
where
M::Item: Clone + PartialEq + Send + Sync,
M::Error: Into<UserError>,
S::Error: Into<UserError>,
{
async fn find_elem<'b>(
&self,
pos: u64,
hashes: &'b [M::Item],
) -> Result<Cow<'b, M::Item>, Error> {
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)
.await
.map_err(|e| Error::StoreError(e.into()))?
.ok_or(Error::InconsistentStore)?;
Ok(Cow::Owned(elem))
}
pub async fn push(&mut self, data: &[u8]) -> Result<u64, Error> {
let elem = M::leaf_hash(data).map_err(|e| Error::MergeError(e.into()))?;
let mut elems = vec![elem];
let elem_pos = self.mmr_size;
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 <<= 1u64;
pos += 1;
let left_pos = pos - peak;
let left_elem = self.find_elem(left_pos, &elems).await?;
let right_elem = elems.last().expect("checked");
let parent_elem = M::merge(&left_elem, right_elem)
.map_err(|e| Error::MergeError(e.into()))?;
elems.push(parent_elem);
}
self.batch.append(elem_pos, elems);
self.mmr_size = pos + 1;
Ok(elem_pos)
}
pub async fn get_root(&self) -> Result<M::Item, Error> {
if self.mmr_size == 0 {
return Err(Error::GetRootOnEmpty);
} else if self.mmr_size == 1 {
return self
.batch
.get_elem(0)
.await
.map_err(|e| Error::StoreError(e.into()))?
.ok_or(Error::InconsistentStore);
}
let elems = self
.batch
.get_elems(PeaksIter::new(self.mmr_size).collect())
.await
.map_err(|e| Error::StoreError(e.into()))?;
let peaks: Vec<M::Item> = elems
.into_iter()
.map(|elem| elem.ok_or(Error::InconsistentStore))
.collect::<Result<Vec<_>, _>>()?;
self
.bag_rhs_peaks(peaks)
.map_err(|e| Error::MergeError(e.into()))?
.ok_or(Error::InconsistentStore)
}
fn bag_rhs_peaks(
&self,
mut rhs_peaks: Vec<M::Item>,
) -> Result<Option<M::Item>, M::Error> {
while rhs_peaks.len() > 1 {
let right_peak = rhs_peaks.pop().expect("pop");
let left_peak = rhs_peaks.pop().expect("pop");
rhs_peaks.push(M::merge_peaks(&right_peak, &left_peak)?);
}
Ok(rhs_peaks.pop())
}
async fn gen_proof_for_peak(
&self,
proof: &mut Vec<M::Item>,
pos_list: Vec<u64>,
peak_pos: u64,
) -> Result<(), Error> {
if pos_list.len() == 1 && pos_list == [peak_pos] {
return Ok(());
}
if pos_list.is_empty() {
proof.push(
self
.batch
.get_elem(peak_pos)
.await
.map_err(|e| Error::StoreError(e.into()))?
.ok_or(Error::InconsistentStore)?,
);
return Ok(());
}
let mut queue: VecDeque<_> =
pos_list.into_iter().map(|pos| (pos, 0)).collect();
while let Some((pos, height)) = queue.pop_front() {
debug_assert!(pos <= peak_pos);
if pos == peak_pos {
if queue.is_empty() {
break;
}
return Err(Error::NodeProofsNotSupported);
}
let (sib_pos, parent_pos) = {
let next_height = pos_height_in_tree(pos + 1);
let sibling_offset = sibling_offset(height);
if next_height > height {
(pos - sibling_offset, pos + 1)
} else {
(pos + sibling_offset, pos + parent_offset(height))
}
};
if Some(&sib_pos) == queue.front().map(|(pos, _)| pos) {
queue.pop_front();
} else {
proof.push(
self
.batch
.get_elem(sib_pos)
.await
.map_err(|e| Error::StoreError(e.into()))?
.ok_or(Error::InconsistentStore)?,
);
}
if parent_pos < peak_pos {
queue.push_back((parent_pos, height + 1));
}
}
Ok(())
}
pub async fn gen_proof(
&mut self,
mut pos_list: Vec<u64>,
) -> Result<InclusionProof<M>, Error> {
if pos_list.is_empty() {
return Err(Error::GenProofForInvalidLeaves);
}
if self.mmr_size == 1 && pos_list == [0] {
return Ok(InclusionProof::new(self.mmr_size, Vec::new()));
}
if pos_list.iter().any(|pos| pos_height_in_tree(*pos) > 0) {
return Err(Error::NodeProofsNotSupported);
}
pos_list.sort_unstable();
pos_list.dedup();
let peaks: Vec<u64> = PeaksIter::new(self.mmr_size).collect();
let mut proof: Vec<M::Item> = Vec::new();
let mut bagging_track = 0;
for peak_pos in peaks {
let pos_list: Vec<_> =
take_while_vec(&mut pos_list, |&pos| pos <= peak_pos);
if pos_list.is_empty() {
bagging_track += 1;
} else {
bagging_track = 0;
}
self
.gen_proof_for_peak(&mut proof, pos_list, peak_pos)
.await?;
}
if !pos_list.is_empty() {
return Err(Error::GenProofForInvalidLeaves);
}
if bagging_track > 1 {
let rhs_peaks = proof.split_off(proof.len() - bagging_track);
proof.push(
self
.bag_rhs_peaks(rhs_peaks)
.map_err(|e| Error::MergeError(e.into()))?
.expect("bagging rhs peaks"),
);
}
Ok(InclusionProof::new(self.mmr_size, proof))
}
}
impl<M: Merge, S: MMRStoreWriteOps<M::Item>> MMR<M, S>
where
M::Item: Send,
S::Error: Into<UserError>,
{
pub async fn commit(&mut self) -> Result<(), Error> {
self
.batch
.commit()
.await
.map_err(|e| Error::StoreError(e.into()))
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(serialize = "M::Item: serde::Serialize"))
)]
#[cfg_attr(
feature = "serde",
serde(bound(deserialize = "M::Item: serde::Deserialize<'de>"))
)]
pub struct InclusionProof<M: Merge> {
mmr_size: u64,
proof: Vec<M::Item>,
#[cfg_attr(feature = "serde", serde(skip))]
merge: PhantomData<M>,
}
impl<M: Merge> InclusionProof<M>
where
M::Item: Clone + PartialEq,
M::Error: Into<UserError>,
{
#[must_use]
pub fn new(mmr_size: u64, proof: Vec<M::Item>) -> Self {
InclusionProof {
mmr_size,
proof,
merge: PhantomData,
}
}
#[must_use]
pub fn mmr_size(&self) -> u64 {
self.mmr_size
}
#[must_use]
pub fn proof_items(&self) -> &[M::Item] {
&self.proof
}
pub fn calculate_root(
&self,
leaves: Vec<(u64, M::Item)>,
) -> Result<M::Item, Error> {
calculate_root::<M, _>(leaves, self.mmr_size, self.proof.iter())
}
pub fn calculate_root_with_new_leaf(
&self,
mut leaves: Vec<(u64, M::Item)>,
new_pos: u64,
new_elem: M::Item,
new_mmr_size: u64,
) -> Result<M::Item, Error> {
let pos_height = pos_height_in_tree(new_pos);
let next_height = pos_height_in_tree(new_pos + 1);
if next_height > pos_height {
let mut peaks_hashes = calculate_peaks_hashes::<M, _>(
leaves,
self.mmr_size,
self.proof.iter(),
)?;
let peaks_pos: Vec<u64> = PeaksIter::new(new_mmr_size).collect();
let mut i = 0;
while peaks_pos[i] < new_pos {
i += 1;
}
peaks_hashes[i..].reverse();
calculate_root::<M, _>(
vec![(new_pos, new_elem)],
new_mmr_size,
peaks_hashes.iter(),
)
} else {
leaves.push((new_pos, new_elem));
calculate_root::<M, _>(leaves, new_mmr_size, self.proof.iter())
}
}
pub fn verify(
&self,
root: &M::Item,
leaves: Vec<(u64, M::Item)>,
) -> Result<bool, Error> {
self
.calculate_root(leaves)
.map(|calculated_root| calculated_root == *root)
}
pub fn verify_incremental(
&self,
root: &M::Item,
prev_root: &M::Item,
incremental: Vec<M::Item>,
) -> Result<bool, Error> {
let current_leaves_count = get_peak_map(self.mmr_size);
if current_leaves_count <= incremental.len() as u64 {
return Err(Error::CorruptedProof);
}
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: Vec<u64> =
PeaksIter::new(prev_mmr_size).collect();
if prev_peaks_positions.len() != self.proof.len() {
return Err(Error::CorruptedProof);
}
prev_peaks_positions
};
let current_peaks_positions: Vec<u64> =
PeaksIter::new(self.mmr_size).collect();
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::<M>(prev_peaks)
.map_err(|e| Error::MergeError(e.into()))?;
if calculated_prev_root != *prev_root {
return Ok(false);
}
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)
.map_err(|e| Error::MergeError(e.into()))
}
}
fn calculate_peak_root<'a, M: Merge, I: Iterator<Item = &'a M::Item>>(
leaves: Vec<(u64, M::Item)>,
peak_pos: u64,
proof_iter: &mut I,
) -> Result<M::Item, Error>
where
M::Item: 'a + Clone,
M::Error: Into<UserError>,
{
debug_assert!(!leaves.is_empty(), "can't be empty");
let mut queue: VecDeque<_> = leaves
.into_iter()
.map(|(pos, item)| (pos, item, 0))
.collect();
while let Some((pos, item, height)) = queue.pop_front() {
if pos == peak_pos {
if queue.is_empty() {
return Ok(item);
}
return Err(Error::CorruptedProof);
}
let next_height = pos_height_in_tree(pos + 1);
let (parent_pos, parent_item) = {
let sibling_offset = sibling_offset(height);
if next_height > height {
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)
.map_err(|e| Error::MergeError(e.into()))?
} else {
let sibling_item = proof_iter.next().ok_or(Error::CorruptedProof)?;
M::merge(sibling_item, &item)
.map_err(|e| Error::MergeError(e.into()))?
};
(parent_pos, parent_item)
} else {
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)
.map_err(|e| Error::MergeError(e.into()))?
} else {
let sibling_item = proof_iter.next().ok_or(Error::CorruptedProof)?;
M::merge(&item, sibling_item)
.map_err(|e| Error::MergeError(e.into()))?
};
(parent_pos, parent_item)
}
};
if parent_pos <= peak_pos {
queue.push_back((parent_pos, parent_item, height + 1));
} else {
return Err(Error::CorruptedProof);
}
}
Err(Error::CorruptedProof)
}
fn calculate_peaks_hashes<'a, M: Merge, I: Iterator<Item = &'a M::Item>>(
mut leaves: Vec<(u64, M::Item)>,
mmr_size: u64,
mut proof_iter: I,
) -> Result<Vec<M::Item>, Error>
where
M::Item: 'a + Clone,
M::Error: Into<UserError>,
{
if leaves.iter().any(|(pos, _)| pos_height_in_tree(*pos) > 0) {
return Err(Error::NodeProofsNotSupported);
}
if mmr_size == 1 && leaves.len() == 1 && leaves[0].0 == 0 {
return Ok(leaves.into_iter().map(|(_pos, item)| item).collect());
}
leaves.sort_by_key(|(pos, _)| *pos);
leaves.dedup_by(|a, b| a.0 == b.0);
let peaks = PeaksIter::new(mmr_size);
let mut peaks_hashes: Vec<M::Item> = Vec::with_capacity(peaks.len() + 1);
for peak_pos in peaks {
let mut leaves: Vec<_> =
take_while_vec(&mut leaves, |(pos, _)| *pos <= peak_pos);
let peak_root = if leaves.len() == 1 && leaves[0].0 == peak_pos {
leaves.remove(0).1
} else if leaves.is_empty() {
if let Some(peak_root) = proof_iter.next() {
peak_root.clone()
} else {
break;
}
} else {
calculate_peak_root::<M, _>(leaves, peak_pos, &mut proof_iter)?
};
peaks_hashes.push(peak_root.clone());
}
if !leaves.is_empty() {
return Err(Error::CorruptedProof);
}
if let Some(rhs_peaks_hashes) = proof_iter.next() {
peaks_hashes.push(rhs_peaks_hashes.clone());
}
if proof_iter.next().is_some() {
return Err(Error::CorruptedProof);
}
Ok(peaks_hashes)
}
fn bagging_peaks_hashes<M: Merge>(
mut peaks_hashes: Vec<M::Item>,
) -> Result<M::Item, Error>
where
M::Error: Into<UserError>,
{
while peaks_hashes.len() > 1 {
let right_peak = peaks_hashes.pop().expect("pop");
let left_peak = peaks_hashes.pop().expect("pop");
peaks_hashes.push(
M::merge_peaks(&right_peak, &left_peak)
.map_err(|e| Error::MergeError(e.into()))?,
);
}
peaks_hashes.pop().ok_or(Error::CorruptedProof)
}
fn calculate_root<'a, M: Merge, I: Iterator<Item = &'a M::Item>>(
leaves: Vec<(u64, M::Item)>,
mmr_size: u64,
proof_iter: I,
) -> Result<M::Item, Error>
where
M::Item: 'a + Clone,
M::Error: Into<UserError>,
{
let peaks_hashes =
calculate_peaks_hashes::<M, _>(leaves, mmr_size, proof_iter)?;
bagging_peaks_hashes::<M>(peaks_hashes)
}
fn take_while_vec<T, P: Fn(&T) -> bool>(v: &mut Vec<T>, p: P) -> Vec<T> {
for i in 0..v.len() {
if !p(&v[i]) {
return v.drain(..i).collect();
}
}
mem::take(v)
}