#[cfg(test)]
mod tests;
use super::{
count_set_bits, get_bit_at_from_msb, set_bit_at_from_msb,
smt::{DEFAULT_VALUE, RIGHT},
tree_hasher::{TreeHasher, LEAF_PREFIX},
};
use alloc::{vec, vec::Vec};
use bytes::Bytes;
use core::marker::PhantomData;
use digest::Digest;
pub struct BadProof;
impl core::fmt::Debug for BadProof {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "bad proof")
}
}
impl core::fmt::Display for BadProof {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "bad proof")
}
}
#[cfg(feature = "std")]
impl std::error::Error for BadProof {}
#[derive(Debug, Clone)]
pub struct SparseMerkleProof<H> {
pub(crate) side_nodes: Vec<Bytes>,
pub(crate) non_membership_leaf_data: Option<Bytes>,
pub(crate) sibling_data: Option<Bytes>,
pub(crate) _marker: PhantomData<H>,
}
impl<H> SparseMerkleProof<H> {
pub fn new(
side_nodes: Vec<Bytes>,
non_membership_leaf_data: Option<Bytes>,
sibling_data: Option<Bytes>,
) -> Self {
Self {
side_nodes,
non_membership_leaf_data,
sibling_data,
_marker: PhantomData,
}
}
#[inline]
pub fn sibling_data(&self) -> Option<&Bytes> {
self.sibling_data.as_ref()
}
#[inline]
pub fn non_membership_leaf_data(&self) -> Option<&Bytes> {
self.non_membership_leaf_data.as_ref()
}
#[inline]
pub fn side_nodes(&self) -> &[Bytes] {
&self.side_nodes
}
}
impl<H: digest::Digest> SparseMerkleProof<H> {
pub fn verify(
&self,
root: impl AsRef<[u8]>,
key: impl AsRef<[u8]>,
value: impl AsRef<[u8]>,
) -> bool {
self.verify_proof(root, key, value)
}
pub fn compact(&self) -> Result<SparseCompactMerkleProof<H>, BadProof> {
let mut th = TreeHasher::<H>::new(vec![0; TreeHasher::<H>::path_size()].into());
if !self.sanity_check(&mut th) {
return Err(BadProof);
}
let mut bit_mask = vec![0u8; ((self.side_nodes.len() as f64) / 8.0).ceil() as usize];
let compacted_side_nodes = self
.side_nodes
.iter()
.enumerate()
.filter_map(|(idx, node)| {
let node = node.slice(..TreeHasher::<H>::path_size());
if node.eq(th.placeholder_ref()) {
set_bit_at_from_msb(bit_mask.as_mut_slice(), idx);
None
} else {
Some(node)
}
})
.collect::<Vec<_>>();
Ok(SparseCompactMerkleProof {
side_nodes: compacted_side_nodes,
non_membership_leaf_data: self.non_membership_leaf_data.clone(),
bitmask: bit_mask.into(),
num_side_nodes: self.side_nodes.len(),
sibling_data: self.sibling_data.clone(),
_marker: PhantomData,
})
}
pub fn compact_into(self) -> Result<SparseCompactMerkleProof<H>, BadProof> {
let mut th = TreeHasher::<H>::new(vec![0; TreeHasher::<H>::path_size()].into());
if !self.sanity_check(&mut th) {
return Err(BadProof);
}
let num_side_nodes = self.side_nodes.len();
let SparseMerkleProof {
side_nodes,
non_membership_leaf_data,
sibling_data,
_marker: _,
} = self;
let mut bit_mask = vec![0u8; ((num_side_nodes as f64) / 8.0).ceil() as usize];
let compacted_side_nodes = side_nodes
.into_iter()
.enumerate()
.filter_map(|(idx, node)| {
let node = node.slice(..TreeHasher::<H>::path_size());
if node.eq(th.placeholder_ref()) {
set_bit_at_from_msb(bit_mask.as_mut_slice(), idx);
None
} else {
Some(node)
}
})
.collect::<Vec<_>>();
Ok(SparseCompactMerkleProof {
side_nodes: compacted_side_nodes,
non_membership_leaf_data,
bitmask: bit_mask.into(),
num_side_nodes,
sibling_data,
_marker: PhantomData,
})
}
#[inline]
fn verify_proof(
&self,
root: impl AsRef<[u8]>,
key: impl AsRef<[u8]>,
value: impl AsRef<[u8]>,
) -> bool {
let mut th = TreeHasher::<H>::new(vec![0; TreeHasher::<H>::path_size()].into());
let path = th.path(key);
if !self.sanity_check(&mut th) {
return false;
}
let mut current_hash;
if value.as_ref().eq(&DEFAULT_VALUE) {
match &self.non_membership_leaf_data {
Some(data) => {
let (actual_path, value_hash) = TreeHasher::<H>::parse_leaf(data);
if actual_path.eq(path.as_slice()) {
return false;
}
current_hash = th.digest_leaf_hash(actual_path, value_hash);
}
None => {
current_hash = th.placeholder();
}
}
} else {
let value_hash = th.digest(value);
current_hash = th.digest_leaf_hash(path, value_hash);
}
let num = self.side_nodes.len();
self.side_nodes.iter().enumerate().for_each(|(idx, path)| {
let node = path.slice(..TreeHasher::<H>::path_size());
if get_bit_at_from_msb(path, num - 1 - idx) == RIGHT {
(current_hash, _) = th.digest_node(node, ¤t_hash);
} else {
(current_hash, _) = th.digest_node(¤t_hash, node);
}
});
current_hash.eq(root.as_ref())
}
pub(crate) fn verify_proof_with_updates(
&self,
root: impl AsRef<[u8]>,
key: impl AsRef<[u8]>,
value: impl AsRef<[u8]>,
) -> (bool, Vec<(Bytes, Bytes)>)
where
H: Digest,
{
let mut th = TreeHasher::<H>::new(vec![0; TreeHasher::<H>::path_size()].into());
let path = th.path(key);
if !self.sanity_check(&mut th) {
return (false, vec![]);
}
let mut updates = Vec::with_capacity(self.side_nodes.len() + 1);
let mut current_hash;
if value.as_ref().eq(&DEFAULT_VALUE) {
match &self.non_membership_leaf_data {
Some(data) => {
let (actual_path, value_hash) = TreeHasher::<H>::parse_leaf(data);
if actual_path.eq(path.as_slice()) {
return (false, vec![]);
}
let (hash, data) = th.digest_leaf(actual_path, value_hash);
current_hash = hash;
updates.push((current_hash.clone(), data));
}
None => {
current_hash = th.placeholder();
}
}
} else {
let value_hash = th.digest(value);
let (hash, data) = th.digest_leaf(path.as_ref(), value_hash);
current_hash = hash;
updates.push((current_hash.clone(), data));
}
let num = self.side_nodes.len();
self.side_nodes
.iter()
.enumerate()
.for_each(|(idx, side_node)| {
let node = side_node.slice(..TreeHasher::<H>::path_size());
if get_bit_at_from_msb(path.as_ref(), num - 1 - idx) == RIGHT {
let (hash, data) = th.digest_node(node, ¤t_hash);
current_hash = hash;
updates.push((current_hash.clone(), data));
} else {
let (hash, data) = th.digest_node(¤t_hash, node);
current_hash = hash;
updates.push((current_hash.clone(), data));
}
});
(current_hash.eq(root.as_ref()), updates)
}
fn sanity_check(&self, th: &mut TreeHasher<H>) -> bool {
if self.side_nodes.len() > TreeHasher::<H>::path_size() * 8 ||
self.check_non_membership_proofs_size(th)
{
return false;
}
for side_node in &self.side_nodes {
if side_node.len() != <H as digest::Digest>::output_size() {
return false;
}
}
if self.side_nodes.is_empty() {
return true;
}
match &self.sibling_data {
Some(sibling_data) => {
let sibling_hash = th.digest(sibling_data);
self.side_nodes[0].eq(sibling_hash.as_slice())
}
None => true,
}
}
#[inline]
fn check_non_membership_proofs_size(&self, _th: &TreeHasher<H>) -> bool {
if let Some(non_membership_proofs) = &self.non_membership_leaf_data {
non_membership_proofs.len()
!= LEAF_PREFIX.len()
+ TreeHasher::<H>::path_size()
+ <H as digest::Digest>::output_size()
} else {
false
}
}
}
#[derive(Debug, Clone)]
pub struct SparseCompactMerkleProof<H> {
side_nodes: Vec<Bytes>,
non_membership_leaf_data: Option<Bytes>,
bitmask: Bytes,
num_side_nodes: usize,
sibling_data: Option<Bytes>,
_marker: PhantomData<H>,
}
impl<H> SparseCompactMerkleProof<H> {
pub fn new(
side_nodes: Vec<Bytes>,
non_membership_leaf_data: Option<Bytes>,
bitmask: Bytes,
num_side_nodes: usize,
sibling_data: Option<Bytes>,
) -> Self {
Self {
side_nodes,
non_membership_leaf_data,
bitmask,
num_side_nodes,
sibling_data,
_marker: PhantomData,
}
}
#[inline]
pub fn sibling_data(&self) -> Option<&Bytes> {
self.sibling_data.as_ref()
}
#[inline]
pub fn non_membership_leaf_data(&self) -> Option<&Bytes> {
self.non_membership_leaf_data.as_ref()
}
#[inline]
pub fn original_side_nodes_len(&self) -> usize {
self.num_side_nodes
}
#[inline]
pub fn side_nodes(&self) -> &[Bytes] {
&self.side_nodes
}
}
impl<H: digest::Digest> SparseCompactMerkleProof<H> {
fn sanity_check(&self, _th: &mut TreeHasher<H>) -> bool {
if self.num_side_nodes > TreeHasher::<H>::path_size() * 8 ||
self.bitmask.len() != ((self.num_side_nodes as f64 ) / 8f64).ceil() as usize ||
(self.num_side_nodes > 0 && self.side_nodes.len() != self.num_side_nodes - count_set_bits(&self.bitmask))
{
return false;
}
true
}
pub fn verify(
&self,
root: impl AsRef<[u8]>,
key: impl AsRef<[u8]>,
value: impl AsRef<[u8]>,
) -> bool {
self.decompact()
.map(|proof| proof.verify(root, key, value))
.unwrap_or(false)
}
pub fn decompact(&self) -> Result<SparseMerkleProof<H>, BadProof> {
let mut th = TreeHasher::<H>::new(vec![0; TreeHasher::<H>::path_size()].into());
if !self.sanity_check(&mut th) {
return Err(BadProof);
}
let mut position = 0;
let nodes = (0..self.num_side_nodes)
.map(|idx| {
if get_bit_at_from_msb(&self.bitmask, idx) == 1 {
th.placeholder()
} else {
position += 1;
self.side_nodes[position - 1].clone()
}
})
.collect::<Vec<_>>();
Ok(SparseMerkleProof {
side_nodes: nodes,
non_membership_leaf_data: self.non_membership_leaf_data.clone(),
sibling_data: self.sibling_data.clone(),
_marker: PhantomData,
})
}
pub fn decompact_into(self) -> Result<SparseMerkleProof<H>, BadProof> {
let mut th = TreeHasher::<H>::new(vec![0; TreeHasher::<H>::path_size()].into());
if !self.sanity_check(&mut th) {
return Err(BadProof);
}
let mut position = 0;
let SparseCompactMerkleProof {
side_nodes,
non_membership_leaf_data,
sibling_data,
bitmask,
num_side_nodes,
_marker,
} = self;
let nodes = (0..num_side_nodes)
.map(|idx| {
if get_bit_at_from_msb(&bitmask, idx) == 1 {
th.placeholder()
} else {
position += 1;
side_nodes[position - 1].clone()
}
})
.collect::<Vec<_>>();
Ok(SparseMerkleProof {
side_nodes: nodes,
non_membership_leaf_data,
sibling_data,
_marker,
})
}
}