2021-06-17 07:34:09 -07:00
|
|
|
//! Types related to Orchard note commitment trees and anchors.
|
|
|
|
|
2021-06-07 22:18:16 -07:00
|
|
|
use crate::{
|
2021-06-09 19:31:47 -07:00
|
|
|
constants::{
|
|
|
|
util::gen_const_array, L_ORCHARD_MERKLE, MERKLE_CRH_PERSONALIZATION, MERKLE_DEPTH_ORCHARD,
|
|
|
|
},
|
2021-06-07 22:18:16 -07:00
|
|
|
note::commitment::ExtractedNoteCommitment,
|
2021-06-09 19:31:47 -07:00
|
|
|
primitives::sinsemilla::{i2lebsp_k, HashDomain},
|
2021-06-07 22:18:16 -07:00
|
|
|
};
|
2021-06-21 09:13:30 -07:00
|
|
|
use incrementalmerkletree::{Altitude, Hashable};
|
2021-06-15 21:09:25 -07:00
|
|
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
2021-06-07 22:18:16 -07:00
|
|
|
|
2021-06-15 07:41:33 -07:00
|
|
|
use ff::{Field, PrimeField, PrimeFieldBits};
|
2021-06-23 14:39:55 -07:00
|
|
|
use lazy_static::lazy_static;
|
2021-04-14 21:14:34 -07:00
|
|
|
use rand::RngCore;
|
2021-06-24 08:57:35 -07:00
|
|
|
use serde::de::{Deserializer, Error};
|
2021-06-21 09:13:30 -07:00
|
|
|
use serde::ser::Serializer;
|
|
|
|
use serde::{Deserialize, Serialize};
|
2021-06-09 09:44:30 -07:00
|
|
|
use std::iter;
|
2021-06-30 09:29:05 -07:00
|
|
|
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
2021-04-14 21:14:34 -07:00
|
|
|
|
2021-06-23 14:39:55 -07:00
|
|
|
// The uncommitted leaf is defined as pallas::Base(2).
|
|
|
|
// <https://zips.z.cash/protocol/protocol.pdf#thmuncommittedorchard>
|
|
|
|
lazy_static! {
|
2021-06-24 11:33:02 -07:00
|
|
|
static ref UNCOMMITTED_ORCHARD: pallas::Base = pallas::Base::from_u64(2);
|
2021-06-10 22:02:05 -07:00
|
|
|
pub(crate) static ref EMPTY_ROOTS: Vec<pallas::Base> = {
|
2021-06-23 14:39:55 -07:00
|
|
|
iter::empty()
|
2021-06-24 11:33:02 -07:00
|
|
|
.chain(Some(*UNCOMMITTED_ORCHARD))
|
2021-06-23 14:39:55 -07:00
|
|
|
.chain(
|
2021-06-24 11:33:02 -07:00
|
|
|
(0..MERKLE_DEPTH_ORCHARD).scan(*UNCOMMITTED_ORCHARD, |state, l| {
|
2021-06-23 14:39:55 -07:00
|
|
|
*state = hash_with_l(
|
|
|
|
l,
|
|
|
|
Pair {
|
|
|
|
left: *state,
|
|
|
|
right: *state,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
Some(*state)
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.collect()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-01-20 12:30:35 -08:00
|
|
|
/// The root of an Orchard commitment tree.
|
2021-06-15 07:42:06 -07:00
|
|
|
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
|
2021-06-12 13:35:37 -07:00
|
|
|
pub struct Anchor(pallas::Base);
|
2021-04-14 21:14:34 -07:00
|
|
|
|
2021-06-07 22:18:16 -07:00
|
|
|
impl From<pallas::Base> for Anchor {
|
|
|
|
fn from(anchor_field: pallas::Base) -> Anchor {
|
2021-06-12 13:35:37 -07:00
|
|
|
Anchor(anchor_field)
|
2021-06-07 22:18:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-22 07:14:34 -07:00
|
|
|
impl Anchor {
|
|
|
|
pub(crate) fn inner(&self) -> pallas::Base {
|
|
|
|
self.0
|
2021-07-19 09:42:22 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-15 07:41:33 -07:00
|
|
|
impl Anchor {
|
|
|
|
/// Parses an Orchard anchor from a byte encoding.
|
|
|
|
pub fn from_bytes(bytes: [u8; 32]) -> Option<Anchor> {
|
|
|
|
pallas::Base::from_repr(bytes).map(Anchor)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the byte encoding of this anchor.
|
|
|
|
pub fn to_bytes(self) -> [u8; 32] {
|
|
|
|
self.0.to_repr()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 07:34:09 -07:00
|
|
|
/// The Merkle path from a leaf of the note commitment tree
|
|
|
|
/// to its anchor.
|
2021-04-14 21:14:34 -07:00
|
|
|
#[derive(Debug)]
|
2021-06-07 22:18:16 -07:00
|
|
|
pub struct MerklePath {
|
|
|
|
position: u32,
|
|
|
|
auth_path: [pallas::Base; MERKLE_DEPTH_ORCHARD],
|
|
|
|
}
|
2021-04-14 21:14:34 -07:00
|
|
|
|
|
|
|
impl MerklePath {
|
|
|
|
/// Generates a dummy Merkle path for use in dummy spent notes.
|
2021-06-11 12:20:41 -07:00
|
|
|
pub(crate) fn dummy(mut rng: &mut impl RngCore) -> Self {
|
2021-06-07 22:18:16 -07:00
|
|
|
MerklePath {
|
|
|
|
position: rng.next_u32(),
|
2021-06-11 12:20:41 -07:00
|
|
|
auth_path: gen_const_array(|_| pallas::Base::random(&mut rng)),
|
2021-06-07 22:18:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-09 22:54:42 -07:00
|
|
|
/// <https://zips.z.cash/protocol/protocol.pdf#orchardmerklecrh>
|
|
|
|
/// The layer with 2^n nodes is called "layer n":
|
|
|
|
/// - leaves are at layer MERKLE_DEPTH_ORCHARD = 32;
|
|
|
|
/// - the root is at layer 0.
|
2021-06-23 14:39:55 -07:00
|
|
|
/// `l` is MERKLE_DEPTH_ORCHARD - layer - 1.
|
2021-06-09 22:54:42 -07:00
|
|
|
/// - when hashing two leaves, we produce a node on the layer above the leaves, i.e.
|
2021-06-23 14:39:55 -07:00
|
|
|
/// layer = 31, l = 0
|
|
|
|
/// - when hashing to the final root, we produce the anchor with layer = 0, l = 31.
|
2021-06-24 08:43:10 -07:00
|
|
|
pub fn root(&self, cmx: ExtractedNoteCommitment) -> CtOption<Anchor> {
|
|
|
|
self.auth_path
|
2021-06-07 22:18:16 -07:00
|
|
|
.iter()
|
|
|
|
.enumerate()
|
2021-07-22 07:14:34 -07:00
|
|
|
.fold(
|
|
|
|
CtOption::new(cmx.inner(), 1.into()),
|
|
|
|
|node, (l, sibling)| {
|
|
|
|
let swap = self.position & (1 << l) != 0;
|
|
|
|
node.and_then(|n| hash_with_l(l, cond_swap(swap, n, *sibling)))
|
|
|
|
},
|
|
|
|
)
|
2021-06-24 08:43:10 -07:00
|
|
|
.map(Anchor)
|
2021-04-14 21:14:34 -07:00
|
|
|
}
|
2021-06-10 01:55:49 -07:00
|
|
|
|
|
|
|
/// Returns the position of the leaf using this Merkle path.
|
|
|
|
pub fn position(&self) -> u32 {
|
|
|
|
self.position
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the authentication path.
|
|
|
|
pub fn auth_path(&self) -> [pallas::Base; MERKLE_DEPTH_ORCHARD] {
|
|
|
|
self.auth_path
|
|
|
|
}
|
2021-04-14 21:14:34 -07:00
|
|
|
}
|
2021-06-07 22:18:16 -07:00
|
|
|
|
|
|
|
struct Pair {
|
|
|
|
left: pallas::Base,
|
|
|
|
right: pallas::Base,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cond_swap(swap: bool, node: pallas::Base, sibling: pallas::Base) -> Pair {
|
|
|
|
if swap {
|
|
|
|
Pair {
|
|
|
|
left: sibling,
|
|
|
|
right: node,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Pair {
|
|
|
|
left: node,
|
|
|
|
right: sibling,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-01 07:09:05 -07:00
|
|
|
/// Implements the function `hash` (internal to MerkleCRH^Orchard) defined
|
|
|
|
/// in <https://zips.z.cash/protocol/protocol.pdf#orchardmerklecrh>
|
|
|
|
///
|
2021-06-11 12:23:40 -07:00
|
|
|
/// The layer with 2^n nodes is called "layer n":
|
|
|
|
/// - leaves are at layer MERKLE_DEPTH_ORCHARD = 32;
|
|
|
|
/// - the root is at layer 0.
|
2021-06-23 14:39:55 -07:00
|
|
|
/// `l` is MERKLE_DEPTH_ORCHARD - layer - 1.
|
2021-06-11 12:23:40 -07:00
|
|
|
/// - when hashing two leaves, we produce a node on the layer above the leaves, i.e.
|
2021-06-23 14:39:55 -07:00
|
|
|
/// layer = 31, l = 0
|
|
|
|
/// - when hashing to the final root, we produce the anchor with layer = 0, l = 31.
|
|
|
|
fn hash_with_l(l: usize, pair: Pair) -> CtOption<pallas::Base> {
|
2021-06-07 22:18:16 -07:00
|
|
|
// MerkleCRH Sinsemilla hash domain.
|
|
|
|
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
|
|
|
|
|
2021-06-15 21:09:25 -07:00
|
|
|
domain.hash(
|
|
|
|
iter::empty()
|
2021-06-23 14:39:55 -07:00
|
|
|
.chain(i2lebsp_k(l).iter().copied())
|
2021-06-15 21:09:25 -07:00
|
|
|
.chain(
|
|
|
|
pair.left
|
|
|
|
.to_le_bits()
|
|
|
|
.iter()
|
|
|
|
.by_val()
|
|
|
|
.take(L_ORCHARD_MERKLE),
|
|
|
|
)
|
|
|
|
.chain(
|
|
|
|
pair.right
|
|
|
|
.to_le_bits()
|
|
|
|
.iter()
|
|
|
|
.by_val()
|
|
|
|
.take(L_ORCHARD_MERKLE),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-06-17 07:34:09 -07:00
|
|
|
/// A newtype wrapper for leaves and internal nodes in the Orchard
|
|
|
|
/// incremental note commitment tree.
|
2021-06-24 08:57:35 -07:00
|
|
|
///
|
|
|
|
/// This wraps a CtOption<pallas::Base> because Sinsemilla hashes
|
|
|
|
/// can produce a bottom value which needs to be accounted for in
|
|
|
|
/// the production of a Merkle root. Leaf nodes are always wrapped
|
|
|
|
/// with the `Some` constructor.
|
2021-06-30 09:29:05 -07:00
|
|
|
#[derive(Copy, Clone, Debug)]
|
2021-07-01 07:09:05 -07:00
|
|
|
pub struct MerkleCrhOrchardOutput(pallas::Base);
|
2021-06-17 07:34:09 -07:00
|
|
|
|
2021-07-01 07:09:05 -07:00
|
|
|
impl MerkleCrhOrchardOutput {
|
2021-06-24 11:34:58 -07:00
|
|
|
/// Creates an incremental tree leaf digest from the specified
|
|
|
|
/// Orchard extracted note commitment.
|
2021-06-17 08:54:27 -07:00
|
|
|
pub fn from_cmx(value: &ExtractedNoteCommitment) -> Self {
|
2021-07-22 07:14:34 -07:00
|
|
|
MerkleCrhOrchardOutput(value.inner())
|
2021-06-17 08:54:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Convert this digest to its canonical byte representation.
|
2021-06-30 09:29:05 -07:00
|
|
|
pub fn to_bytes(&self) -> [u8; 32] {
|
|
|
|
self.0.to_bytes()
|
2021-06-17 08:54:27 -07:00
|
|
|
}
|
2021-06-17 09:57:47 -07:00
|
|
|
|
|
|
|
/// Parses a incremental tree leaf digest from the bytes of
|
|
|
|
/// a note commitment.
|
2021-06-24 08:57:35 -07:00
|
|
|
///
|
|
|
|
/// Returns the empty `CtOption` if the provided bytes represent
|
|
|
|
/// a non-canonical encoding.
|
|
|
|
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
|
2021-07-01 07:09:05 -07:00
|
|
|
pallas::Base::from_bytes(bytes).map(MerkleCrhOrchardOutput)
|
2021-06-17 09:57:47 -07:00
|
|
|
}
|
2021-06-17 08:54:27 -07:00
|
|
|
}
|
|
|
|
|
2021-06-28 10:47:02 -07:00
|
|
|
/// This instance should only be used for hash table key comparisons.
|
2021-07-01 07:09:05 -07:00
|
|
|
impl std::cmp::PartialEq for MerkleCrhOrchardOutput {
|
2021-06-17 08:54:27 -07:00
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.0.ct_eq(&other.0).into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-28 10:47:02 -07:00
|
|
|
/// This instance should only be used for hash table key comparisons.
|
2021-07-01 07:09:05 -07:00
|
|
|
impl std::cmp::Eq for MerkleCrhOrchardOutput {}
|
2021-06-17 08:54:27 -07:00
|
|
|
|
2021-06-28 10:47:02 -07:00
|
|
|
/// This instance should only be used for hash table key hashing.
|
2021-07-01 07:09:05 -07:00
|
|
|
impl std::hash::Hash for MerkleCrhOrchardOutput {
|
2021-06-17 08:54:27 -07:00
|
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
2021-06-21 09:13:30 -07:00
|
|
|
<Option<pallas::Base>>::from(self.0)
|
|
|
|
.map(|b| b.to_bytes())
|
|
|
|
.hash(state)
|
2021-06-17 07:34:09 -07:00
|
|
|
}
|
|
|
|
}
|
2021-06-15 21:09:25 -07:00
|
|
|
|
2021-07-01 07:09:05 -07:00
|
|
|
impl ConditionallySelectable for MerkleCrhOrchardOutput {
|
2021-06-30 09:29:05 -07:00
|
|
|
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
2021-07-01 07:09:05 -07:00
|
|
|
MerkleCrhOrchardOutput(pallas::Base::conditional_select(&a.0, &b.0, choice))
|
2021-06-30 09:29:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-01 07:09:05 -07:00
|
|
|
impl Hashable for MerkleCrhOrchardOutput {
|
2021-06-15 21:09:25 -07:00
|
|
|
fn empty_leaf() -> Self {
|
2021-07-01 07:09:05 -07:00
|
|
|
MerkleCrhOrchardOutput(*UNCOMMITTED_ORCHARD)
|
2021-06-15 21:09:25 -07:00
|
|
|
}
|
|
|
|
|
2021-07-01 07:09:05 -07:00
|
|
|
/// Implements `MerkleCRH^Orchard` as defined in
|
|
|
|
/// <https://zips.z.cash/protocol/protocol.pdf#orchardmerklecrh>
|
2021-06-30 09:29:05 -07:00
|
|
|
fn combine(altitude: Altitude, left: &Self, right: &Self) -> Self {
|
|
|
|
hash_with_l(
|
|
|
|
altitude.into(),
|
|
|
|
Pair {
|
|
|
|
left: left.0,
|
|
|
|
right: right.0,
|
|
|
|
},
|
|
|
|
)
|
2021-07-01 07:09:05 -07:00
|
|
|
.map(MerkleCrhOrchardOutput)
|
|
|
|
.unwrap_or_else(|| MerkleCrhOrchardOutput(pallas::Base::zero()))
|
2021-06-15 21:09:25 -07:00
|
|
|
}
|
2021-06-23 14:39:55 -07:00
|
|
|
|
2021-06-28 10:47:02 -07:00
|
|
|
fn empty_root(altitude: Altitude) -> Self {
|
2021-07-01 07:09:05 -07:00
|
|
|
MerkleCrhOrchardOutput(EMPTY_ROOTS[<usize>::from(altitude)])
|
2021-06-23 14:39:55 -07:00
|
|
|
}
|
2021-06-07 22:18:16 -07:00
|
|
|
}
|
2021-06-08 00:27:08 -07:00
|
|
|
|
2021-07-01 07:09:05 -07:00
|
|
|
impl Serialize for MerkleCrhOrchardOutput {
|
2021-06-17 09:57:47 -07:00
|
|
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
|
|
self.to_bytes().serialize(serializer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-01 07:09:05 -07:00
|
|
|
impl<'de> Deserialize<'de> for MerkleCrhOrchardOutput {
|
2021-06-17 09:57:47 -07:00
|
|
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
|
|
let parsed = <[u8; 32]>::deserialize(deserializer)?;
|
2021-06-28 09:56:25 -07:00
|
|
|
<Option<_>>::from(Self::from_bytes(&parsed)).ok_or_else(|| {
|
|
|
|
Error::custom(
|
2021-06-28 10:47:02 -07:00
|
|
|
"Attempted to deserialize a non-canonical representation of a Pallas base field element.",
|
2021-06-28 09:56:25 -07:00
|
|
|
)
|
|
|
|
})
|
2021-06-17 09:57:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-08 00:27:08 -07:00
|
|
|
/// Generators for property testing.
|
|
|
|
#[cfg(any(test, feature = "test-dependencies"))]
|
|
|
|
pub mod testing {
|
2021-06-28 09:56:25 -07:00
|
|
|
#[cfg(test)]
|
2021-06-23 14:39:55 -07:00
|
|
|
use incrementalmerkletree::{
|
|
|
|
bridgetree::Frontier as BridgeFrontier, Altitude, Frontier, Hashable,
|
|
|
|
};
|
2021-06-28 09:56:25 -07:00
|
|
|
|
2021-06-08 00:27:08 -07:00
|
|
|
use std::convert::TryInto;
|
|
|
|
|
|
|
|
use crate::{
|
2021-06-09 22:54:42 -07:00
|
|
|
constants::MERKLE_DEPTH_ORCHARD,
|
2021-06-08 00:27:08 -07:00
|
|
|
note::{commitment::ExtractedNoteCommitment, testing::arb_note, Note},
|
|
|
|
value::{testing::arb_positive_note_value, MAX_NOTE_VALUE},
|
|
|
|
};
|
2021-06-28 09:56:25 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
use pasta_curves::arithmetic::FieldExt;
|
|
|
|
use pasta_curves::pallas;
|
2021-06-08 00:27:08 -07:00
|
|
|
|
|
|
|
use proptest::collection::vec;
|
|
|
|
use proptest::prelude::*;
|
|
|
|
|
2021-06-28 09:56:25 -07:00
|
|
|
#[cfg(test)]
|
2021-07-01 07:09:05 -07:00
|
|
|
use super::MerkleCrhOrchardOutput;
|
2021-06-28 09:56:25 -07:00
|
|
|
use super::{hash_with_l, Anchor, MerklePath, Pair, EMPTY_ROOTS};
|
2021-06-08 00:27:08 -07:00
|
|
|
|
2021-06-11 04:03:14 -07:00
|
|
|
#[test]
|
|
|
|
fn test_vectors() {
|
|
|
|
let tv_empty_roots = crate::test_vectors::commitment_tree::test_vectors().empty_roots;
|
|
|
|
|
|
|
|
for (height, root) in EMPTY_ROOTS.iter().enumerate() {
|
|
|
|
assert_eq!(tv_empty_roots[height], root.to_bytes());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-08 00:27:08 -07:00
|
|
|
prop_compose! {
|
|
|
|
/// Generates an arbitrary Merkle tree of with `n_notes` nonempty leaves.
|
2021-06-09 22:54:42 -07:00
|
|
|
pub fn arb_tree(n_notes: usize)
|
2021-06-08 00:27:08 -07:00
|
|
|
(
|
|
|
|
// generate note values that we're certain won't exceed MAX_NOTE_VALUE in total
|
|
|
|
notes in vec(
|
|
|
|
arb_positive_note_value(MAX_NOTE_VALUE / n_notes as u64).prop_flat_map(arb_note),
|
2021-06-09 22:54:42 -07:00
|
|
|
n_notes
|
2021-06-08 00:27:08 -07:00
|
|
|
),
|
|
|
|
)
|
|
|
|
-> (Vec<(Note, MerklePath)>, Anchor) {
|
|
|
|
// Inefficient algorithm to build a perfect subtree containing all notes.
|
|
|
|
let perfect_subtree_depth = (n_notes as f64).log2().ceil() as usize;
|
2021-06-09 22:54:42 -07:00
|
|
|
let n_leaves = 1 << perfect_subtree_depth;
|
|
|
|
|
|
|
|
let commitments: Vec<Option<ExtractedNoteCommitment>> = notes.iter().map(|note| {
|
2021-06-08 00:27:08 -07:00
|
|
|
let cmx: ExtractedNoteCommitment = note.commitment().into();
|
2021-06-09 22:54:42 -07:00
|
|
|
Some(cmx)
|
2021-06-08 00:27:08 -07:00
|
|
|
}).collect();
|
|
|
|
|
|
|
|
let padded_leaves = {
|
|
|
|
let mut padded_leaves = commitments.clone();
|
|
|
|
|
2021-06-09 22:54:42 -07:00
|
|
|
let pad = (0..(n_leaves - n_notes)).map(
|
|
|
|
|_| None
|
2021-06-08 00:27:08 -07:00
|
|
|
).collect::<Vec<_>>();
|
|
|
|
|
|
|
|
padded_leaves.extend_from_slice(&pad);
|
|
|
|
padded_leaves
|
|
|
|
};
|
|
|
|
|
2021-06-09 22:54:42 -07:00
|
|
|
let perfect_subtree = {
|
|
|
|
let mut perfect_subtree: Vec<Vec<Option<pallas::Base>>> = vec![
|
2021-07-22 07:14:34 -07:00
|
|
|
padded_leaves.iter().map(|cmx| cmx.map(|cmx| cmx.inner())).collect()
|
2021-06-09 22:54:42 -07:00
|
|
|
];
|
|
|
|
|
|
|
|
// <https://zips.z.cash/protocol/protocol.pdf#orchardmerklecrh>
|
|
|
|
// The layer with 2^n nodes is called "layer n":
|
|
|
|
// - leaves are at layer MERKLE_DEPTH_ORCHARD = 32;
|
|
|
|
// - the root is at layer 0.
|
2021-06-23 14:39:55 -07:00
|
|
|
// `l` is MERKLE_DEPTH_ORCHARD - layer - 1.
|
2021-06-09 22:54:42 -07:00
|
|
|
// - when hashing two leaves, we produce a node on the layer above the leaves, i.e.
|
2021-06-23 14:39:55 -07:00
|
|
|
// layer = 31, l = 0
|
|
|
|
// - when hashing to the final root, we produce the anchor with layer = 0, l = 31.
|
|
|
|
for l in 0..perfect_subtree_depth {
|
|
|
|
let inner_nodes = (0..(n_leaves >> (l + 1))).map(|pos| {
|
|
|
|
let left = perfect_subtree[l][pos * 2];
|
|
|
|
let right = perfect_subtree[l][pos * 2 + 1];
|
2021-06-09 22:54:42 -07:00
|
|
|
match (left, right) {
|
|
|
|
(None, None) => None,
|
|
|
|
(Some(left), None) => {
|
2021-06-23 14:39:55 -07:00
|
|
|
let right = EMPTY_ROOTS[l];
|
|
|
|
Some(hash_with_l(l, Pair {left, right}).unwrap())
|
2021-06-09 22:54:42 -07:00
|
|
|
},
|
|
|
|
(Some(left), Some(right)) => {
|
2021-06-23 14:39:55 -07:00
|
|
|
Some(hash_with_l(l, Pair {left, right}).unwrap())
|
2021-06-09 22:54:42 -07:00
|
|
|
},
|
|
|
|
(None, Some(_)) => {
|
|
|
|
unreachable!("The perfect subtree is left-packed.")
|
|
|
|
}
|
|
|
|
}
|
2021-06-08 00:27:08 -07:00
|
|
|
}).collect();
|
2021-06-09 22:54:42 -07:00
|
|
|
perfect_subtree.push(inner_nodes);
|
2021-06-08 00:27:08 -07:00
|
|
|
};
|
2021-06-09 22:54:42 -07:00
|
|
|
perfect_subtree
|
2021-06-08 00:27:08 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// Get Merkle path for each note commitment
|
|
|
|
let auth_paths = {
|
|
|
|
let mut auth_paths: Vec<MerklePath> = Vec::new();
|
|
|
|
for (pos, _) in commitments.iter().enumerate() {
|
2021-06-09 22:54:42 -07:00
|
|
|
|
|
|
|
// Initialize the authentication path to the path for an empty tree.
|
2021-06-08 00:27:08 -07:00
|
|
|
let mut auth_path: [pallas::Base; MERKLE_DEPTH_ORCHARD] = (0..MERKLE_DEPTH_ORCHARD).map(|idx| EMPTY_ROOTS[idx]).collect::<Vec<_>>().try_into().unwrap();
|
|
|
|
|
|
|
|
let mut layer_pos = pos;
|
|
|
|
for height in 0..perfect_subtree_depth {
|
|
|
|
let is_right_sibling = layer_pos & 1 == 1;
|
|
|
|
let sibling = if is_right_sibling {
|
2021-06-09 22:54:42 -07:00
|
|
|
// This node is the right sibling, so we need its left sibling at the current height.
|
|
|
|
perfect_subtree[height][layer_pos - 1]
|
2021-06-08 00:27:08 -07:00
|
|
|
} else {
|
2021-06-09 22:54:42 -07:00
|
|
|
// This node is the left sibling, so we need its right sibling at the current height.
|
|
|
|
perfect_subtree[height][layer_pos + 1]
|
2021-06-08 00:27:08 -07:00
|
|
|
};
|
2021-06-09 22:54:42 -07:00
|
|
|
if let Some(sibling) = sibling {
|
|
|
|
auth_path[height] = sibling;
|
|
|
|
}
|
2021-06-08 00:27:08 -07:00
|
|
|
layer_pos = (layer_pos - is_right_sibling as usize) / 2;
|
|
|
|
};
|
|
|
|
|
|
|
|
let path = MerklePath {position: pos as u32, auth_path};
|
|
|
|
auth_paths.push(path);
|
|
|
|
}
|
|
|
|
auth_paths
|
|
|
|
};
|
|
|
|
|
|
|
|
// Compute anchor for this tree
|
2021-06-24 08:43:10 -07:00
|
|
|
let anchor = auth_paths[0].root(notes[0].commitment().into()).unwrap();
|
2021-06-08 00:27:08 -07:00
|
|
|
|
|
|
|
(
|
|
|
|
notes.into_iter().zip(auth_paths.into_iter()).map(|(note, auth_path)| (note, auth_path)).collect(),
|
|
|
|
anchor
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2021-06-09 22:54:42 -07:00
|
|
|
|
|
|
|
proptest! {
|
2021-06-23 14:39:55 -07:00
|
|
|
#![proptest_config(ProptestConfig::with_cases(10))]
|
2021-06-09 22:54:42 -07:00
|
|
|
#[allow(clippy::redundant_closure)]
|
|
|
|
#[test]
|
|
|
|
fn tree(
|
|
|
|
(notes_and_auth_paths, anchor) in (1usize..4).prop_flat_map(|n_notes| arb_tree(n_notes))
|
|
|
|
) {
|
|
|
|
for (note, auth_path) in notes_and_auth_paths.iter() {
|
2021-06-24 08:43:10 -07:00
|
|
|
let computed_anchor = auth_path.root(note.commitment().into()).unwrap();
|
2021-06-09 22:54:42 -07:00
|
|
|
assert_eq!(anchor, computed_anchor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-21 09:13:30 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn empty_roots_incremental() {
|
|
|
|
let tv_empty_roots = crate::test_vectors::commitment_tree::test_vectors().empty_roots;
|
|
|
|
|
|
|
|
for (altitude, tv_root) in tv_empty_roots.iter().enumerate() {
|
|
|
|
assert_eq!(
|
2021-07-01 07:09:05 -07:00
|
|
|
MerkleCrhOrchardOutput::empty_root(Altitude::from(altitude as u8))
|
2021-06-21 09:13:30 -07:00
|
|
|
.0
|
|
|
|
.to_bytes(),
|
|
|
|
*tv_root,
|
|
|
|
"Empty root mismatch at altitude {}",
|
|
|
|
altitude
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2021-06-23 14:39:55 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn anchor_incremental() {
|
2021-06-24 11:34:58 -07:00
|
|
|
// These commitment values are derived from the bundle data that was generated for
|
|
|
|
// testing commitment tree construction inside of zcashd here.
|
|
|
|
// https://github.com/zcash/zcash/blob/ecec1f9769a5e37eb3f7fd89a4fcfb35bc28eed7/src/test/data/merkle_roots_orchard.h
|
2021-06-23 14:39:55 -07:00
|
|
|
let commitments = [
|
|
|
|
[
|
|
|
|
0x68, 0x13, 0x5c, 0xf4, 0x99, 0x33, 0x22, 0x90, 0x99, 0xa4, 0x4e, 0xc9, 0x9a, 0x75,
|
|
|
|
0xe1, 0xe1, 0xcb, 0x46, 0x40, 0xf9, 0xb5, 0xbd, 0xec, 0x6b, 0x32, 0x23, 0x85, 0x6f,
|
|
|
|
0xea, 0x16, 0x39, 0x0a,
|
|
|
|
],
|
|
|
|
[
|
|
|
|
0x78, 0x31, 0x50, 0x08, 0xfb, 0x29, 0x98, 0xb4, 0x30, 0xa5, 0x73, 0x1d, 0x67, 0x26,
|
|
|
|
0x20, 0x7d, 0xc0, 0xf0, 0xec, 0x81, 0xea, 0x64, 0xaf, 0x5c, 0xf6, 0x12, 0x95, 0x69,
|
|
|
|
0x01, 0xe7, 0x2f, 0x0e,
|
|
|
|
],
|
|
|
|
[
|
|
|
|
0xee, 0x94, 0x88, 0x05, 0x3a, 0x30, 0xc5, 0x96, 0xb4, 0x30, 0x14, 0x10, 0x5d, 0x34,
|
|
|
|
0x77, 0xe6, 0xf5, 0x78, 0xc8, 0x92, 0x40, 0xd1, 0xd1, 0xee, 0x17, 0x43, 0xb7, 0x7b,
|
|
|
|
0xb6, 0xad, 0xc4, 0x0a,
|
|
|
|
],
|
|
|
|
[
|
|
|
|
0x9d, 0xdc, 0xe7, 0xf0, 0x65, 0x01, 0xf3, 0x63, 0x76, 0x8c, 0x5b, 0xca, 0x3f, 0x26,
|
|
|
|
0x46, 0x60, 0x83, 0x4d, 0x4d, 0xf4, 0x46, 0xd1, 0x3e, 0xfc, 0xd7, 0xc6, 0xf1, 0x7b,
|
|
|
|
0x16, 0x7a, 0xac, 0x1a,
|
|
|
|
],
|
|
|
|
[
|
|
|
|
0xbd, 0x86, 0x16, 0x81, 0x1c, 0x6f, 0x5f, 0x76, 0x9e, 0xa4, 0x53, 0x9b, 0xba, 0xff,
|
|
|
|
0x0f, 0x19, 0x8a, 0x6c, 0xdf, 0x3b, 0x28, 0x0d, 0xd4, 0x99, 0x26, 0x16, 0x3b, 0xd5,
|
|
|
|
0x3f, 0x53, 0xa1, 0x21,
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
2021-06-28 10:47:02 -07:00
|
|
|
// This value was produced by the Python test vector generation code implemented here:
|
2021-06-24 11:34:58 -07:00
|
|
|
// https://github.com/zcash-hackworks/zcash-test-vectors/blob/f4d756410c8f2456f5d84cedf6dac6eb8c068eed/orchard_merkle_tree.py
|
2021-06-23 14:39:55 -07:00
|
|
|
let anchor = [
|
|
|
|
0xc8, 0x75, 0xbe, 0x2d, 0x60, 0x87, 0x3f, 0x8b, 0xcd, 0xeb, 0x91, 0x28, 0x2e, 0x64,
|
|
|
|
0x2e, 0x0c, 0xc6, 0x5f, 0xf7, 0xd0, 0x64, 0x2d, 0x13, 0x7b, 0x28, 0xcf, 0x28, 0xcc,
|
|
|
|
0x9c, 0x52, 0x7f, 0x0e,
|
|
|
|
];
|
|
|
|
|
2021-07-01 07:09:05 -07:00
|
|
|
let mut frontier = BridgeFrontier::<MerkleCrhOrchardOutput, 32>::new();
|
2021-06-23 14:39:55 -07:00
|
|
|
for commitment in commitments.iter() {
|
2021-07-01 07:09:05 -07:00
|
|
|
let cmx = MerkleCrhOrchardOutput(pallas::Base::from_bytes(commitment).unwrap());
|
2021-06-23 14:39:55 -07:00
|
|
|
frontier.append(&cmx);
|
|
|
|
}
|
|
|
|
assert_eq!(
|
2021-06-30 09:29:05 -07:00
|
|
|
frontier.root().0,
|
2021-06-23 14:39:55 -07:00
|
|
|
pallas::Base::from_bytes(&anchor).unwrap()
|
|
|
|
);
|
|
|
|
}
|
2021-06-08 00:27:08 -07:00
|
|
|
}
|