Allow CommitmentTree completeness & auth paths to be computed at arbitrary depths.

Fixes #546
This commit is contained in:
Kris Nuttycombe 2022-05-04 17:08:44 -06:00
parent 4969d81ad3
commit a63a37aab2
2 changed files with 22 additions and 16 deletions

View File

@ -5,7 +5,7 @@
#![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))]
// Catch documentation errors caused by code changes. // Catch documentation errors caused by code changes.
#![deny(broken_intra_doc_links)] #![deny(rustdoc::broken_intra_doc_links)]
// Temporary until we have addressed all Result<T, ()> cases. // Temporary until we have addressed all Result<T, ()> cases.
#![allow(clippy::result_unit_err)] #![allow(clippy::result_unit_err)]

View File

@ -9,6 +9,7 @@ use incrementalmerkletree::{
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 std::iter::repeat;
use zcash_encoding::{Optional, Vector}; use zcash_encoding::{Optional, Vector};
use crate::sapling::SAPLING_COMMITMENT_TREE_DEPTH; use crate::sapling::SAPLING_COMMITMENT_TREE_DEPTH;
@ -199,8 +200,12 @@ impl<Node> CommitmentTree<Node> {
} else { } else {
self.left.is_some() self.left.is_some()
&& self.right.is_some() && self.right.is_some()
&& self.parents.len() == depth - 1 && self
&& self.parents.iter().all(|p| p.is_some()) .parents
.iter()
.chain(repeat(&None))
.take(depth - 1)
.all(|p| p.is_some())
} }
} }
} }
@ -285,20 +290,17 @@ impl<Node: Hashable> CommitmentTree<Node> {
&self.right.unwrap_or_else(|| filler.next(0)), &self.right.unwrap_or_else(|| filler.next(0)),
); );
// 2) Hash in parents up to the currently-filled depth. // 2) Extend the parents to the desired depth with None values, then hash from leaf to
// - Roots of the empty subtrees are used as needed. // root. Roots of the empty subtrees are used as needed.
let mid_root = self self.parents
.parents
.iter() .iter()
.chain(repeat(&None))
.take(depth - 1)
.enumerate() .enumerate()
.fold(leaf_root, |root, (i, p)| match p { .fold(leaf_root, |root, (i, p)| match p {
Some(node) => Node::combine(i + 1, node, &root), Some(node) => Node::combine(i + 1, node, &root),
None => Node::combine(i + 1, &root, &filler.next(i + 1)), None => Node::combine(i + 1, &root, &filler.next(i + 1)),
}); })
// 3) Hash in roots of the empty subtrees up to the final depth.
((self.parents.len() + 1)..depth)
.fold(mid_root, |root, d| Node::combine(d, &root, &filler.next(d)))
} }
} }
@ -498,16 +500,20 @@ impl<Node: Hashable> IncrementalWitness<Node> {
return None; return None;
} }
for (i, p) in self.tree.parents.iter().enumerate() { for (i, p) in self
.tree
.parents
.iter()
.chain(repeat(&None))
.take(depth - 1)
.enumerate()
{
auth_path.push(match p { auth_path.push(match p {
Some(node) => (*node, true), Some(node) => (*node, true),
None => (filler.next(i + 1), false), None => (filler.next(i + 1), false),
}); });
} }
for i in self.tree.parents.len()..(depth - 1) {
auth_path.push((filler.next(i + 1), false));
}
assert_eq!(auth_path.len(), depth); assert_eq!(auth_path.len(), depth);
Some(MerklePath::from_path(auth_path, self.position() as u64)) Some(MerklePath::from_path(auth_path, self.position() as u64))