use crate::{
mmr::{
storage::{OffchainStorage, RuntimeStorage, Storage},
Hasher, Node, NodeOf,
},
primitives::{self, Error, NodeIndex},
Config, HashingOf,
};
use sp_mmr_primitives::{mmr_lib, utils::NodesUtils};
use sp_std::prelude::*;
pub fn verify_leaves_proof<H, L>(
root: H::Output,
leaves: Vec<Node<H, L>>,
proof: primitives::Proof<H::Output>,
) -> Result<bool, Error>
where
H: sp_runtime::traits::Hash,
L: primitives::FullLeaf,
{
let size = NodesUtils::new(proof.leaf_count).size();
if leaves.len() != proof.leaf_indices.len() {
return Err(Error::Verify.log_debug("Proof leaf_indices not same length with leaves"))
}
let leaves_and_position_data = proof
.leaf_indices
.into_iter()
.map(|index| mmr_lib::leaf_index_to_pos(index))
.zip(leaves.into_iter())
.collect();
let p = mmr_lib::MerkleProof::<Node<H, L>, Hasher<H, L>>::new(
size,
proof.items.into_iter().map(Node::Hash).collect(),
);
p.verify(Node::Hash(root), leaves_and_position_data)
.map_err(|e| Error::Verify.log_debug(e))
}
pub struct Mmr<StorageType, T, I, L>
where
T: Config<I>,
I: 'static,
L: primitives::FullLeaf,
Storage<StorageType, T, I, L>: mmr_lib::MMRStore<NodeOf<T, I, L>>,
{
mmr: mmr_lib::MMR<NodeOf<T, I, L>, Hasher<HashingOf<T, I>, L>, Storage<StorageType, T, I, L>>,
leaves: NodeIndex,
}
impl<StorageType, T, I, L> Mmr<StorageType, T, I, L>
where
T: Config<I>,
I: 'static,
L: primitives::FullLeaf,
Storage<StorageType, T, I, L>: mmr_lib::MMRStore<NodeOf<T, I, L>>,
{
pub fn new(leaves: NodeIndex) -> Self {
let size = NodesUtils::new(leaves).size();
Self { mmr: mmr_lib::MMR::new(size, Default::default()), leaves }
}
pub fn verify_leaves_proof(
&self,
leaves: Vec<L>,
proof: primitives::Proof<<T as Config<I>>::Hash>,
) -> Result<bool, Error> {
let p = mmr_lib::MerkleProof::<NodeOf<T, I, L>, Hasher<HashingOf<T, I>, L>>::new(
self.mmr.mmr_size(),
proof.items.into_iter().map(Node::Hash).collect(),
);
if leaves.len() != proof.leaf_indices.len() {
return Err(Error::Verify.log_debug("Proof leaf_indices not same length with leaves"))
}
let leaves_positions_and_data = proof
.leaf_indices
.into_iter()
.map(|index| mmr_lib::leaf_index_to_pos(index))
.zip(leaves.into_iter().map(|leaf| Node::Data(leaf)))
.collect();
let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?;
p.verify(root, leaves_positions_and_data)
.map_err(|e| Error::Verify.log_debug(e))
}
#[cfg(test)]
pub fn size(&self) -> NodeIndex {
self.mmr.mmr_size()
}
}
impl<T, I, L> Mmr<RuntimeStorage, T, I, L>
where
T: Config<I>,
I: 'static,
L: primitives::FullLeaf,
{
pub fn push(&mut self, leaf: L) -> Option<NodeIndex> {
let position =
self.mmr.push(Node::Data(leaf)).map_err(|e| Error::Push.log_error(e)).ok()?;
self.leaves += 1;
Some(position)
}
pub fn finalize(self) -> Result<(NodeIndex, <T as Config<I>>::Hash), Error> {
let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?;
self.mmr.commit().map_err(|e| Error::Commit.log_error(e))?;
Ok((self.leaves, root.hash()))
}
}
impl<T, I, L> Mmr<OffchainStorage, T, I, L>
where
T: Config<I>,
I: 'static,
L: primitives::FullLeaf + codec::Decode,
{
pub fn generate_proof(
&self,
leaf_indices: Vec<NodeIndex>,
) -> Result<(Vec<L>, primitives::Proof<<T as Config<I>>::Hash>), Error> {
let positions = leaf_indices
.iter()
.map(|index| mmr_lib::leaf_index_to_pos(*index))
.collect::<Vec<_>>();
let store = <Storage<OffchainStorage, T, I, L>>::default();
let leaves = positions
.iter()
.map(|pos| match mmr_lib::MMRStore::get_elem(&store, *pos) {
Ok(Some(Node::Data(leaf))) => Ok(leaf),
e => Err(Error::LeafNotFound.log_debug(e)),
})
.collect::<Result<Vec<_>, Error>>()?;
let leaf_count = self.leaves;
self.mmr
.gen_proof(positions)
.map_err(|e| Error::GenerateProof.log_error(e))
.map(|p| primitives::Proof {
leaf_indices,
leaf_count,
items: p.proof_items().iter().map(|x| x.hash()).collect(),
})
.map(|p| (leaves, p))
}
}