Merge pull request #534 from nuttycom/commitmenttree_from_frontier

Add conversion from incrementalmerkletree::bridgetree::Frontier -> CommitmentTree
This commit is contained in:
Kris Nuttycombe 2022-04-08 15:15:34 -06:00 committed by GitHub
commit d5c5f04894
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 11 deletions

View File

@ -1,13 +1,17 @@
//! Implementation of a Merkle tree of commitments used to prove the existence of notes. //! Implementation of a Merkle tree of commitments used to prove the existence of notes.
use byteorder::{LittleEndian, ReadBytesExt}; use byteorder::{LittleEndian, ReadBytesExt};
use incrementalmerkletree::{self, bridgetree, Altitude}; use incrementalmerkletree::{
self,
bridgetree::{self, Leaf},
Altitude,
};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use zcash_encoding::{Optional, Vector}; use zcash_encoding::{Optional, Vector};
use crate::sapling::{SAPLING_COMMITMENT_TREE_DEPTH, SAPLING_COMMITMENT_TREE_DEPTH_U8}; use crate::sapling::SAPLING_COMMITMENT_TREE_DEPTH;
pub mod incremental; pub mod incremental;
@ -100,7 +104,7 @@ impl<Node: Hashable> PathFiller<Node> {
/// ///
/// The depth of the Merkle tree is fixed at 32, equal to the depth of the Sapling /// The depth of the Merkle tree is fixed at 32, equal to the depth of the Sapling
/// commitment tree. /// commitment tree.
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct CommitmentTree<Node> { pub struct CommitmentTree<Node> {
pub(crate) left: Option<Node>, pub(crate) left: Option<Node>,
pub(crate) right: Option<Node>, pub(crate) right: Option<Node>,
@ -117,7 +121,35 @@ impl<Node> CommitmentTree<Node> {
} }
} }
pub fn to_frontier(&self) -> bridgetree::Frontier<Node, SAPLING_COMMITMENT_TREE_DEPTH_U8> pub fn from_frontier<const DEPTH: u8>(frontier: &bridgetree::Frontier<Node, DEPTH>) -> Self
where
Node: Clone,
{
frontier.value().map_or_else(Self::empty, |f| {
let (left, right) = match f.leaf() {
Leaf::Left(v) => (Some(v.clone()), None),
Leaf::Right(l, r) => (Some(l.clone()), Some(r.clone())),
};
let mut ommers_iter = f.ommers().iter().cloned();
let upos: usize = f.position().into();
Self {
left,
right,
parents: (1..DEPTH)
.into_iter()
.map(|i| {
if upos & (1 << i) == 0 {
None
} else {
ommers_iter.next()
}
})
.collect(),
}
})
}
pub fn to_frontier<const DEPTH: u8>(&self) -> bridgetree::Frontier<Node, DEPTH>
where where
Node: incrementalmerkletree::Hashable + Clone, Node: incrementalmerkletree::Hashable + Clone,
{ {
@ -576,12 +608,18 @@ impl<Node: Hashable> MerklePath<Node> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{CommitmentTree, Hashable, IncrementalWitness, MerklePath, PathFiller}; use incrementalmerkletree::bridgetree::Frontier;
use crate::sapling::Node; use proptest::prelude::*;
use std::convert::TryInto; use std::convert::TryInto;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use crate::sapling::{testing::arb_node, Node};
use super::{
testing::arb_commitment_tree, CommitmentTree, Hashable, IncrementalWitness, MerklePath,
PathFiller,
};
const HEX_EMPTY_ROOTS: [&str; 33] = [ const HEX_EMPTY_ROOTS: [&str; 33] = [
"0100000000000000000000000000000000000000000000000000000000000000", "0100000000000000000000000000000000000000000000000000000000000000",
"817de36ab2d57feb077634bca77819c8e0bd298c04f6fed0e6a83cc1356ca155", "817de36ab2d57feb077634bca77819c8e0bd298c04f6fed0e6a83cc1356ca155",
@ -1137,6 +1175,17 @@ mod tests {
assert!(witness.append(node).is_err()); assert!(witness.append(node).is_err());
} }
} }
proptest! {
#[test]
fn prop_commitment_tree_roundtrip(ct in arb_commitment_tree(32, arb_node(), 8)) {
let frontier: Frontier<Node, 8> = ct.to_frontier();
let ct0 = CommitmentTree::from_frontier(&frontier);
assert_eq!(ct, ct0);
let frontier0: Frontier<Node, 8> = ct0.to_frontier();
assert_eq!(frontier, frontier0);
}
}
} }
#[cfg(any(test, feature = "test-dependencies"))] #[cfg(any(test, feature = "test-dependencies"))]
@ -1150,12 +1199,15 @@ pub mod testing {
pub fn arb_commitment_tree<Node: Hashable + Debug, T: Strategy<Value = Node>>( pub fn arb_commitment_tree<Node: Hashable + Debug, T: Strategy<Value = Node>>(
min_size: usize, min_size: usize,
arb_node: T, arb_node: T,
depth: u8,
) -> impl Strategy<Value = CommitmentTree<Node>> { ) -> impl Strategy<Value = CommitmentTree<Node>> {
vec(arb_node, min_size..(min_size + 100)).prop_map(|v| { assert!((1 << depth) >= min_size + 100);
vec(arb_node, min_size..(min_size + 100)).prop_map(move |v| {
let mut tree = CommitmentTree::empty(); let mut tree = CommitmentTree::empty();
for node in v.into_iter() { for node in v.into_iter() {
tree.append(node).unwrap(); tree.append(node).unwrap();
} }
tree.parents.resize_with((depth - 1).into(), || None);
tree tree
}) })
} }

View File

@ -226,7 +226,7 @@ mod tests {
proptest! { proptest! {
#[test] #[test]
fn frontier_serialization_v0(t in arb_commitment_tree(0, sapling::arb_node())) fn frontier_serialization_v0(t in arb_commitment_tree(0, sapling::arb_node(), 32))
{ {
let mut buffer = vec![]; let mut buffer = vec![];
t.write(&mut buffer).unwrap(); t.write(&mut buffer).unwrap();
@ -237,7 +237,7 @@ mod tests {
} }
#[test] #[test]
fn frontier_serialization_v1(t in arb_commitment_tree(1, sapling::arb_node())) fn frontier_serialization_v1(t in arb_commitment_tree(1, sapling::arb_node(), 32))
{ {
let original: Frontier<Node, 32> = t.to_frontier(); let original: Frontier<Node, 32> = t.to_frontier();

View File

@ -581,7 +581,7 @@ pub mod testing {
n_notes n_notes
), ),
commitment_trees in vec( commitment_trees in vec(
arb_commitment_tree(n_notes, arb_node()).prop_map( arb_commitment_tree(n_notes, arb_node(), 32).prop_map(
|t| IncrementalWitness::from_tree(&t).path().unwrap() |t| IncrementalWitness::from_tree(&t).path().unwrap()
), ),
n_notes n_notes