Move merkle_tree::Node into sapling module

This makes the merkle_tree module properly generic over the tree hash.
It still hard-codes a depth 32 tree, because Rust doesn't yet support
generic sizes, and we are unlikely to need to alter the tree depth in
future circuit changes.
This commit is contained in:
Jack Grigg
2019-04-16 00:27:44 +01:00
parent 263bbe1207
commit b9cea33804
3 changed files with 118 additions and 109 deletions

View File

@@ -1,81 +1,35 @@
use byteorder::{LittleEndian, ReadBytesExt}; use byteorder::{LittleEndian, ReadBytesExt};
use ff::{PrimeField, PrimeFieldRepr}; use sapling_crypto::circuit::sapling::TREE_DEPTH;
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use sapling_crypto::{circuit::sapling::TREE_DEPTH, primitives::Note};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::iter; use std::iter;
use sapling::merkle_hash; use sapling::SAPLING_COMMITMENT_TREE_DEPTH;
use serialize::{Optional, Vector}; use serialize::{Optional, Vector};
const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32; /// A hashable node within a Merkle tree.
pub trait Hashable: Clone + Copy {
/// Parses a node from the given byte source.
fn read<R: Read>(reader: R) -> io::Result<Self>;
/// Serializes this node.
fn write<W: Write>(&self, writer: W) -> io::Result<()>;
trait Hashable: Clone + Copy {
/// Returns the parent node within the tree of the two given nodes. /// Returns the parent node within the tree of the two given nodes.
fn combine(usize, &Self, &Self) -> Self; fn combine(usize, &Self, &Self) -> Self;
/// Returns a blank leaf node. /// Returns a blank leaf node.
fn blank() -> Self; fn blank() -> Self;
/// Returns the empty root for the given depth.
fn empty_root(usize) -> Self;
} }
#[derive(Clone, Copy, Debug, PartialEq)] struct PathFiller<Node: Hashable> {
pub struct Node {
repr: FrRepr,
}
impl Node {
pub fn new(repr: FrRepr) -> Self {
Node { repr }
}
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut repr = FrRepr::default();
repr.read_le(&mut reader)?;
Ok(Node::new(repr))
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
self.repr.write_le(&mut writer)
}
}
impl Hashable for Node {
fn combine(depth: usize, lhs: &Self, rhs: &Self) -> Self {
Node {
repr: merkle_hash(depth, &lhs.repr, &rhs.repr),
}
}
fn blank() -> Self {
Node {
repr: Note::<Bls12>::uncommitted().into_repr(),
}
}
}
impl From<Node> for Fr {
fn from(node: Node) -> Self {
Fr::from_repr(node.repr).expect("Tree nodes should be in the prime field")
}
}
lazy_static! {
static ref EMPTY_ROOTS: Vec<Node> = {
let mut v = vec![Node::blank()];
for d in 0..SAPLING_COMMITMENT_TREE_DEPTH {
let next = Node::combine(d, &v[d], &v[d]);
v.push(next);
}
v
};
}
struct PathFiller {
queue: VecDeque<Node>, queue: VecDeque<Node>,
} }
impl PathFiller { impl<Node: Hashable> PathFiller<Node> {
fn empty() -> Self { fn empty() -> Self {
PathFiller { PathFiller {
queue: VecDeque::new(), queue: VecDeque::new(),
@@ -83,19 +37,21 @@ impl PathFiller {
} }
fn next(&mut self, depth: usize) -> Node { fn next(&mut self, depth: usize) -> Node {
self.queue.pop_front().unwrap_or_else(|| EMPTY_ROOTS[depth]) self.queue
.pop_front()
.unwrap_or_else(|| Node::empty_root(depth))
} }
} }
/// A Merkle tree of Sapling note commitments. /// A Merkle tree of Sapling note commitments.
#[derive(Clone)] #[derive(Clone)]
pub struct CommitmentTree { pub struct CommitmentTree<Node: Hashable> {
left: Option<Node>, left: Option<Node>,
right: Option<Node>, right: Option<Node>,
parents: Vec<Option<Node>>, parents: Vec<Option<Node>>,
} }
impl CommitmentTree { impl<Node: Hashable> CommitmentTree<Node> {
/// Creates an empty tree. /// Creates an empty tree.
pub fn new() -> Self { pub fn new() -> Self {
CommitmentTree { CommitmentTree {
@@ -192,7 +148,7 @@ impl CommitmentTree {
self.root_inner(SAPLING_COMMITMENT_TREE_DEPTH, PathFiller::empty()) self.root_inner(SAPLING_COMMITMENT_TREE_DEPTH, PathFiller::empty())
} }
fn root_inner(&self, depth: usize, mut filler: PathFiller) -> Node { fn root_inner(&self, depth: usize, mut filler: PathFiller<Node>) -> Node {
assert!(depth > 0); assert!(depth > 0);
// 1) Hash left and right leaves together. // 1) Hash left and right leaves together.
@@ -227,15 +183,15 @@ impl CommitmentTree {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct IncrementalWitness { pub struct IncrementalWitness<Node: Hashable> {
tree: CommitmentTree, tree: CommitmentTree<Node>,
filled: Vec<Node>, filled: Vec<Node>,
cursor_depth: usize, cursor_depth: usize,
cursor: Option<CommitmentTree>, cursor: Option<CommitmentTree<Node>>,
} }
impl IncrementalWitness { impl<Node: Hashable> IncrementalWitness<Node> {
pub fn from_tree(tree: &CommitmentTree) -> IncrementalWitness { pub fn from_tree(tree: &CommitmentTree<Node>) -> IncrementalWitness<Node> {
IncrementalWitness { IncrementalWitness {
tree: tree.clone(), tree: tree.clone(),
filled: vec![], filled: vec![],
@@ -272,7 +228,7 @@ impl IncrementalWitness {
self.tree.size() - 1 self.tree.size() - 1
} }
fn filler(&self) -> PathFiller { fn filler(&self) -> PathFiller<Node> {
let cursor_root = self let cursor_root = self
.cursor .cursor
.as_ref() .as_ref()
@@ -374,11 +330,11 @@ impl IncrementalWitness {
} }
/// Returns the current witness, or None if the tree is empty. /// Returns the current witness, or None if the tree is empty.
pub fn path(&self) -> Option<CommitmentTreeWitness> { pub fn path(&self) -> Option<CommitmentTreeWitness<Node>> {
self.path_inner(SAPLING_COMMITMENT_TREE_DEPTH) self.path_inner(SAPLING_COMMITMENT_TREE_DEPTH)
} }
fn path_inner(&self, depth: usize) -> Option<CommitmentTreeWitness> { fn path_inner(&self, depth: usize) -> Option<CommitmentTreeWitness<Node>> {
let mut filler = self.filler(); let mut filler = self.filler();
let mut auth_path = Vec::new(); let mut auth_path = Vec::new();
@@ -406,10 +362,7 @@ impl IncrementalWitness {
assert_eq!(auth_path.len(), depth); assert_eq!(auth_path.len(), depth);
Some(CommitmentTreeWitness::from_path( Some(CommitmentTreeWitness::from_path(
auth_path auth_path,
.iter()
.map(|n| n.map(|(node, b)| (node.into(), b)))
.collect(),
self.position() as u64, self.position() as u64,
)) ))
} }
@@ -418,13 +371,13 @@ impl IncrementalWitness {
/// A witness to a path from a position in a particular Sapling commitment tree /// A witness to a path from a position in a particular Sapling commitment tree
/// to the root of that tree. /// to the root of that tree.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct CommitmentTreeWitness { pub struct CommitmentTreeWitness<Node: Hashable> {
pub auth_path: Vec<Option<(Fr, bool)>>, pub auth_path: Vec<Option<(Node, bool)>>,
pub position: u64, pub position: u64,
} }
impl CommitmentTreeWitness { impl<Node: Hashable> CommitmentTreeWitness<Node> {
pub fn from_path(auth_path: Vec<Option<(Fr, bool)>>, position: u64) -> Self { pub fn from_path(auth_path: Vec<Option<(Node, bool)>>, position: u64) -> Self {
CommitmentTreeWitness { CommitmentTreeWitness {
auth_path, auth_path,
position, position,
@@ -449,7 +402,8 @@ impl CommitmentTreeWitness {
// The vector works in reverse // The vector works in reverse
for i in (0..depth).rev() { for i in (0..depth).rev() {
// skip length of inner vector // skip length of inner vector
if witness[0] != 32 { // the length of a pedersen hash if witness[0] != 32 {
// the length of a pedersen hash
return Err(()); return Err(());
} }
witness = &witness[1..]; witness = &witness[1..];
@@ -460,11 +414,7 @@ impl CommitmentTreeWitness {
witness = &witness[32..]; witness = &witness[32..];
// Sibling node should be an element of Fr // Sibling node should be an element of Fr
let sibling = match { let sibling = match Node::read(&sibling[..]) {
let mut repr = FrRepr::default();
repr.read_le(&sibling[..]).expect("length is 32 bytes");
Fr::from_repr(repr)
} {
Ok(p) => p, Ok(p) => p,
Err(_) => return Err(()), Err(_) => return Err(()),
}; };
@@ -506,10 +456,8 @@ impl CommitmentTreeWitness {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{ use super::{CommitmentTree, CommitmentTreeWitness, Hashable, IncrementalWitness, PathFiller};
CommitmentTree, CommitmentTreeWitness, Hashable, IncrementalWitness, Node, PathFiller, use sapling::Node;
EMPTY_ROOTS,
};
use ff::PrimeFieldRepr; use ff::PrimeFieldRepr;
use hex; use hex;
@@ -554,7 +502,7 @@ mod tests {
const TESTING_DEPTH: usize = 4; const TESTING_DEPTH: usize = 4;
struct TestCommitmentTree(CommitmentTree); struct TestCommitmentTree(CommitmentTree<Node>);
impl TestCommitmentTree { impl TestCommitmentTree {
fn new() -> Self { fn new() -> Self {
@@ -583,7 +531,7 @@ mod tests {
} }
} }
struct TestIncrementalWitness(IncrementalWitness); struct TestIncrementalWitness(IncrementalWitness<Node>);
impl TestIncrementalWitness { impl TestIncrementalWitness {
fn from_tree(tree: &TestCommitmentTree) -> Self { fn from_tree(tree: &TestCommitmentTree) -> Self {
@@ -607,7 +555,7 @@ mod tests {
self.0.root_inner(TESTING_DEPTH) self.0.root_inner(TESTING_DEPTH)
} }
fn path(&self) -> Option<CommitmentTreeWitness> { fn path(&self) -> Option<CommitmentTreeWitness<Node>> {
self.0.path_inner(TESTING_DEPTH) self.0.path_inner(TESTING_DEPTH)
} }
} }
@@ -616,9 +564,8 @@ mod tests {
fn empty_root_test_vectors() { fn empty_root_test_vectors() {
let mut tmp = [0u8; 32]; let mut tmp = [0u8; 32];
for i in 0..HEX_EMPTY_ROOTS.len() { for i in 0..HEX_EMPTY_ROOTS.len() {
EMPTY_ROOTS[i] Node::empty_root(i)
.repr .write(&mut tmp[..])
.write_le(&mut tmp[..])
.expect("length is 32 bytes"); .expect("length is 32 bytes");
assert_eq!(hex::encode(tmp), HEX_EMPTY_ROOTS[i]); assert_eq!(hex::encode(tmp), HEX_EMPTY_ROOTS[i]);
} }
@@ -627,10 +574,9 @@ mod tests {
#[test] #[test]
fn sapling_empty_root() { fn sapling_empty_root() {
let mut tmp = [0u8; 32]; let mut tmp = [0u8; 32];
CommitmentTree::new() CommitmentTree::<Node>::new()
.root() .root()
.repr .write(&mut tmp[..])
.write_le(&mut tmp[..])
.expect("length is 32 bytes"); .expect("length is 32 bytes");
assert_eq!( assert_eq!(
hex::encode(tmp), hex::encode(tmp),
@@ -640,12 +586,11 @@ mod tests {
#[test] #[test]
fn empty_commitment_tree_roots() { fn empty_commitment_tree_roots() {
let tree = CommitmentTree::new(); let tree = CommitmentTree::<Node>::new();
let mut tmp = [0u8; 32]; let mut tmp = [0u8; 32];
for i in 1..HEX_EMPTY_ROOTS.len() { for i in 1..HEX_EMPTY_ROOTS.len() {
tree.root_inner(i, PathFiller::empty()) tree.root_inner(i, PathFiller::empty())
.repr .write(&mut tmp[..])
.write_le(&mut tmp[..])
.expect("length is 32 bytes"); .expect("length is 32 bytes");
assert_eq!(hex::encode(tmp), HEX_EMPTY_ROOTS[i]); assert_eq!(hex::encode(tmp), HEX_EMPTY_ROOTS[i]);
} }
@@ -980,9 +925,7 @@ mod tests {
fn assert_root_eq(root: Node, expected: &str) { fn assert_root_eq(root: Node, expected: &str) {
let mut tmp = [0u8; 32]; let mut tmp = [0u8; 32];
root.repr root.write(&mut tmp[..]).expect("length is 32 bytes");
.write_le(&mut tmp[..])
.expect("length is 32 bytes");
assert_eq!(hex::encode(tmp), expected); assert_eq!(hex::encode(tmp), expected);
} }

View File

@@ -1,14 +1,19 @@
use ff::{BitIterator, PrimeField}; use ff::{BitIterator, PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr, FrRepr}; use pairing::bls12_381::{Bls12, Fr, FrRepr};
use rand::OsRng; use rand::OsRng;
use sapling_crypto::{ use sapling_crypto::{
jubjub::{fs::Fs, FixedGenerators, JubjubBls12}, jubjub::{fs::Fs, FixedGenerators, JubjubBls12},
pedersen_hash::{pedersen_hash, Personalization}, pedersen_hash::{pedersen_hash, Personalization},
primitives::Note,
redjubjub::{PrivateKey, PublicKey, Signature}, redjubjub::{PrivateKey, PublicKey, Signature},
}; };
use std::io::{self, Read, Write};
use crate::merkle_tree::Hashable;
use JUBJUB; use JUBJUB;
pub(crate) const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32;
/// Compute a parent node in the Sapling commitment tree given its two children. /// Compute a parent node in the Sapling commitment tree given its two children.
pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr {
let lhs = { let lhs = {
@@ -40,6 +45,63 @@ pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr {
.into_repr() .into_repr()
} }
/// A node within a Merkle tree.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Node {
repr: FrRepr,
}
impl Node {
pub fn new(repr: FrRepr) -> Self {
Node { repr }
}
}
impl Hashable for Node {
fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut repr = FrRepr::default();
repr.read_le(&mut reader)?;
Ok(Node::new(repr))
}
fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
self.repr.write_le(&mut writer)
}
fn combine(depth: usize, lhs: &Self, rhs: &Self) -> Self {
Node {
repr: merkle_hash(depth, &lhs.repr, &rhs.repr),
}
}
fn blank() -> Self {
Node {
repr: Note::<Bls12>::uncommitted().into_repr(),
}
}
fn empty_root(depth: usize) -> Self {
EMPTY_ROOTS[depth]
}
}
impl From<Node> for Fr {
fn from(node: Node) -> Self {
Fr::from_repr(node.repr).expect("Tree nodes should be in the prime field")
}
}
lazy_static! {
static ref EMPTY_ROOTS: Vec<Node> = {
let mut v = vec![Node::blank()];
for d in 0..SAPLING_COMMITMENT_TREE_DEPTH {
let next = Node::combine(d, &v[d], &v[d]);
v.push(next);
}
v
};
}
/// Create the spendAuthSig for a Sapling SpendDescription. /// Create the spendAuthSig for a Sapling SpendDescription.
pub fn spend_sig( pub fn spend_sig(
ask: PrivateKey<Bls12>, ask: PrivateKey<Bls12>,

View File

@@ -13,7 +13,7 @@ use sapling_crypto::{
primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ValueCommitment}, primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ValueCommitment},
redjubjub::{PrivateKey, PublicKey, Signature}, redjubjub::{PrivateKey, PublicKey, Signature},
}; };
use zcash_primitives::merkle_tree::CommitmentTreeWitness; use zcash_primitives::{merkle_tree::CommitmentTreeWitness, sapling::Node};
use super::compute_value_balance; use super::compute_value_balance;
@@ -43,7 +43,7 @@ impl SaplingProvingContext {
ar: Fs, ar: Fs,
value: u64, value: u64,
anchor: Fr, anchor: Fr,
witness: CommitmentTreeWitness, witness: CommitmentTreeWitness<Node>,
proving_key: &Parameters<Bls12>, proving_key: &Parameters<Bls12>,
verifying_key: &PreparedVerifyingKey<Bls12>, verifying_key: &PreparedVerifyingKey<Bls12>,
params: &JubjubBls12, params: &JubjubBls12,
@@ -112,7 +112,11 @@ impl SaplingProvingContext {
payment_address: Some(payment_address), payment_address: Some(payment_address),
commitment_randomness: Some(rcm), commitment_randomness: Some(rcm),
ar: Some(ar), ar: Some(ar),
auth_path: witness.auth_path, auth_path: witness
.auth_path
.iter()
.map(|n| n.map(|(node, b)| (node.into(), b)))
.collect(),
anchor: Some(anchor), anchor: Some(anchor),
}; };