diff --git a/src/crypto/coin.rs b/src/crypto/coin.rs new file mode 100644 index 000000000..cd86af7cb --- /dev/null +++ b/src/crypto/coin.rs @@ -0,0 +1,98 @@ +use std::io; +use group::Curve; +use bitvec::{order::Lsb0, view::AsBits}; +use lazy_static::lazy_static; +use ff::PrimeField; + +use super::merkle::Hashable; + +pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 4; + +/// Compute a parent node in the Sapling commitment tree given its two children. +pub fn merkle_hash(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> bls12_381::Scalar { + // This thing is nasty lol + let lhs = { + let mut tmp = [false; 256]; + for (a, b) in tmp.iter_mut().zip(lhs.as_bits::()) { + *a = *b; + } + tmp + }; + + let rhs = { + let mut tmp = [false; 256]; + for (a, b) in tmp.iter_mut().zip(rhs.as_bits::()) { + *a = *b; + } + tmp + }; + + jubjub::ExtendedPoint::from(zcash_primitives::pedersen_hash::pedersen_hash( + zcash_primitives::pedersen_hash::Personalization::MerkleTree(depth), + lhs.iter() + .copied() + .take(bls12_381::Scalar::NUM_BITS as usize) + .chain( + rhs.iter() + .copied() + .take(bls12_381::Scalar::NUM_BITS as usize), + ), + )) + .to_affine() + .get_u() +} + +/// A node within the Sapling commitment tree. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Node { + repr: [u8; 32], +} + +impl Node { + pub fn new(repr: [u8; 32]) -> Self { + Node { repr } + } +} + +impl Hashable for Node { + fn read(mut reader: R) -> io::Result { + let mut repr = [0u8; 32]; + reader.read_exact(&mut repr)?; + Ok(Node::new(repr)) + } + + fn write(&self, mut writer: W) -> io::Result<()> { + writer.write_all(self.repr.as_ref()) + } + + fn combine(depth: usize, lhs: &Self, rhs: &Self) -> Self { + Node { + repr: merkle_hash(depth, &lhs.repr, &rhs.repr).to_repr(), + } + } + + fn blank() -> Self { + // The smallest u-coordinate that is not on the curve + // is one. + let uncommitted_note = bls12_381::Scalar::one(); + Node { + repr: uncommitted_note.to_repr(), + } + } + + fn empty_root(depth: usize) -> Self { + EMPTY_ROOTS[depth] + } +} + +lazy_static! { + static ref EMPTY_ROOTS: Vec = { + 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 + }; +} + diff --git a/src/crypto/merkle.rs b/src/crypto/merkle.rs new file mode 100644 index 000000000..c48ef52bd --- /dev/null +++ b/src/crypto/merkle.rs @@ -0,0 +1,501 @@ +//! Implementation of a Merkle tree of commitments used to prove the existence of notes. + +//use byteorder::{LittleEndian, ReadBytesExt}; +use std::collections::VecDeque; +use std::io::{self, Read, Write}; + +//use crate::serialize::{Optional, Vector}; +use super::coin::SAPLING_COMMITMENT_TREE_DEPTH; + +/// A hashable node within a Merkle tree. +pub trait Hashable: Clone + Copy { + /// Parses a node from the given byte source. + fn read(reader: R) -> io::Result; + + /// Serializes this node. + fn write(&self, writer: W) -> io::Result<()>; + + /// Returns the parent node within the tree of the two given nodes. + fn combine(_: usize, _: &Self, _: &Self) -> Self; + + /// Returns a blank leaf node. + fn blank() -> Self; + + /// Returns the empty root for the given depth. + fn empty_root(_: usize) -> Self; +} + +struct PathFiller { + queue: VecDeque, +} + +impl PathFiller { + fn empty() -> Self { + PathFiller { + queue: VecDeque::new(), + } + } + + fn next(&mut self, depth: usize) -> Node { + self.queue + .pop_front() + .unwrap_or_else(|| Node::empty_root(depth)) + } +} + +/// A Merkle tree of note commitments. +/// +/// The depth of the Merkle tree is fixed at 32, equal to the depth of the Sapling +/// commitment tree. +#[derive(Clone)] +pub struct CommitmentTree { + left: Option, + right: Option, + parents: Vec>, +} + +impl CommitmentTree { + /// Creates an empty tree. + pub fn empty() -> Self { + CommitmentTree { + left: None, + right: None, + parents: vec![], + } + } + + /* + /// Reads a `CommitmentTree` from its serialized form. + #[allow(clippy::redundant_closure)] + pub fn read(mut reader: R) -> io::Result { + let left = Optional::read(&mut reader, |r| Node::read(r))?; + let right = Optional::read(&mut reader, |r| Node::read(r))?; + let parents = Vector::read(&mut reader, |r| Optional::read(r, |r| Node::read(r)))?; + + Ok(CommitmentTree { + left, + right, + parents, + }) + } + + /// Serializes this tree as an array of bytes. + pub fn write(&self, mut writer: W) -> io::Result<()> { + Optional::write(&mut writer, &self.left, |w, n| n.write(w))?; + Optional::write(&mut writer, &self.right, |w, n| n.write(w))?; + Vector::write(&mut writer, &self.parents, |w, e| { + Optional::write(w, e, |w, n| n.write(w)) + }) + } + */ + + /// Returns the number of leaf nodes in the tree. + pub fn size(&self) -> usize { + self.parents.iter().enumerate().fold( + match (self.left, self.right) { + (None, None) => 0, + (Some(_), None) => 1, + (Some(_), Some(_)) => 2, + (None, Some(_)) => unreachable!(), + }, + |acc, (i, p)| { + // Treat occupation of parents array as a binary number + // (right-shifted by 1) + acc + if p.is_some() { 1 << (i + 1) } else { 0 } + }, + ) + } + + fn is_complete(&self, depth: usize) -> bool { + self.left.is_some() + && self.right.is_some() + && self.parents.len() == depth - 1 + && self.parents.iter().all(|p| p.is_some()) + } + + /// Adds a leaf node to the tree. + /// + /// Returns an error if the tree is full. + pub fn append(&mut self, node: Node) -> Result<(), ()> { + self.append_inner(node, SAPLING_COMMITMENT_TREE_DEPTH) + } + + fn append_inner(&mut self, node: Node, depth: usize) -> Result<(), ()> { + if self.is_complete(depth) { + // Tree is full + return Err(()); + } + + match (self.left, self.right) { + (None, _) => self.left = Some(node), + (_, None) => self.right = Some(node), + (Some(l), Some(r)) => { + let mut combined = Node::combine(0, &l, &r); + self.left = Some(node); + self.right = None; + + for i in 0..depth { + if i < self.parents.len() { + if let Some(p) = self.parents[i] { + combined = Node::combine(i + 1, &p, &combined); + self.parents[i] = None; + } else { + self.parents[i] = Some(combined); + break; + } + } else { + self.parents.push(Some(combined)); + break; + } + } + } + } + + Ok(()) + } + + /// Returns the current root of the tree. + pub fn root(&self) -> Node { + self.root_inner(SAPLING_COMMITMENT_TREE_DEPTH, PathFiller::empty()) + } + + fn root_inner(&self, depth: usize, mut filler: PathFiller) -> Node { + assert!(depth > 0); + + // 1) Hash left and right leaves together. + // - Empty leaves are used as needed. + let leaf_root = Node::combine( + 0, + &self.left.unwrap_or_else(|| filler.next(0)), + &self.right.unwrap_or_else(|| filler.next(0)), + ); + + // 2) Hash in parents up to the currently-filled depth. + // - Roots of the empty subtrees are used as needed. + let mid_root = self + .parents + .iter() + .enumerate() + .fold(leaf_root, |root, (i, p)| match p { + Some(node) => Node::combine(i + 1, node, &root), + 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))) + } +} + +/// An updatable witness to a path from a position in a particular [`CommitmentTree`]. +/// +/// Appending the same commitments in the same order to both the original +/// [`CommitmentTree`] and this `IncrementalWitness` will result in a witness to the path +/// from the target position to the root of the updated tree. +/// +/// # Examples +/// +/// ``` +/// use ff::{Field, PrimeField}; +/// use rand_core::OsRng; +/// use zcash_primitives::{ +/// merkle_tree::{CommitmentTree, IncrementalWitness}, +/// sapling::Node, +/// }; +/// +/// let mut rng = OsRng; +/// +/// let mut tree = CommitmentTree::::empty(); +/// +/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr())); +/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr())); +/// let mut witness = IncrementalWitness::from_tree(&tree); +/// assert_eq!(witness.position(), 1); +/// assert_eq!(tree.root(), witness.root()); +/// +/// let cmu = Node::new(bls12_381::Scalar::random(&mut rng).to_repr()); +/// tree.append(cmu); +/// witness.append(cmu); +/// assert_eq!(tree.root(), witness.root()); +/// ``` +#[derive(Clone)] +pub struct IncrementalWitness { + tree: CommitmentTree, + filled: Vec, + cursor_depth: usize, + cursor: Option>, +} + +impl IncrementalWitness { + /// Creates an `IncrementalWitness` for the most recent commitment added to the given + /// [`CommitmentTree`]. + pub fn from_tree(tree: &CommitmentTree) -> IncrementalWitness { + IncrementalWitness { + tree: tree.clone(), + filled: vec![], + cursor_depth: 0, + cursor: None, + } + } + + /* + /// Reads an `IncrementalWitness` from its serialized form. + #[allow(clippy::redundant_closure)] + pub fn read(mut reader: R) -> io::Result { + let tree = CommitmentTree::read(&mut reader)?; + let filled = Vector::read(&mut reader, |r| Node::read(r))?; + let cursor = Optional::read(&mut reader, |r| CommitmentTree::read(r))?; + + let mut witness = IncrementalWitness { + tree, + filled, + cursor_depth: 0, + cursor, + }; + + witness.cursor_depth = witness.next_depth(); + + Ok(witness) + } + + /// Serializes this `IncrementalWitness` as an array of bytes. + pub fn write(&self, mut writer: W) -> io::Result<()> { + self.tree.write(&mut writer)?; + Vector::write(&mut writer, &self.filled, |w, n| n.write(w))?; + Optional::write(&mut writer, &self.cursor, |w, t| t.write(w)) + } + */ + + /// Returns the position of the witnessed leaf node in the commitment tree. + pub fn position(&self) -> usize { + self.tree.size() - 1 + } + + fn filler(&self) -> PathFiller { + let cursor_root = self + .cursor + .as_ref() + .map(|c| c.root_inner(self.cursor_depth, PathFiller::empty())); + + PathFiller { + queue: self.filled.iter().cloned().chain(cursor_root).collect(), + } + } + + /// Finds the next "depth" of an unfilled subtree. + fn next_depth(&self) -> usize { + let mut skip = self.filled.len(); + + if self.tree.left.is_none() { + if skip > 0 { + skip -= 1; + } else { + return 0; + } + } + + if self.tree.right.is_none() { + if skip > 0 { + skip -= 1; + } else { + return 0; + } + } + + let mut d = 1; + for p in &self.tree.parents { + if p.is_none() { + if skip > 0 { + skip -= 1; + } else { + return d; + } + } + d += 1; + } + + d + skip + } + + /// Tracks a leaf node that has been added to the underlying tree. + /// + /// Returns an error if the tree is full. + pub fn append(&mut self, node: Node) -> Result<(), ()> { + self.append_inner(node, SAPLING_COMMITMENT_TREE_DEPTH) + } + + fn append_inner(&mut self, node: Node, depth: usize) -> Result<(), ()> { + if let Some(mut cursor) = self.cursor.take() { + cursor + .append_inner(node, depth) + .expect("cursor should not be full"); + if cursor.is_complete(self.cursor_depth) { + self.filled + .push(cursor.root_inner(self.cursor_depth, PathFiller::empty())); + } else { + self.cursor = Some(cursor); + } + } else { + self.cursor_depth = self.next_depth(); + if self.cursor_depth >= depth { + // Tree is full + return Err(()); + } + + if self.cursor_depth == 0 { + self.filled.push(node); + } else { + let mut cursor = CommitmentTree::empty(); + cursor + .append_inner(node, depth) + .expect("cursor should not be full"); + self.cursor = Some(cursor); + } + } + + Ok(()) + } + + /// Returns the current root of the tree corresponding to the witness. + pub fn root(&self) -> Node { + self.root_inner(SAPLING_COMMITMENT_TREE_DEPTH) + } + + fn root_inner(&self, depth: usize) -> Node { + self.tree.root_inner(depth, self.filler()) + } + + /// Returns the current witness, or None if the tree is empty. + pub fn path(&self) -> Option> { + self.path_inner(SAPLING_COMMITMENT_TREE_DEPTH) + } + + fn path_inner(&self, depth: usize) -> Option> { + let mut filler = self.filler(); + let mut auth_path = Vec::new(); + + if let Some(node) = self.tree.left { + if self.tree.right.is_some() { + auth_path.push((node, true)); + } else { + auth_path.push((filler.next(0), false)); + } + } else { + // Can't create an authentication path for the beginning of the tree + return None; + } + + for (i, p) in self.tree.parents.iter().enumerate() { + auth_path.push(match p { + Some(node) => (*node, true), + 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); + + Some(MerklePath::from_path(auth_path, self.position() as u64)) + } +} + +/// A path from a position in a particular commitment tree to the root of that tree. +#[derive(Clone, Debug, PartialEq)] +pub struct MerklePath { + pub auth_path: Vec<(Node, bool)>, + pub position: u64, +} + +impl MerklePath { + /// Constructs a Merkle path directly from a path and position. + pub fn from_path(auth_path: Vec<(Node, bool)>, position: u64) -> Self { + MerklePath { + auth_path, + position, + } + } + + /* + /// Reads a Merkle path from its serialized form. + pub fn from_slice(witness: &[u8]) -> Result { + Self::from_slice_with_depth(witness, SAPLING_COMMITMENT_TREE_DEPTH) + } + + fn from_slice_with_depth(mut witness: &[u8], depth: usize) -> Result { + // Skip the first byte, which should be "depth" to signify the length of + // the following vector of Pedersen hashes. + if witness[0] != depth as u8 { + return Err(()); + } + witness = &witness[1..]; + + // Begin to construct the authentication path + let iter = witness.chunks_exact(33); + witness = iter.remainder(); + + // The vector works in reverse + let mut auth_path = iter + .rev() + .map(|bytes| { + // Length of inner vector should be the length of a Pedersen hash + if bytes[0] == 32 { + // Sibling node should be an element of Fr + Node::read(&bytes[1..]) + .map(|sibling| { + // Set the value in the auth path; we put false here + // for now (signifying the position bit) which we'll + // fill in later. + (sibling, false) + }) + .map_err(|_| ()) + } else { + Err(()) + } + }) + .collect::, _>>()?; + if auth_path.len() != depth { + return Err(()); + } + + // Read the position from the witness + let position = witness.read_u64::().map_err(|_| ())?; + + // Given the position, let's finish constructing the authentication + // path + let mut tmp = position; + for entry in auth_path.iter_mut() { + entry.1 = (tmp & 1) == 1; + tmp >>= 1; + } + + // The witness should be empty now; if it wasn't, the caller would + // have provided more information than they should have, indicating + // a bug downstream + if witness.is_empty() { + Ok(MerklePath { + auth_path, + position, + }) + } else { + Err(()) + } + } + */ + + /// Returns the root of the tree corresponding to this path applied to `leaf`. + pub fn root(&self, leaf: Node) -> Node { + self.auth_path + .iter() + .enumerate() + .fold( + leaf, + |root, (i, (p, leaf_is_on_right))| match leaf_is_on_right { + false => Node::combine(i, &root, p), + true => Node::combine(i, p, &root), + }, + ) + } +} + diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index fd9ddec82..908a9f5b8 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,5 +1,7 @@ +pub mod coin; pub mod diffie_hellman; pub mod fr_serial; +pub mod merkle; pub mod mint_proof; pub mod note; pub mod schnorr; diff --git a/src/crypto/spend_proof.rs b/src/crypto/spend_proof.rs index 78dcfe078..0de5ceae8 100644 --- a/src/crypto/spend_proof.rs +++ b/src/crypto/spend_proof.rs @@ -9,45 +9,9 @@ use rand::rngs::OsRng; use std::time::Instant; use crate::circuit::spend_contract::SpendContract; +use super::coin::merkle_hash; use crate::error::Result; -// This thing is nasty lol -pub fn merkle_hash( - depth: usize, - lhs: &bls12_381::Scalar, - rhs: &bls12_381::Scalar, -) -> bls12_381::Scalar { - let lhs = { - let mut tmp = [false; 256]; - for (a, b) in tmp.iter_mut().zip(lhs.to_repr().as_bits::()) { - *a = *b; - } - tmp - }; - - let rhs = { - let mut tmp = [false; 256]; - for (a, b) in tmp.iter_mut().zip(rhs.to_repr().as_bits::()) { - *a = *b; - } - tmp - }; - - jubjub::ExtendedPoint::from(zcash_primitives::pedersen_hash::pedersen_hash( - zcash_primitives::pedersen_hash::Personalization::MerkleTree(depth), - lhs.iter() - .copied() - .take(bls12_381::Scalar::NUM_BITS as usize) - .chain( - rhs.iter() - .copied() - .take(bls12_381::Scalar::NUM_BITS as usize), - ), - )) - .to_affine() - .get_u() -} - pub struct SpendRevealedValues { pub value_commit: jubjub::SubgroupPoint, pub nullifier: [u8; 32], @@ -111,9 +75,9 @@ impl SpendRevealedValues { for (i, (right, is_right)) in merkle_path.iter().enumerate() { if *is_right { - merkle_root = merkle_hash(i, &right, &merkle_root); + merkle_root = merkle_hash(i, &right.to_repr(), &merkle_root.to_repr()); } else { - merkle_root = merkle_hash(i, &merkle_root, &right); + merkle_root = merkle_hash(i, &merkle_root.to_repr(), &right.to_repr()); } }