2019-09-25 23:23:04 -07: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.
|
2020-07-28 00:52:04 -07:00
|
|
|
|
|
2020-05-26 18:00:58 -07:00
|
|
|
|
#![allow(clippy::unit_arg)]
|
2020-07-28 00:52:04 -07:00
|
|
|
|
#![allow(dead_code)]
|
2019-09-25 23:23:04 -07:00
|
|
|
|
|
2020-09-15 12:20:17 -07:00
|
|
|
|
use std::fmt;
|
2020-02-07 12:53:44 -08:00
|
|
|
|
|
2020-07-28 00:52:04 -07:00
|
|
|
|
use bitvec::prelude::*;
|
2020-09-18 19:47:22 -07:00
|
|
|
|
use lazy_static::lazy_static;
|
2020-09-23 18:52:52 -07:00
|
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
2020-01-27 14:49:25 -08:00
|
|
|
|
use proptest_derive::Arbitrary;
|
|
|
|
|
|
2020-09-15 12:20:17 -07:00
|
|
|
|
use super::commitment::{pedersen_hashes::pedersen_hash, NoteCommitment};
|
2020-07-28 00:52:04 -07:00
|
|
|
|
|
|
|
|
|
/// MerkleCRH^Sapling Hash Function
|
|
|
|
|
///
|
2020-09-09 00:15:12 -07:00
|
|
|
|
/// MerkleCRH^Sapling(layer, left, right) := PedersenHash(“Zcash_PH”, l || left || right)
|
2020-09-18 19:47:22 -07:00
|
|
|
|
/// where l = I2LEBSP_6(MerkleDepth^Sapling − 1 − layer) and
|
|
|
|
|
/// left, right, and the output are all technically 255 bits (l_MerkleSapling), not 256.
|
2020-07-28 00:52:04 -07:00
|
|
|
|
///
|
|
|
|
|
/// https://zips.z.cash/protocol/protocol.pdf#merklecrh
|
2020-09-09 00:15:12 -07:00
|
|
|
|
fn merkle_crh_sapling(layer: u8, left: [u8; 32], right: [u8; 32]) -> [u8; 32] {
|
2020-09-18 19:47:22 -07:00
|
|
|
|
let mut s = bitvec![Lsb0, u8;];
|
2020-07-28 00:52:04 -07:00
|
|
|
|
|
|
|
|
|
// Prefix: l = I2LEBSP_6(MerkleDepth^Sapling − 1 − layer)
|
2020-09-18 19:47:22 -07:00
|
|
|
|
s.extend_from_slice(&layer.bits::<Lsb0>()[0..6]);
|
|
|
|
|
s.extend_from_slice(&left.bits::<Lsb0>()[0..255]);
|
|
|
|
|
s.extend_from_slice(&right.bits::<Lsb0>()[0..255]);
|
2020-07-28 00:52:04 -07:00
|
|
|
|
|
2020-09-09 00:15:12 -07:00
|
|
|
|
pedersen_hash(*b"Zcash_PH", &s).to_bytes()
|
2020-07-28 00:52:04 -07:00
|
|
|
|
}
|
2019-09-25 23:23:04 -07:00
|
|
|
|
|
2020-09-18 19:47:22 -07:00
|
|
|
|
lazy_static! {
|
|
|
|
|
/// Sapling note commitment trees have a max depth of 32.
|
|
|
|
|
///
|
|
|
|
|
/// https://zips.z.cash/protocol/canopy.pdf#constants
|
|
|
|
|
static ref EMPTY_ROOTS: Vec<[u8; 32]> = {
|
|
|
|
|
// Uncommitted^Sapling = I2LEBSP_l_MerkleSapling(1)
|
|
|
|
|
let mut v = vec![jubjub::Fq::one().to_bytes()];
|
|
|
|
|
|
|
|
|
|
for d in 0..32 {
|
|
|
|
|
let next = merkle_crh_sapling(d as u8, v[d], v[d]);
|
|
|
|
|
v.push(next);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-27 23:27:02 -07:00
|
|
|
|
/// The index of a note’s commitment at the leafmost layer of its Note
|
|
|
|
|
/// Commitment Tree.
|
|
|
|
|
///
|
|
|
|
|
/// https://zips.z.cash/protocol/protocol.pdf#merkletree
|
|
|
|
|
pub struct Position(pub(crate) u64);
|
|
|
|
|
|
2019-09-25 23:23:04 -07:00
|
|
|
|
/// Sapling note commitment tree root node hash.
|
|
|
|
|
///
|
|
|
|
|
/// The root hash in LEBS2OSP256(rt) encoding of the Sapling note
|
|
|
|
|
/// commitment tree corresponding to the final Sapling treestate of
|
|
|
|
|
/// this block. A root of a note commitment tree is associated with
|
|
|
|
|
/// each treestate.
|
2020-09-24 15:46:04 -07:00
|
|
|
|
#[derive(Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
2020-09-23 18:52:52 -07:00
|
|
|
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
|
2020-08-28 01:22:40 -07:00
|
|
|
|
pub struct Root(pub [u8; 32]);
|
2019-09-25 23:23:04 -07:00
|
|
|
|
|
2020-08-28 01:22:40 -07:00
|
|
|
|
impl fmt::Debug for Root {
|
2020-02-07 12:53:44 -08:00
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2020-08-28 01:22:40 -07:00
|
|
|
|
f.debug_tuple("Root").field(&hex::encode(&self.0)).finish()
|
2020-02-07 12:53:44 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-15 12:20:17 -07:00
|
|
|
|
/// Sapling Note Commitment Tree
|
|
|
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
|
|
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
|
|
|
|
|
struct NoteCommitmentTree {
|
|
|
|
|
/// The root node of the tree (often used as an anchor).
|
|
|
|
|
root: Root,
|
|
|
|
|
/// The height of the tree (maximum height for Sapling is 32).
|
|
|
|
|
height: u8,
|
|
|
|
|
/// The number of leaves (note commitments) in this tree.
|
|
|
|
|
count: u32,
|
|
|
|
|
}
|
2019-09-25 23:23:04 -07:00
|
|
|
|
|
2020-09-15 12:20:17 -07:00
|
|
|
|
impl From<Vec<NoteCommitment>> for NoteCommitmentTree {
|
|
|
|
|
fn from(_values: Vec<NoteCommitment>) -> Self {
|
2019-09-25 23:23:04 -07:00
|
|
|
|
unimplemented!();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-15 12:20:17 -07:00
|
|
|
|
impl From<Vec<jubjub::Fq>> for NoteCommitmentTree {
|
|
|
|
|
// XXX broken
|
|
|
|
|
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 = 0;
|
|
|
|
|
let mut current_layer: Vec<[u8; 32]> =
|
|
|
|
|
values.into_iter().map(|cm_u| cm_u.to_bytes()).collect();
|
|
|
|
|
|
|
|
|
|
while current_layer.len() > 1 {
|
|
|
|
|
let mut next_layer_up = vec![];
|
|
|
|
|
|
|
|
|
|
while !current_layer.is_empty() {
|
|
|
|
|
let left = current_layer.remove(0);
|
|
|
|
|
let right;
|
|
|
|
|
if current_layer.is_empty() {
|
|
|
|
|
right = jubjub::Fq::one().to_bytes();
|
|
|
|
|
//next_layer_up.push(current_layer.remove(0));
|
|
|
|
|
} else {
|
|
|
|
|
//let left = current_layer.remove(0);
|
|
|
|
|
right = current_layer.remove(0);
|
|
|
|
|
}
|
|
|
|
|
next_layer_up.push(merkle_crh_sapling(height, left, right));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
height += 1;
|
|
|
|
|
current_layer = next_layer_up;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert!(current_layer.len() == 1);
|
|
|
|
|
|
|
|
|
|
NoteCommitmentTree {
|
|
|
|
|
root: Root(current_layer.remove(0)),
|
|
|
|
|
height,
|
|
|
|
|
count,
|
|
|
|
|
}
|
2019-09-25 23:23:04 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-15 12:20:17 -07:00
|
|
|
|
impl NoteCommitmentTree {
|
|
|
|
|
/// Get the Jubjub-based Pedersen hash of root node of this merkle tree of
|
|
|
|
|
/// commitment notes.
|
|
|
|
|
pub fn hash(&self) -> [u8; 32] {
|
|
|
|
|
self.root.0
|
2019-09-25 23:23:04 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-15 12:20:17 -07:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
|
|
|
|
|
use hex::FromHex;
|
|
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
2020-09-18 19:47:22 -07:00
|
|
|
|
const HEX_EMPTY_ROOTS: [&str; 33] = [
|
|
|
|
|
"0100000000000000000000000000000000000000000000000000000000000000",
|
|
|
|
|
"817de36ab2d57feb077634bca77819c8e0bd298c04f6fed0e6a83cc1356ca155",
|
|
|
|
|
"ffe9fc03f18b176c998806439ff0bb8ad193afdb27b2ccbc88856916dd804e34",
|
|
|
|
|
"d8283386ef2ef07ebdbb4383c12a739a953a4d6e0d6fb1139a4036d693bfbb6c",
|
|
|
|
|
"e110de65c907b9dea4ae0bd83a4b0a51bea175646a64c12b4c9f931b2cb31b49",
|
|
|
|
|
"912d82b2c2bca231f71efcf61737fbf0a08befa0416215aeef53e8bb6d23390a",
|
|
|
|
|
"8ac9cf9c391e3fd42891d27238a81a8a5c1d3a72b1bcbea8cf44a58ce7389613",
|
|
|
|
|
"d6c639ac24b46bd19341c91b13fdcab31581ddaf7f1411336a271f3d0aa52813",
|
|
|
|
|
"7b99abdc3730991cc9274727d7d82d28cb794edbc7034b4f0053ff7c4b680444",
|
|
|
|
|
"43ff5457f13b926b61df552d4e402ee6dc1463f99a535f9a713439264d5b616b",
|
|
|
|
|
"ba49b659fbd0b7334211ea6a9d9df185c757e70aa81da562fb912b84f49bce72",
|
|
|
|
|
"4777c8776a3b1e69b73a62fa701fa4f7a6282d9aee2c7a6b82e7937d7081c23c",
|
|
|
|
|
"ec677114c27206f5debc1c1ed66f95e2b1885da5b7be3d736b1de98579473048",
|
|
|
|
|
"1b77dac4d24fb7258c3c528704c59430b630718bec486421837021cf75dab651",
|
|
|
|
|
"bd74b25aacb92378a871bf27d225cfc26baca344a1ea35fdd94510f3d157082c",
|
|
|
|
|
"d6acdedf95f608e09fa53fb43dcd0990475726c5131210c9e5caeab97f0e642f",
|
|
|
|
|
"1ea6675f9551eeb9dfaaa9247bc9858270d3d3a4c5afa7177a984d5ed1be2451",
|
|
|
|
|
"6edb16d01907b759977d7650dad7e3ec049af1a3d875380b697c862c9ec5d51c",
|
|
|
|
|
"cd1c8dbf6e3acc7a80439bc4962cf25b9dce7c896f3a5bd70803fc5a0e33cf00",
|
|
|
|
|
"6aca8448d8263e547d5ff2950e2ed3839e998d31cbc6ac9fd57bc6002b159216",
|
|
|
|
|
"8d5fa43e5a10d11605ac7430ba1f5d81fb1b68d29a640405767749e841527673",
|
|
|
|
|
"08eeab0c13abd6069e6310197bf80f9c1ea6de78fd19cbae24d4a520e6cf3023",
|
|
|
|
|
"0769557bc682b1bf308646fd0b22e648e8b9e98f57e29f5af40f6edb833e2c49",
|
|
|
|
|
"4c6937d78f42685f84b43ad3b7b00f81285662f85c6a68ef11d62ad1a3ee0850",
|
|
|
|
|
"fee0e52802cb0c46b1eb4d376c62697f4759f6c8917fa352571202fd778fd712",
|
|
|
|
|
"16d6252968971a83da8521d65382e61f0176646d771c91528e3276ee45383e4a",
|
|
|
|
|
"d2e1642c9a462229289e5b0e3b7f9008e0301cbb93385ee0e21da2545073cb58",
|
|
|
|
|
"a5122c08ff9c161d9ca6fc462073396c7d7d38e8ee48cdb3bea7e2230134ed6a",
|
|
|
|
|
"28e7b841dcbc47cceb69d7cb8d94245fb7cb2ba3a7a6bc18f13f945f7dbd6e2a",
|
|
|
|
|
"e1f34b034d4a3cd28557e2907ebf990c918f64ecb50a94f01d6fda5ca5c7ef72",
|
|
|
|
|
"12935f14b676509b81eb49ef25f39269ed72309238b4c145803544b646dca62d",
|
|
|
|
|
"b2eed031d4d6a4f02a097f80b54cc1541d4163c6b6f5971f88b6e41d35c53814",
|
|
|
|
|
"fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493e",
|
|
|
|
|
];
|
|
|
|
|
|
2020-09-15 12:20:17 -07:00
|
|
|
|
#[test]
|
|
|
|
|
fn incremental_roots() {
|
|
|
|
|
// From https://github.com/zcash/zcash/blob/master/src/test/data/merkle_commitments_sapling.json
|
2020-09-18 19:47:22 -07:00
|
|
|
|
// Byte-reversed from those ones because the original test vectors are loaded using uint256S()
|
2020-09-15 12:20:17 -07:00
|
|
|
|
let commitments = [
|
|
|
|
|
"b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55",
|
|
|
|
|
"225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b11458",
|
|
|
|
|
"7c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e036c",
|
|
|
|
|
"50421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a030",
|
|
|
|
|
"aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc12",
|
|
|
|
|
"f76748d40d5ee5f9a608512e7954dd515f86e8f6d009141c89163de1cf351a02",
|
|
|
|
|
"bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0b2e",
|
|
|
|
|
"da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a511",
|
|
|
|
|
"3a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f77446",
|
|
|
|
|
"c7ca8f7df8fd997931d33985d935ee2d696856cc09cc516d419ea6365f163008",
|
|
|
|
|
"f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c3702",
|
|
|
|
|
"e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c608",
|
|
|
|
|
"8cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e826",
|
|
|
|
|
"22fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c03",
|
|
|
|
|
"f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c",
|
|
|
|
|
"3a3661bc12b72646c94bc6c92796e81953985ee62d80a9ec3645a9a95740ac15",
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// From https://github.com/zcash/zcash/blob/master/src/test/data/merkle_roots_sapling.json
|
|
|
|
|
let _roots = [
|
|
|
|
|
"8c3daa300c9710bf24d2595536e7c80ff8d147faca726636d28e8683a0c27703",
|
|
|
|
|
"8611f17378eb55e8c3c3f0a5f002e2b0a7ca39442fc928322b8072d1079c213d",
|
|
|
|
|
"3db73b998d536be0e1c2ec124df8e0f383ae7b602968ff6a5276ca0695023c46",
|
|
|
|
|
"7ac2e6442fec5970e116dfa4f2ee606f395366cafb1fa7dfd6c3de3ce18c4363",
|
|
|
|
|
"6a8f11ab2a11c262e39ed4ea3825ae6c94739ccf94479cb69402c5722b034532",
|
|
|
|
|
"149595eed0b54a7e694cc8a68372525b9ae2c7b102514f527460db91eb690565",
|
|
|
|
|
"8c0432f1994a2381a7a4b5fda770336011f9e0b30784f9a5597901619c797045",
|
|
|
|
|
"e780c48d70420601f3313ff8488d7766b70c059c53aa3cda2ff1ef57ff62383c",
|
|
|
|
|
"f919f03caaed8a2c60f58c0d43838f83e670dc7e8ccd25daa04a13f3e8f45541",
|
|
|
|
|
"74f32b36629724038e71cbd6823b5a666440205a7d1a9242e95870b53d81f34a",
|
|
|
|
|
"a4af205a4e1ee02102866b23a68930ac33efda9235832f49b17fcc4939be4525",
|
|
|
|
|
"a946a42f1636045a16e65b2308e036d9da70089686c87c692e45912bd1cab772",
|
|
|
|
|
"a1db2dbac055364c1cb43cbeb49c7e2815bff855122602a2ad0fb981a91e0e39",
|
|
|
|
|
"16329b3ba4f0640f4d306532d9ea6ba0fbf0e70e44ed57d27b4277ed9cda6849",
|
|
|
|
|
"7b6523b2d9b23f72fec6234aa6a1f8fae3dba1c6a266023ea8b1826feba7a25c",
|
|
|
|
|
"5c0bea7e17bde5bee4eb795c2eec3d389a68da587b36dd687b134826ecc09308",
|
|
|
|
|
];
|
|
|
|
|
|
2020-09-18 19:47:22 -07:00
|
|
|
|
for i in 0..32 {
|
|
|
|
|
println!("EMPTY_ROOT : {:?}", hex::encode(EMPTY_ROOTS[i]));
|
|
|
|
|
println!("HEX_EMPTY_ROOT: {:?}", HEX_EMPTY_ROOTS[i]);
|
|
|
|
|
}
|
2020-09-15 12:20:17 -07:00
|
|
|
|
|
2020-09-18 19:47:22 -07:00
|
|
|
|
// let mut leaves = vec![];
|
2020-09-15 12:20:17 -07:00
|
|
|
|
|
2020-09-18 19:47:22 -07:00
|
|
|
|
// for cm_u in commitments.iter() {
|
|
|
|
|
// let bytes = <[u8; 32]>::from_hex(cm_u).unwrap();
|
2020-09-15 12:20:17 -07:00
|
|
|
|
|
2020-09-18 19:47:22 -07:00
|
|
|
|
// println!("bytes: {:?}", hex::encode(bytes));
|
2020-09-15 12:20:17 -07:00
|
|
|
|
|
2020-09-18 19:47:22 -07:00
|
|
|
|
// leaves.push(jubjub::Fq::from_bytes(&bytes).unwrap());
|
2020-09-15 12:20:17 -07:00
|
|
|
|
|
2020-09-18 19:47:22 -07:00
|
|
|
|
// let tree = NoteCommitmentTree::from(leaves.clone());
|
|
|
|
|
|
|
|
|
|
// println!("root: {:x?}", hex::encode(tree.hash()));
|
|
|
|
|
// }
|
2019-09-25 23:23:04 -07:00
|
|
|
|
}
|
|
|
|
|
}
|