Implements the updated, total definition of MerkleCRH^Orchard

See https://github.com/zcash/zips/pull/530
This commit is contained in:
Kris Nuttycombe 2021-06-30 10:29:05 -06:00
parent 21b77d6ec5
commit fd94759fab
1 changed files with 27 additions and 27 deletions

View File

@ -17,7 +17,7 @@ use serde::de::{Deserializer, Error};
use serde::ser::Serializer; use serde::ser::Serializer;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::iter; use std::iter;
use subtle::{ConstantTimeEq, CtOption}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
// The uncommitted leaf is defined as pallas::Base(2). // The uncommitted leaf is defined as pallas::Base(2).
// <https://zips.z.cash/protocol/protocol.pdf#thmuncommittedorchard> // <https://zips.z.cash/protocol/protocol.pdf#thmuncommittedorchard>
@ -170,19 +170,19 @@ fn hash_with_l(l: usize, pair: Pair) -> CtOption<pallas::Base> {
/// can produce a bottom value which needs to be accounted for in /// can produce a bottom value which needs to be accounted for in
/// the production of a Merkle root. Leaf nodes are always wrapped /// the production of a Merkle root. Leaf nodes are always wrapped
/// with the `Some` constructor. /// with the `Some` constructor.
#[derive(Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct OrchardIncrementalTreeDigest(CtOption<pallas::Base>); pub struct OrchardIncrementalTreeDigest(pallas::Base);
impl OrchardIncrementalTreeDigest { impl OrchardIncrementalTreeDigest {
/// Creates an incremental tree leaf digest from the specified /// Creates an incremental tree leaf digest from the specified
/// Orchard extracted note commitment. /// Orchard extracted note commitment.
pub fn from_cmx(value: &ExtractedNoteCommitment) -> Self { pub fn from_cmx(value: &ExtractedNoteCommitment) -> Self {
OrchardIncrementalTreeDigest(CtOption::new(**value, 1.into())) OrchardIncrementalTreeDigest(**value)
} }
/// Convert this digest to its canonical byte representation. /// Convert this digest to its canonical byte representation.
pub fn to_bytes(&self) -> Option<[u8; 32]> { pub fn to_bytes(&self) -> [u8; 32] {
<Option<pallas::Base>>::from(self.0).map(|b| b.to_bytes()) self.0.to_bytes()
} }
/// Parses a incremental tree leaf digest from the bytes of /// Parses a incremental tree leaf digest from the bytes of
@ -191,8 +191,7 @@ impl OrchardIncrementalTreeDigest {
/// Returns the empty `CtOption` if the provided bytes represent /// Returns the empty `CtOption` if the provided bytes represent
/// a non-canonical encoding. /// a non-canonical encoding.
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> { pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
pallas::Base::from_bytes(bytes) pallas::Base::from_bytes(bytes).map(OrchardIncrementalTreeDigest)
.map(|b| OrchardIncrementalTreeDigest(CtOption::new(b, 1.into())))
} }
} }
@ -215,24 +214,31 @@ impl std::hash::Hash for OrchardIncrementalTreeDigest {
} }
} }
impl ConditionallySelectable for OrchardIncrementalTreeDigest {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
OrchardIncrementalTreeDigest(pallas::Base::conditional_select(&a.0, &b.0, choice))
}
}
impl Hashable for OrchardIncrementalTreeDigest { impl Hashable for OrchardIncrementalTreeDigest {
fn empty_leaf() -> Self { fn empty_leaf() -> Self {
OrchardIncrementalTreeDigest(CtOption::new(*UNCOMMITTED_ORCHARD, 1.into())) OrchardIncrementalTreeDigest(*UNCOMMITTED_ORCHARD)
} }
fn combine(altitude: Altitude, left_opt: &Self, right_opt: &Self) -> Self { fn combine(altitude: Altitude, left: &Self, right: &Self) -> Self {
OrchardIncrementalTreeDigest(left_opt.0.and_then(|left| { hash_with_l(
right_opt altitude.into(),
.0 Pair {
.and_then(|right| hash_with_l(altitude.into(), Pair { left, right })) left: left.0,
})) right: right.0,
},
)
.map(OrchardIncrementalTreeDigest)
.unwrap_or_else(|| OrchardIncrementalTreeDigest(pallas::Base::zero()))
} }
fn empty_root(altitude: Altitude) -> Self { fn empty_root(altitude: Altitude) -> Self {
OrchardIncrementalTreeDigest(CtOption::new( OrchardIncrementalTreeDigest(EMPTY_ROOTS[<usize>::from(altitude)])
EMPTY_ROOTS[<usize>::from(altitude)],
1.into(),
))
} }
} }
@ -262,8 +268,6 @@ pub mod testing {
}; };
use std::convert::TryInto; use std::convert::TryInto;
#[cfg(test)]
use subtle::CtOption;
use crate::{ use crate::{
constants::MERKLE_DEPTH_ORCHARD, constants::MERKLE_DEPTH_ORCHARD,
@ -419,7 +423,6 @@ pub mod testing {
assert_eq!( assert_eq!(
OrchardIncrementalTreeDigest::empty_root(Altitude::from(altitude as u8)) OrchardIncrementalTreeDigest::empty_root(Altitude::from(altitude as u8))
.0 .0
.unwrap()
.to_bytes(), .to_bytes(),
*tv_root, *tv_root,
"Empty root mismatch at altitude {}", "Empty root mismatch at altitude {}",
@ -471,14 +474,11 @@ pub mod testing {
let mut frontier = BridgeFrontier::<OrchardIncrementalTreeDigest, 32>::new(); let mut frontier = BridgeFrontier::<OrchardIncrementalTreeDigest, 32>::new();
for commitment in commitments.iter() { for commitment in commitments.iter() {
let cmx = OrchardIncrementalTreeDigest(CtOption::new( let cmx = OrchardIncrementalTreeDigest(pallas::Base::from_bytes(commitment).unwrap());
pallas::Base::from_bytes(commitment).unwrap(),
1.into(),
));
frontier.append(&cmx); frontier.append(&cmx);
} }
assert_eq!( assert_eq!(
frontier.root().0.unwrap(), frontier.root().0,
pallas::Base::from_bytes(&anchor).unwrap() pallas::Base::from_bytes(&anchor).unwrap()
); );
} }