#![allow(clippy::len_without_is_empty)]
use std::fmt::Debug;
use std::marker::PhantomData;
use std::slice::Iter;
use anyhow::{ensure, Result};
use blstrs::Scalar as Fr;
use filecoin_hashers::{Hasher, PoseidonArity};
use generic_array::typenum::{Unsigned, U0};
use merkletree::hash::Algorithm;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::drgraph::graph_height;
pub trait MerkleProofTrait: Clone + Serialize + DeserializeOwned + Debug + Sync + Send {
type Hasher: Hasher;
type Arity: 'static + PoseidonArity;
type SubTreeArity: 'static + PoseidonArity;
type TopTreeArity: 'static + PoseidonArity;
fn try_from_proof(
p: merkletree::proof::Proof<<Self::Hasher as Hasher>::Domain, Self::Arity>,
) -> Result<Self>;
fn as_options(&self) -> Vec<(Vec<Option<Fr>>, Option<usize>)> {
self.path()
.iter()
.map(|v| {
(
v.0.iter().copied().map(Into::into).map(Some).collect(),
Some(v.1),
)
})
.collect::<Vec<_>>()
}
fn into_options_with_leaf(self) -> (Option<Fr>, Vec<(Vec<Option<Fr>>, Option<usize>)>) {
let leaf = self.leaf();
let path = self.path();
(
Some(leaf.into()),
path.into_iter()
.map(|(a, b)| {
(
a.iter().copied().map(Into::into).map(Some).collect(),
Some(b),
)
})
.collect::<Vec<_>>(),
)
}
fn as_pairs(&self) -> Vec<(Vec<Fr>, usize)> {
self.path()
.iter()
.map(|v| (v.0.iter().copied().map(Into::into).collect(), v.1))
.collect::<Vec<_>>()
}
fn verify(&self) -> bool;
fn validate(&self, node: usize) -> bool {
if !self.verify() {
return false;
}
node == self.path_index()
}
fn validate_data(&self, data: <Self::Hasher as Hasher>::Domain) -> bool {
if !self.verify() {
return false;
}
self.leaf() == data
}
fn leaf(&self) -> <Self::Hasher as Hasher>::Domain;
fn root(&self) -> <Self::Hasher as Hasher>::Domain;
fn len(&self) -> usize;
fn path(&self) -> Vec<(Vec<<Self::Hasher as Hasher>::Domain>, usize)>;
fn path_index(&self) -> usize {
self.path()
.iter()
.rev()
.fold(0, |acc, (_, index)| (acc * Self::Arity::to_usize()) + index)
}
fn proves_challenge(&self, challenge: usize) -> bool {
self.path_index() == challenge
}
fn expected_len(&self, leaves: usize) -> usize {
compound_path_length::<Self::Arity, Self::SubTreeArity, Self::TopTreeArity>(leaves)
}
}
pub fn base_path_length<A: Unsigned, B: Unsigned, C: Unsigned>(leaves: usize) -> usize {
let leaves = if C::to_usize() > 0 {
leaves / C::to_usize() / B::to_usize()
} else if B::to_usize() > 0 {
leaves / B::to_usize()
} else {
leaves
};
graph_height::<A>(leaves) - 1
}
pub fn compound_path_length<A: Unsigned, B: Unsigned, C: Unsigned>(leaves: usize) -> usize {
let mut len = base_path_length::<A, B, C>(leaves);
if B::to_usize() > 0 {
len += 1;
}
if C::to_usize() > 0 {
len += 1;
}
len
}
pub fn compound_tree_height<A: Unsigned, B: Unsigned, C: Unsigned>(leaves: usize) -> usize {
let a = graph_height::<A>(leaves) - 1;
let b = if B::to_usize() > 0 {
B::to_usize() - 1
} else {
0
};
let c = if C::to_usize() > 0 {
C::to_usize() - 1
} else {
0
};
a + b + c
}
macro_rules! forward_method {
($caller:expr, $name:ident) => {
match $caller {
ProofData::Single(ref proof) => proof.$name(),
ProofData::Sub(ref proof) => proof.$name(),
ProofData::Top(ref proof) => proof.$name(),
}
};
($caller:expr, $name:ident, $( $args:expr ),+) => {
match $caller {
ProofData::Single(ref proof) => proof.$name($($args),+),
ProofData::Sub(ref proof) => proof.$name($($args),+),
ProofData::Top(ref proof) => proof.$name($($args),+),
}
};
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct InclusionPath<H: Hasher, Arity: PoseidonArity> {
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
path: Vec<PathElement<H, Arity>>,
}
impl<H: Hasher, Arity: PoseidonArity> From<Vec<PathElement<H, Arity>>> for InclusionPath<H, Arity> {
fn from(path: Vec<PathElement<H, Arity>>) -> Self {
Self { path }
}
}
impl<H: Hasher, Arity: PoseidonArity> InclusionPath<H, Arity> {
pub fn root(&self, leaf: H::Domain) -> H::Domain {
let mut a = H::Function::default();
(0..self.path.len()).fold(leaf, |h, height| {
a.reset();
let index = self.path[height].index;
let mut nodes = self.path[height].hashes.clone();
nodes.insert(index, h);
a.multi_node(&nodes, height)
})
}
pub fn len(&self) -> usize {
self.path.len()
}
pub fn is_empty(&self) -> bool {
self.path.is_empty()
}
pub fn iter(&self) -> Iter<'_, PathElement<H, Arity>> {
self.path.iter()
}
pub fn path_index(&self) -> usize {
self.path
.iter()
.rev()
.fold(0, |acc, p| (acc * Arity::to_usize()) + p.index)
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct PathElement<H: Hasher, Arity: PoseidonArity> {
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
hashes: Vec<H::Domain>,
index: usize,
#[serde(skip)]
_arity: PhantomData<Arity>,
}
impl<H: Hasher, Arity: PoseidonArity> From<(Vec<H::Domain>, usize)> for PathElement<H, Arity> {
#[inline]
fn from((hashes, index): (Vec<H::Domain>, usize)) -> Self {
PathElement {
hashes,
index,
_arity: PhantomData,
}
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct MerkleProof<
H: Hasher,
BaseArity: PoseidonArity,
SubTreeArity: PoseidonArity = U0,
TopTreeArity: PoseidonArity = U0,
> {
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
data: ProofData<H, BaseArity, SubTreeArity, TopTreeArity>,
}
impl<
H: Hasher,
Arity: 'static + PoseidonArity,
SubTreeArity: 'static + PoseidonArity,
TopTreeArity: 'static + PoseidonArity,
> MerkleProofTrait for MerkleProof<H, Arity, SubTreeArity, TopTreeArity>
{
type Hasher = H;
type Arity = Arity;
type SubTreeArity = SubTreeArity;
type TopTreeArity = TopTreeArity;
fn try_from_proof(
p: merkletree::proof::Proof<<Self::Hasher as Hasher>::Domain, Self::Arity>,
) -> Result<Self> {
if p.top_layer_nodes() > 0 {
Ok(MerkleProof {
data: ProofData::Top(TopProof::try_from_proof(p)?),
})
} else if p.sub_layer_nodes() > 0 {
Ok(MerkleProof {
data: ProofData::Sub(SubProof::try_from_proof(p)?),
})
} else {
Ok(MerkleProof {
data: ProofData::Single(SingleProof::try_from_proof(p)?),
})
}
}
fn verify(&self) -> bool {
forward_method!(self.data, verify)
}
fn leaf(&self) -> H::Domain {
forward_method!(self.data, leaf)
}
fn root(&self) -> H::Domain {
forward_method!(self.data, root)
}
fn len(&self) -> usize {
forward_method!(self.data, len)
}
fn path(&self) -> Vec<(Vec<H::Domain>, usize)> {
forward_method!(self.data, path)
}
fn path_index(&self) -> usize {
forward_method!(self.data, path_index)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
enum ProofData<
H: Hasher,
BaseArity: PoseidonArity,
SubTreeArity: PoseidonArity,
TopTreeArity: PoseidonArity,
> {
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
Single(SingleProof<H, BaseArity>),
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
Sub(SubProof<H, BaseArity, SubTreeArity>),
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
Top(TopProof<H, BaseArity, SubTreeArity, TopTreeArity>),
}
impl<
H: Hasher,
Arity: 'static + PoseidonArity,
SubTreeArity: 'static + PoseidonArity,
TopTreeArity: 'static + PoseidonArity,
> Default for ProofData<H, Arity, SubTreeArity, TopTreeArity>
{
fn default() -> Self {
ProofData::Single(SingleProof::default())
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
struct SingleProof<H: Hasher, Arity: PoseidonArity> {
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
root: H::Domain,
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
leaf: H::Domain,
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
path: InclusionPath<H, Arity>,
}
impl<H: Hasher, Arity: PoseidonArity> SingleProof<H, Arity> {
pub fn new(path: InclusionPath<H, Arity>, root: H::Domain, leaf: H::Domain) -> Self {
SingleProof { root, leaf, path }
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
struct SubProof<H: Hasher, BaseArity: PoseidonArity, SubTreeArity: PoseidonArity> {
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
base_proof: InclusionPath<H, BaseArity>,
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
sub_proof: InclusionPath<H, SubTreeArity>,
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
root: H::Domain,
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
leaf: H::Domain,
}
impl<H: Hasher, BaseArity: PoseidonArity, SubTreeArity: PoseidonArity>
SubProof<H, BaseArity, SubTreeArity>
{
pub fn new(
base_proof: InclusionPath<H, BaseArity>,
sub_proof: InclusionPath<H, SubTreeArity>,
root: H::Domain,
leaf: H::Domain,
) -> Self {
Self {
base_proof,
sub_proof,
root,
leaf,
}
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
struct TopProof<
H: Hasher,
BaseArity: PoseidonArity,
SubTreeArity: PoseidonArity,
TopTreeArity: PoseidonArity,
> {
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
base_proof: InclusionPath<H, BaseArity>,
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
sub_proof: InclusionPath<H, SubTreeArity>,
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
top_proof: InclusionPath<H, TopTreeArity>,
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
root: H::Domain,
#[serde(bound(
serialize = "H::Domain: Serialize",
deserialize = "H::Domain: Deserialize<'de>"
))]
leaf: H::Domain,
}
impl<
H: Hasher,
BaseArity: PoseidonArity,
SubTreeArity: PoseidonArity,
TopTreeArity: PoseidonArity,
> TopProof<H, BaseArity, SubTreeArity, TopTreeArity>
{
pub fn new(
base_proof: InclusionPath<H, BaseArity>,
sub_proof: InclusionPath<H, SubTreeArity>,
top_proof: InclusionPath<H, TopTreeArity>,
root: H::Domain,
leaf: H::Domain,
) -> Self {
Self {
base_proof,
sub_proof,
top_proof,
root,
leaf,
}
}
}
impl<
H: Hasher,
BaseArity: PoseidonArity,
SubTreeArity: PoseidonArity,
TopTreeArity: PoseidonArity,
> MerkleProof<H, BaseArity, SubTreeArity, TopTreeArity>
{
pub fn new(n: usize) -> Self {
let root = Default::default();
let leaf = Default::default();
let path_elem = PathElement {
hashes: vec![Default::default(); BaseArity::to_usize()],
index: 0,
_arity: Default::default(),
};
let path = vec![path_elem; n];
MerkleProof {
data: ProofData::Single(SingleProof::new(path.into(), root, leaf)),
}
}
pub fn from_parts(
leaf: H::Domain,
root: H::Domain,
mut path: Vec<(Vec<H::Domain>, usize)>,
) -> Self {
if SubTreeArity::to_usize() == 0 {
let base_path = path.into_iter().map(PathElement::from).collect::<Vec<_>>();
MerkleProof {
data: ProofData::Single(SingleProof {
leaf,
root,
path: base_path.into(),
}),
}
} else if TopTreeArity::to_usize() == 0 {
let sub_elem = path.pop().expect("path should not be empty");
let base_path = path.into_iter().map(PathElement::from).collect::<Vec<_>>();
MerkleProof {
data: ProofData::Sub(SubProof {
leaf,
root,
base_proof: base_path.into(),
sub_proof: vec![PathElement::from(sub_elem)].into(),
}),
}
} else {
let top_elem = path.pop().expect("path should not be empty");
let sub_elem = path.pop().expect("path should not be empty");
let base_path = path.into_iter().map(PathElement::from).collect::<Vec<_>>();
MerkleProof {
data: ProofData::Top(TopProof {
leaf,
root,
base_proof: base_path.into(),
sub_proof: vec![PathElement::from(sub_elem)].into(),
top_proof: vec![PathElement::from(top_elem)].into(),
}),
}
}
}
}
fn proof_to_single<H: Hasher, Arity: PoseidonArity, TargetArity: PoseidonArity>(
proof: &merkletree::proof::Proof<H::Domain, Arity>,
lemma_start_index: usize,
sub_root: Option<H::Domain>,
) -> SingleProof<H, TargetArity> {
let root = proof.root();
let leaf = if let Some(sub_root) = sub_root {
sub_root
} else {
proof.item()
};
let path = extract_path::<H, TargetArity>(proof.lemma(), proof.path(), lemma_start_index);
SingleProof::new(path, root, leaf)
}
fn extract_path<H: Hasher, Arity: PoseidonArity>(
lemma: &[H::Domain],
path: &[usize],
lemma_start_index: usize,
) -> InclusionPath<H, Arity> {
let path = lemma[lemma_start_index..lemma.len() - 1]
.chunks(Arity::to_usize() - 1)
.zip(path.iter())
.map(|(hashes, index)| PathElement {
hashes: hashes.to_vec(),
index: *index,
_arity: Default::default(),
})
.collect::<Vec<_>>();
path.into()
}
impl<H: Hasher, Arity: 'static + PoseidonArity> SingleProof<H, Arity> {
fn try_from_proof(p: merkletree::proof::Proof<<H as Hasher>::Domain, Arity>) -> Result<Self> {
Ok(proof_to_single(&p, 1, None))
}
fn verify(&self) -> bool {
let calculated_root = self.path.root(self.leaf);
self.root == calculated_root
}
fn leaf(&self) -> H::Domain {
self.leaf
}
fn root(&self) -> H::Domain {
self.root
}
fn len(&self) -> usize {
self.path.len() * (Arity::to_usize() - 1) + 2
}
fn path(&self) -> Vec<(Vec<H::Domain>, usize)> {
self.path
.iter()
.map(|x| (x.hashes.clone(), x.index))
.collect::<Vec<_>>()
}
fn path_index(&self) -> usize {
self.path.path_index()
}
}
impl<H: Hasher, Arity: 'static + PoseidonArity, SubTreeArity: 'static + PoseidonArity>
SubProof<H, Arity, SubTreeArity>
{
fn try_from_proof(p: merkletree::proof::Proof<<H as Hasher>::Domain, Arity>) -> Result<Self> {
ensure!(
p.sub_layer_nodes() == SubTreeArity::to_usize(),
"sub arity mismatch"
);
ensure!(
p.sub_tree_proof.is_some(),
"Cannot generate sub proof without a base-proof"
);
let base_p = p.sub_tree_proof.as_ref().expect("proof as_ref failure");
let root = p.root();
let leaf = base_p.item();
let base_proof = extract_path::<H, Arity>(base_p.lemma(), base_p.path(), 1);
let sub_proof = extract_path::<H, SubTreeArity>(p.lemma(), p.path(), 0);
Ok(SubProof::new(base_proof, sub_proof, root, leaf))
}
fn verify(&self) -> bool {
let sub_leaf = self.base_proof.root(self.leaf);
let calculated_root = self.sub_proof.root(sub_leaf);
self.root == calculated_root
}
fn leaf(&self) -> H::Domain {
self.leaf
}
fn root(&self) -> H::Domain {
self.root
}
fn len(&self) -> usize {
SubTreeArity::to_usize()
}
fn path(&self) -> Vec<(Vec<H::Domain>, usize)> {
self.base_proof
.iter()
.map(|x| (x.hashes.clone(), x.index))
.chain(self.sub_proof.iter().map(|x| (x.hashes.clone(), x.index)))
.collect()
}
fn path_index(&self) -> usize {
let mut base_proof_leaves = 1;
for _i in 0..self.base_proof.len() {
base_proof_leaves *= Arity::to_usize()
}
let sub_proof_index = self.sub_proof.path_index();
(sub_proof_index * base_proof_leaves) + self.base_proof.path_index()
}
}
impl<
H: Hasher,
Arity: 'static + PoseidonArity,
SubTreeArity: 'static + PoseidonArity,
TopTreeArity: 'static + PoseidonArity,
> TopProof<H, Arity, SubTreeArity, TopTreeArity>
{
fn try_from_proof(p: merkletree::proof::Proof<<H as Hasher>::Domain, Arity>) -> Result<Self> {
ensure!(
p.top_layer_nodes() == TopTreeArity::to_usize(),
"top arity mismatch"
);
ensure!(
p.sub_layer_nodes() == SubTreeArity::to_usize(),
"sub arity mismatch"
);
ensure!(
p.sub_tree_proof.is_some(),
"Cannot generate top proof without a sub-proof"
);
let sub_p = p.sub_tree_proof.as_ref().expect("proofs as ref failure");
ensure!(
sub_p.sub_tree_proof.is_some(),
"Cannot generate top proof without a base-proof"
);
let base_p = sub_p
.sub_tree_proof
.as_ref()
.expect("proofs as ref failure");
let root = p.root();
let leaf = base_p.item();
let base_proof = extract_path::<H, Arity>(base_p.lemma(), base_p.path(), 1);
let sub_proof = extract_path::<H, SubTreeArity>(sub_p.lemma(), sub_p.path(), 0);
let top_proof = extract_path::<H, TopTreeArity>(p.lemma(), p.path(), 0);
Ok(TopProof::new(base_proof, sub_proof, top_proof, root, leaf))
}
fn verify(&self) -> bool {
let sub_leaf = self.base_proof.root(self.leaf);
let top_leaf = self.sub_proof.root(sub_leaf);
let calculated_root = self.top_proof.root(top_leaf);
self.root == calculated_root
}
fn leaf(&self) -> H::Domain {
self.leaf
}
fn root(&self) -> H::Domain {
self.root
}
fn len(&self) -> usize {
TopTreeArity::to_usize()
}
fn path(&self) -> Vec<(Vec<H::Domain>, usize)> {
self.base_proof
.iter()
.map(|x| (x.hashes.clone(), x.index))
.chain(self.sub_proof.iter().map(|x| (x.hashes.clone(), x.index)))
.chain(self.top_proof.iter().map(|x| (x.hashes.clone(), x.index)))
.collect()
}
fn path_index(&self) -> usize {
let mut base_proof_leaves = 1;
for _i in 0..self.base_proof.len() {
base_proof_leaves *= Arity::to_usize()
}
let sub_proof_leaves = base_proof_leaves * SubTreeArity::to_usize();
let sub_proof_index = self.sub_proof.path_index();
let top_proof_index = self.top_proof.path_index();
(sub_proof_index * base_proof_leaves)
+ (top_proof_index * sub_proof_leaves)
+ self.base_proof.path_index()
}
}
#[cfg(test)]
mod tests {
use super::*;
use filecoin_hashers::{
blake2s::Blake2sHasher, poseidon::PoseidonHasher, sha256::Sha256Hasher, Domain,
};
use generic_array::typenum::{U2, U4, U8};
use rand::thread_rng;
use crate::merkle::{
generate_tree, get_base_tree_count, DiskStore, MerkleTreeTrait, MerkleTreeWrapper,
};
fn merklepath<Tree: 'static + MerkleTreeTrait>() {
let node_size = 32;
let nodes = 64 * get_base_tree_count::<Tree>();
let mut rng = thread_rng();
let (data, tree) = generate_tree::<Tree, _>(&mut rng, nodes, None);
for i in 0..nodes {
let proof = tree.gen_proof(i).expect("gen_proof failure");
assert!(proof.verify(), "failed to validate");
assert!(proof.validate(i), "failed to validate valid merkle path");
let data_slice = &data[i * node_size..(i + 1) * node_size].to_vec();
assert!(
proof.validate_data(
<Tree::Hasher as Hasher>::Domain::try_from_bytes(data_slice)
.expect("try from bytes failure")
),
"failed to validate valid data"
);
}
}
#[test]
fn merklepath_poseidon_2() {
merklepath::<
MerkleTreeWrapper<
PoseidonHasher,
DiskStore<<PoseidonHasher as Hasher>::Domain>,
U2,
U0,
U0,
>,
>();
}
#[test]
fn merklepath_poseidon_4() {
merklepath::<
MerkleTreeWrapper<
PoseidonHasher,
DiskStore<<PoseidonHasher as Hasher>::Domain>,
U4,
U0,
U0,
>,
>();
}
#[test]
fn merklepath_poseidon_8() {
merklepath::<
MerkleTreeWrapper<
PoseidonHasher,
DiskStore<<PoseidonHasher as Hasher>::Domain>,
U8,
U0,
U0,
>,
>();
}
#[test]
fn merklepath_poseidon_8_2() {
merklepath::<
MerkleTreeWrapper<
PoseidonHasher,
DiskStore<<PoseidonHasher as Hasher>::Domain>,
U8,
U2,
U0,
>,
>();
}
#[test]
fn merklepath_poseidon_8_4() {
merklepath::<
MerkleTreeWrapper<
PoseidonHasher,
DiskStore<<PoseidonHasher as Hasher>::Domain>,
U8,
U4,
U0,
>,
>();
}
#[test]
fn merklepath_poseidon_8_4_2() {
merklepath::<
MerkleTreeWrapper<
PoseidonHasher,
DiskStore<<PoseidonHasher as Hasher>::Domain>,
U8,
U4,
U2,
>,
>();
}
#[test]
fn merklepath_sha256_2() {
merklepath::<
MerkleTreeWrapper<
Sha256Hasher,
DiskStore<<Sha256Hasher as Hasher>::Domain>,
U2,
U0,
U0,
>,
>();
}
#[test]
fn merklepath_sha256_4() {
merklepath::<
MerkleTreeWrapper<
Sha256Hasher,
DiskStore<<Sha256Hasher as Hasher>::Domain>,
U4,
U0,
U0,
>,
>();
}
#[test]
fn merklepath_sha256_2_4() {
merklepath::<
MerkleTreeWrapper<
Sha256Hasher,
DiskStore<<Sha256Hasher as Hasher>::Domain>,
U2,
U4,
U0,
>,
>();
}
#[test]
fn merklepath_sha256_top_2_4_2() {
merklepath::<
MerkleTreeWrapper<
Sha256Hasher,
DiskStore<<Sha256Hasher as Hasher>::Domain>,
U2,
U4,
U2,
>,
>();
}
#[test]
fn merklepath_blake2s_2() {
merklepath::<
MerkleTreeWrapper<
Blake2sHasher,
DiskStore<<Blake2sHasher as Hasher>::Domain>,
U2,
U0,
U0,
>,
>();
}
#[test]
fn merklepath_blake2s_4() {
merklepath::<
MerkleTreeWrapper<
Blake2sHasher,
DiskStore<<Blake2sHasher as Hasher>::Domain>,
U4,
U0,
U0,
>,
>();
}
#[test]
fn merklepath_blake2s_8_4_2() {
merklepath::<
MerkleTreeWrapper<
Blake2sHasher,
DiskStore<<Blake2sHasher as Hasher>::Domain>,
U8,
U4,
U2,
>,
>();
}
}