mirror of https://github.com/zcash/orchard.git
src::tree.rs: Implement MerklePath.root() method.
Co-authored-by: Kris Nuttycombe <kris@electriccoin.co>
This commit is contained in:
parent
e21f133862
commit
b33248bdb0
|
@ -198,12 +198,11 @@ impl Builder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consistency check: all anchors must be equal.
|
// Consistency check: all anchors must be equal.
|
||||||
let _cm = note.commitment();
|
let cm = note.commitment();
|
||||||
// TODO: Once we have tree logic.
|
let path_root: Anchor = merkle_path.root(cm.into());
|
||||||
// let path_root: bls12_381::Scalar = merkle_path.root(cmu).into();
|
if path_root != self.anchor {
|
||||||
// if path_root != anchor {
|
return Err("All anchors must be equal.");
|
||||||
// return Err(Error::AnchorMismatch);
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
self.spends.push(SpendInfo {
|
self.spends.push(SpendInfo {
|
||||||
dummy_sk: None,
|
dummy_sk: None,
|
||||||
|
@ -487,7 +486,7 @@ pub mod testing {
|
||||||
rng: R,
|
rng: R,
|
||||||
sk: SpendingKey,
|
sk: SpendingKey,
|
||||||
anchor: Anchor,
|
anchor: Anchor,
|
||||||
notes: Vec<Note>,
|
notes: Vec<(Note, MerklePath)>,
|
||||||
recipient_amounts: Vec<(Address, NoteValue)>,
|
recipient_amounts: Vec<(Address, NoteValue)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,8 +498,8 @@ pub mod testing {
|
||||||
let flags = Flags::from_parts(true, true);
|
let flags = Flags::from_parts(true, true);
|
||||||
let mut builder = Builder::new(flags, self.anchor);
|
let mut builder = Builder::new(flags, self.anchor);
|
||||||
|
|
||||||
for note in self.notes.into_iter() {
|
for (note, path) in self.notes.into_iter() {
|
||||||
builder.add_spend(fvk.clone(), note, MerklePath).unwrap();
|
builder.add_spend(fvk.clone(), note, path).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (addr, value) in self.recipient_amounts.into_iter() {
|
for (addr, value) in self.recipient_amounts.into_iter() {
|
||||||
|
|
|
@ -19,6 +19,9 @@ pub mod util;
|
||||||
|
|
||||||
pub use load::{OrchardFixedBase, OrchardFixedBasesFull, ValueCommitV};
|
pub use load::{OrchardFixedBase, OrchardFixedBasesFull, ValueCommitV};
|
||||||
|
|
||||||
|
/// $\mathsf{MerkleDepth^{Orchard}}$
|
||||||
|
pub(crate) const MERKLE_DEPTH_ORCHARD: usize = 32;
|
||||||
|
|
||||||
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
|
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
|
||||||
pub(crate) const L_ORCHARD_BASE: usize = 255;
|
pub(crate) const L_ORCHARD_BASE: usize = 255;
|
||||||
|
|
||||||
|
|
|
@ -63,3 +63,11 @@ impl From<NoteCommitment> for ExtractedNoteCommitment {
|
||||||
ExtractedNoteCommitment(extract_p(&cm.0))
|
ExtractedNoteCommitment(extract_p(&cm.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for ExtractedNoteCommitment {
|
||||||
|
type Target = pallas::Base;
|
||||||
|
|
||||||
|
fn deref(&self) -> &pallas::Base {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use pasta_curves::pallas;
|
||||||
use subtle::CtOption;
|
use subtle::CtOption;
|
||||||
|
|
||||||
use crate::spec::extract_p_bottom;
|
use crate::spec::extract_p_bottom;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
mod addition;
|
mod addition;
|
||||||
use self::addition::IncompletePoint;
|
use self::addition::IncompletePoint;
|
||||||
|
@ -20,6 +21,18 @@ fn lebs2ip_k(bits: &[bool]) -> u32 {
|
||||||
.fold(0u32, |acc, (i, b)| acc + if *b { 1 << i } else { 0 })
|
.fold(0u32, |acc, (i, b)| acc + if *b { 1 << i } else { 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The sequence of K bits in little-endian order representing an integer
|
||||||
|
/// up to `2^K` - 1.
|
||||||
|
pub fn i2lebsp_k(int: usize) -> [bool; K] {
|
||||||
|
assert!(int < (1 << K));
|
||||||
|
|
||||||
|
(0..K)
|
||||||
|
.map(|mask| ((int & (1 << mask)) >> mask) == 1)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// Pads the given iterator (which MUST have length $\leq K * C$) with zero-bits to a
|
/// Pads the given iterator (which MUST have length $\leq K * C$) with zero-bits to a
|
||||||
/// multiple of $K$ bits.
|
/// multiple of $K$ bits.
|
||||||
struct Pad<I: Iterator<Item = bool>> {
|
struct Pad<I: Iterator<Item = bool>> {
|
||||||
|
|
96
src/tree.rs
96
src/tree.rs
|
@ -1,15 +1,103 @@
|
||||||
|
use crate::{
|
||||||
|
constants::{MERKLE_CRH_PERSONALIZATION, MERKLE_DEPTH_ORCHARD},
|
||||||
|
note::commitment::ExtractedNoteCommitment,
|
||||||
|
primitives::sinsemilla::{i2lebsp_k, HashDomain, K},
|
||||||
|
};
|
||||||
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||||
|
|
||||||
|
use ff::{PrimeField, PrimeFieldBits};
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
|
use std::{convert::TryInto, iter};
|
||||||
|
|
||||||
/// The root of an Orchard commitment tree.
|
/// The root of an Orchard commitment tree.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||||
pub struct Anchor(pub [u8; 32]);
|
pub struct Anchor(pub [u8; 32]);
|
||||||
|
|
||||||
|
impl From<pallas::Base> for Anchor {
|
||||||
|
fn from(anchor_field: pallas::Base) -> Anchor {
|
||||||
|
Anchor(anchor_field.to_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MerklePath;
|
pub struct MerklePath {
|
||||||
|
position: u32,
|
||||||
|
auth_path: [pallas::Base; MERKLE_DEPTH_ORCHARD],
|
||||||
|
}
|
||||||
|
|
||||||
impl MerklePath {
|
impl MerklePath {
|
||||||
/// Generates a dummy Merkle path for use in dummy spent notes.
|
/// Generates a dummy Merkle path for use in dummy spent notes.
|
||||||
pub(crate) fn dummy(_rng: &mut impl RngCore) -> Self {
|
pub(crate) fn dummy(rng: &mut impl RngCore) -> Self {
|
||||||
MerklePath
|
MerklePath {
|
||||||
|
position: rng.next_u32(),
|
||||||
|
auth_path: (0..MERKLE_DEPTH_ORCHARD)
|
||||||
|
.map(|_| pallas::Base::rand())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn root(&self, cmx: ExtractedNoteCommitment) -> Anchor {
|
||||||
|
// Initialize `node` to the first hash.
|
||||||
|
let init_node = {
|
||||||
|
let pos = self.position % 2 == 1;
|
||||||
|
hash_layer(0, cond_swap(pos, *cmx, self.auth_path[0]))
|
||||||
|
};
|
||||||
|
let node = self.auth_path[1..]
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.fold(init_node, |node, (i, sibling)| {
|
||||||
|
let l_star = i + 1;
|
||||||
|
let swap = (self.position >> l_star) == 1;
|
||||||
|
hash_layer(l_star, cond_swap(swap, node, *sibling))
|
||||||
|
});
|
||||||
|
Anchor(node.to_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <https://zips.z.cash/protocol/protocol.pdf#orchardmerklecrh>
|
||||||
|
fn hash_layer(l_star: usize, pair: Pair) -> pallas::Base {
|
||||||
|
// MerkleCRH Sinsemilla hash domain.
|
||||||
|
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
|
||||||
|
|
||||||
|
domain
|
||||||
|
.hash(
|
||||||
|
iter::empty()
|
||||||
|
.chain(i2lebsp_k(l_star).iter().copied().take(K))
|
||||||
|
.chain(
|
||||||
|
pair.left
|
||||||
|
.to_le_bits()
|
||||||
|
.iter()
|
||||||
|
.by_val()
|
||||||
|
.take(pallas::Base::NUM_BITS as usize),
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
pair.right
|
||||||
|
.to_le_bits()
|
||||||
|
.iter()
|
||||||
|
.by_val()
|
||||||
|
.take(pallas::Base::NUM_BITS as usize),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue