src::tree.rs: Implement MerklePath.root() method.

Co-authored-by: Kris Nuttycombe <kris@electriccoin.co>
This commit is contained in:
therealyingtong 2021-06-08 13:18:16 +08:00
parent e21f133862
commit b33248bdb0
5 changed files with 124 additions and 13 deletions

View File

@ -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() {

View File

@ -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;

View File

@ -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
}
}

View File

@ -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>> {

View File

@ -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()
}