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.
use byteorder::{LittleEndian, ReadBytesExt};
use incrementalmerkletree::{self, bridgetree, Altitude};
use incrementalmerkletree::{
self,
bridgetree::{self, Leaf},
Altitude,
};
use std::collections::VecDeque;
use std::convert::TryFrom;
use std::io::{self, Read, Write};
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;
@ -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
/// commitment tree.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CommitmentTree<Node> {
pub(crate) left: 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
Node: incrementalmerkletree::Hashable + Clone,
{
@ -576,12 +608,18 @@ impl<Node: Hashable> MerklePath<Node> {
#[cfg(test)]
mod tests {
use super::{CommitmentTree, Hashable, IncrementalWitness, MerklePath, PathFiller};
use crate::sapling::Node;
use incrementalmerkletree::bridgetree::Frontier;
use proptest::prelude::*;
use std::convert::TryInto;
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] = [
"0100000000000000000000000000000000000000000000000000000000000000",
"817de36ab2d57feb077634bca77819c8e0bd298c04f6fed0e6a83cc1356ca155",
@ -1137,6 +1175,17 @@ mod tests {
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"))]
@ -1150,12 +1199,15 @@ pub mod testing {
pub fn arb_commitment_tree<Node: Hashable + Debug, T: Strategy<Value = Node>>(
min_size: usize,
arb_node: T,
depth: u8,
) -> 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();
for node in v.into_iter() {
tree.append(node).unwrap();
}
tree.parents.resize_with((depth - 1).into(), || None);
tree
})
}

View File

@ -226,7 +226,7 @@ mod tests {
proptest! {
#[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![];
t.write(&mut buffer).unwrap();
@ -237,7 +237,7 @@ mod tests {
}
#[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();

View File

@ -581,7 +581,7 @@ pub mod testing {
n_notes
),
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()
),
n_notes