2021-03-07 02:15:38 -08:00
|
|
|
|
//! Note Commitment Trees.
|
|
|
|
|
//!
|
|
|
|
|
//! A note commitment tree is an incremental Merkle tree of fixed depth
|
|
|
|
|
//! used to store note commitments that JoinSplit transfers or Spend
|
|
|
|
|
//! transfers produce. Just as the unspent transaction output set (UTXO
|
|
|
|
|
//! set) used in Bitcoin, it is used to express the existence of value and
|
|
|
|
|
//! the capability to spend it. However, unlike the UTXO set, it is not
|
|
|
|
|
//! the job of this tree to protect against double-spending, as it is
|
|
|
|
|
//! append-only.
|
|
|
|
|
//!
|
|
|
|
|
//! A root of a note commitment tree is associated with each treestate.
|
|
|
|
|
|
|
|
|
|
#![allow(clippy::unit_arg)]
|
2021-03-15 00:16:49 -07:00
|
|
|
|
#![allow(dead_code)]
|
2021-03-07 02:15:38 -08:00
|
|
|
|
|
|
|
|
|
use std::{collections::VecDeque, fmt};
|
|
|
|
|
|
|
|
|
|
use bitvec::prelude::*;
|
2021-03-15 00:16:49 -07:00
|
|
|
|
use halo2::arithmetic::FieldExt;
|
2021-03-07 02:15:38 -08:00
|
|
|
|
use lazy_static::lazy_static;
|
|
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
|
|
|
use proptest_derive::Arbitrary;
|
|
|
|
|
|
2021-03-10 13:39:31 -08:00
|
|
|
|
use super::{commitment::NoteCommitment, sinsemilla::*};
|
2021-03-07 02:15:38 -08:00
|
|
|
|
|
|
|
|
|
const MERKLE_DEPTH: usize = 32;
|
|
|
|
|
|
2021-03-10 13:39:31 -08:00
|
|
|
|
/// MerkleCRH^Orchard Hash Function
|
2021-03-07 02:15:38 -08:00
|
|
|
|
///
|
2021-03-10 13:39:31 -08:00
|
|
|
|
/// Used to hash incremental Merkle tree hash values for Orchard.
|
2021-03-07 02:15:38 -08:00
|
|
|
|
///
|
2021-03-16 16:28:15 -07:00
|
|
|
|
/// MerkleCRH^Orchard(layer, left, right) := SinsemillaHash("z.cash:Orchard-MerkleCRH", l || left || right),
|
2021-03-07 02:15:38 -08:00
|
|
|
|
///
|
2021-03-16 16:28:15 -07:00
|
|
|
|
/// where l = I2LEBSP_10(MerkleDepth^Orchard − 1 − layer) and left, right, and
|
2021-03-10 13:39:31 -08:00
|
|
|
|
/// the output are all technically 255 bits (l_MerkleOrchard), not 256.
|
|
|
|
|
///
|
|
|
|
|
/// https://zips.z.cash/protocol/nu5.pdf#merklecrh
|
2021-03-16 16:28:15 -07:00
|
|
|
|
/// https://zips.z.cash/protocol/nu5.pdf#constants
|
2021-03-10 13:39:31 -08:00
|
|
|
|
fn merkle_crh_orchard(layer: u8, left: [u8; 32], right: [u8; 32]) -> [u8; 32] {
|
2021-03-07 02:15:38 -08:00
|
|
|
|
let mut s = bitvec![Lsb0, u8;];
|
|
|
|
|
|
2021-03-16 16:28:15 -07:00
|
|
|
|
// Prefix: l = I2LEBSP_10(MerkleDepth^Orchard − 1 − layer)
|
|
|
|
|
s.extend_from_slice(&layer.bits::<Lsb0>()[0..10]);
|
2021-03-07 02:15:38 -08:00
|
|
|
|
s.extend_from_slice(&left.bits::<Lsb0>()[0..255]);
|
|
|
|
|
s.extend_from_slice(&right.bits::<Lsb0>()[0..255]);
|
|
|
|
|
|
2021-03-15 00:16:49 -07:00
|
|
|
|
sinsemilla_hash(b"z.cash:Orchard-MerkleCRH", &s).to_bytes()
|
2021-03-07 02:15:38 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lazy_static! {
|
2021-03-10 13:39:31 -08:00
|
|
|
|
/// Orchard note commitment trees have a max depth of 32.
|
2021-03-07 02:15:38 -08:00
|
|
|
|
///
|
2021-04-27 16:25:38 -07:00
|
|
|
|
/// https://zips.z.cash/protocol/nu5.pdf#constants
|
2021-03-07 02:15:38 -08:00
|
|
|
|
static ref EMPTY_ROOTS: Vec<[u8; 32]> = {
|
2021-03-10 13:39:31 -08:00
|
|
|
|
// Uncommitted^Orchard = I2LEBSP_l_MerkleOrchard(1)
|
2021-03-07 02:15:38 -08:00
|
|
|
|
let mut v = vec![jubjub::Fq::one().to_bytes()];
|
|
|
|
|
|
|
|
|
|
for d in 0..MERKLE_DEPTH {
|
2021-03-10 13:39:31 -08:00
|
|
|
|
let next = merkle_crh_orchard(d as u8, v[d], v[d]);
|
2021-03-07 02:15:38 -08:00
|
|
|
|
v.push(next);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 13:39:31 -08:00
|
|
|
|
/// The index of a note’s commitment at the leafmost layer of its
|
|
|
|
|
/// `NoteCommitmentTree`.
|
2021-03-07 02:15:38 -08:00
|
|
|
|
///
|
2021-04-27 16:25:38 -07:00
|
|
|
|
/// https://zips.z.cash/protocol/nu5.pdf#merkletree
|
2021-03-10 13:39:31 -08:00
|
|
|
|
// XXX: dedupe with sapling?
|
2021-03-07 02:15:38 -08:00
|
|
|
|
pub struct Position(pub(crate) u64);
|
|
|
|
|
|
2021-03-10 13:39:31 -08:00
|
|
|
|
/// Orchard note commitment tree root node hash.
|
2021-03-07 02:15:38 -08:00
|
|
|
|
///
|
2021-03-10 13:39:31 -08:00
|
|
|
|
/// The root hash in LEBS2OSP256(rt) encoding of the Orchard note commitment
|
|
|
|
|
/// tree corresponding to the final Orchard treestate of this block. A root of a
|
|
|
|
|
/// note commitment tree is associated with each treestate.
|
2021-03-07 02:15:38 -08:00
|
|
|
|
#[derive(Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
|
|
|
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
|
2021-03-10 13:39:31 -08:00
|
|
|
|
// XXX: dedupe with sapling?
|
2021-03-07 02:15:38 -08:00
|
|
|
|
pub struct Root(pub [u8; 32]);
|
|
|
|
|
|
|
|
|
|
impl fmt::Debug for Root {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
f.debug_tuple("Root").field(&hex::encode(&self.0)).finish()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 13:39:31 -08:00
|
|
|
|
/// Orchard Note Commitment Tree
|
2021-03-07 02:15:38 -08:00
|
|
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
|
|
|
struct NoteCommitmentTree {
|
|
|
|
|
/// The root node of the tree (often used as an anchor).
|
|
|
|
|
root: Root,
|
2021-03-10 13:39:31 -08:00
|
|
|
|
/// The height of the tree (maximum height for Orchard is 32).
|
2021-03-07 02:15:38 -08:00
|
|
|
|
height: u8,
|
|
|
|
|
/// The number of leaves (note commitments) in this tree.
|
|
|
|
|
count: u32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Vec<NoteCommitment>> for NoteCommitmentTree {
|
|
|
|
|
fn from(_values: Vec<NoteCommitment>) -> Self {
|
|
|
|
|
unimplemented!();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Vec<jubjub::Fq>> for NoteCommitmentTree {
|
|
|
|
|
fn from(values: Vec<jubjub::Fq>) -> Self {
|
|
|
|
|
if values.is_empty() {
|
|
|
|
|
return NoteCommitmentTree {
|
|
|
|
|
root: Root::default(),
|
|
|
|
|
height: 0,
|
|
|
|
|
count: 0,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let count = values.len() as u32;
|
|
|
|
|
let mut height = 0u8;
|
|
|
|
|
let mut current_layer: VecDeque<[u8; 32]> =
|
|
|
|
|
values.into_iter().map(|cm_u| cm_u.to_bytes()).collect();
|
|
|
|
|
|
|
|
|
|
while usize::from(height) < MERKLE_DEPTH {
|
|
|
|
|
let mut next_layer_up = vec![];
|
|
|
|
|
|
|
|
|
|
while !current_layer.is_empty() {
|
|
|
|
|
let left = current_layer.pop_front().unwrap();
|
|
|
|
|
let right;
|
|
|
|
|
if current_layer.is_empty() {
|
|
|
|
|
right = EMPTY_ROOTS[height as usize];
|
|
|
|
|
} else {
|
|
|
|
|
right = current_layer.pop_front().unwrap();
|
|
|
|
|
}
|
2021-03-10 13:39:31 -08:00
|
|
|
|
next_layer_up.push(merkle_crh_orchard(height, left, right));
|
2021-03-07 02:15:38 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
height += 1;
|
|
|
|
|
current_layer = next_layer_up.into();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert!(current_layer.len() == 1);
|
|
|
|
|
|
|
|
|
|
NoteCommitmentTree {
|
|
|
|
|
root: Root(current_layer.pop_front().unwrap()),
|
|
|
|
|
height,
|
|
|
|
|
count,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl NoteCommitmentTree {
|
2021-03-15 00:16:49 -07:00
|
|
|
|
/// Get the Pallas-based Pedersen hash of root node of this merkle tree of
|
2021-03-07 02:15:38 -08:00
|
|
|
|
/// commitment notes.
|
|
|
|
|
pub fn hash(&self) -> [u8; 32] {
|
|
|
|
|
self.root.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-20 22:09:58 -07:00
|
|
|
|
// TODO: check empty roots, incremental roots, as part of https://github.com/ZcashFoundation/zebra/issues/1287
|