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.
|
||||
let _cm = note.commitment();
|
||||
// TODO: Once we have tree logic.
|
||||
// let path_root: bls12_381::Scalar = merkle_path.root(cmu).into();
|
||||
// if path_root != anchor {
|
||||
// return Err(Error::AnchorMismatch);
|
||||
// }
|
||||
let cm = note.commitment();
|
||||
let path_root: Anchor = merkle_path.root(cm.into());
|
||||
if path_root != self.anchor {
|
||||
return Err("All anchors must be equal.");
|
||||
}
|
||||
|
||||
self.spends.push(SpendInfo {
|
||||
dummy_sk: None,
|
||||
|
@ -487,7 +486,7 @@ pub mod testing {
|
|||
rng: R,
|
||||
sk: SpendingKey,
|
||||
anchor: Anchor,
|
||||
notes: Vec<Note>,
|
||||
notes: Vec<(Note, MerklePath)>,
|
||||
recipient_amounts: Vec<(Address, NoteValue)>,
|
||||
}
|
||||
|
||||
|
@ -499,8 +498,8 @@ pub mod testing {
|
|||
let flags = Flags::from_parts(true, true);
|
||||
let mut builder = Builder::new(flags, self.anchor);
|
||||
|
||||
for note in self.notes.into_iter() {
|
||||
builder.add_spend(fvk.clone(), note, MerklePath).unwrap();
|
||||
for (note, path) in self.notes.into_iter() {
|
||||
builder.add_spend(fvk.clone(), note, path).unwrap();
|
||||
}
|
||||
|
||||
for (addr, value) in self.recipient_amounts.into_iter() {
|
||||
|
|
|
@ -19,6 +19,9 @@ pub mod util;
|
|||
|
||||
pub use load::{OrchardFixedBase, OrchardFixedBasesFull, ValueCommitV};
|
||||
|
||||
/// $\mathsf{MerkleDepth^{Orchard}}$
|
||||
pub(crate) const MERKLE_DEPTH_ORCHARD: usize = 32;
|
||||
|
||||
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
|
||||
pub(crate) const L_ORCHARD_BASE: usize = 255;
|
||||
|
||||
|
|
|
@ -63,3 +63,11 @@ impl From<NoteCommitment> for ExtractedNoteCommitment {
|
|||
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 crate::spec::extract_p_bottom;
|
||||
use std::convert::TryInto;
|
||||
|
||||
mod addition;
|
||||
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 })
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// multiple of $K$ bits.
|
||||
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 std::{convert::TryInto, iter};
|
||||
|
||||
/// The root of an Orchard commitment tree.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
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)]
|
||||
pub struct MerklePath;
|
||||
pub struct MerklePath {
|
||||
position: u32,
|
||||
auth_path: [pallas::Base; MERKLE_DEPTH_ORCHARD],
|
||||
}
|
||||
|
||||
impl MerklePath {
|
||||
/// Generates a dummy Merkle path for use in dummy spent notes.
|
||||
pub(crate) fn dummy(_rng: &mut impl RngCore) -> Self {
|
||||
MerklePath
|
||||
pub(crate) fn dummy(rng: &mut impl RngCore) -> Self {
|
||||
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