mirror of https://github.com/zcash/orchard.git
Implements the updated, total definition of MerkleCRH^Orchard
See https://github.com/zcash/zips/pull/530
This commit is contained in:
parent
21b77d6ec5
commit
fd94759fab
54
src/tree.rs
54
src/tree.rs
|
@ -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()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue