From 5ede33b23169b87e57b51ecb494298655ff3fe73 Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Tue, 16 Mar 2021 04:15:59 -0400 Subject: [PATCH] Orchard: update nullifiers --- zebra-chain/src/orchard/commitment.rs | 12 +++- zebra-chain/src/orchard/note.rs | 37 +++++----- zebra-chain/src/orchard/note/nullifiers.rs | 79 +++++++++++----------- zebra-chain/src/sapling/note/nullifiers.rs | 2 +- 4 files changed, 71 insertions(+), 59 deletions(-) diff --git a/zebra-chain/src/orchard/commitment.rs b/zebra-chain/src/orchard/commitment.rs index 367340cef..50e0f8ea3 100644 --- a/zebra-chain/src/orchard/commitment.rs +++ b/zebra-chain/src/orchard/commitment.rs @@ -11,7 +11,6 @@ use halo2::{ arithmetic::{CurveAffine, FieldExt}, pasta::pallas, }; - use rand_core::{CryptoRng, RngCore}; use crate::{ @@ -23,6 +22,7 @@ use crate::{ use super::{ keys::{Diversifier, TransmissionKey}, + note::Note, sinsemilla::*, }; @@ -95,15 +95,21 @@ impl NoteCommitment { /// before it is encrypted as part of an output of an _Action_. This is a /// higher level function that calls `NoteCommit^Orchard_rcm` internally. /// + /// Unlike in Sapling, the definition of an Orchard _note_ includes the ρ + /// field; the _note_'s position in the _note commitment tree_ does not need + /// to be known in order to compute this value. + /// /// NoteCommit^Orchard_rcm(repr_P(gd),repr_P(pkd), v, ρ, ψ) := /// /// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit #[allow(non_snake_case)] + // TODO: update to handle rseed, psi, rho pub fn new( csprng: &mut T, diversifier: Diversifier, transmission_key: TransmissionKey, value: Amount, + note: Note, ) -> Option<(CommitmentRandomness, Self)> where T: RngCore + CryptoRng, @@ -151,8 +157,8 @@ impl NoteCommitment { } } -/// A homomorphic Pedersen commitment to the net value of a note, used in Action -/// descriptions. +/// A homomorphic Pedersen commitment to the net value of a _note_, used in +/// Action descriptions. /// /// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit #[derive(Clone, Copy, Deserialize, PartialEq, Serialize)] diff --git a/zebra-chain/src/orchard/note.rs b/zebra-chain/src/orchard/note.rs index 2bca3dd6f..627e7b973 100644 --- a/zebra-chain/src/orchard/note.rs +++ b/zebra-chain/src/orchard/note.rs @@ -5,21 +5,22 @@ use halo2::pasta::pallas; -mod ciphertexts; -mod nullifiers; - -#[cfg(any(test, feature = "proptest-impl"))] -mod arbitrary; - -use crate::amount::{Amount, NonNegative}; +use crate::{ + amount::{Amount, NonNegative}, + transaction::Memo, +}; use super::{ commitment::CommitmentRandomness, keys::{Diversifier, TransmissionKey}, }; -pub use ciphertexts::{EncryptedNote, WrappedNoteKey}; +#[cfg(any(test, feature = "proptest-impl"))] +mod arbitrary; +mod ciphertexts; +mod nullifiers; +pub use ciphertexts::{EncryptedNote, WrappedNoteKey}; pub use nullifiers::Nullifier; /// A Note represents that a value is spendable by the recipient who @@ -27,18 +28,20 @@ pub use nullifiers::Nullifier; /// address. #[derive(Clone, Debug)] pub struct Note { - /// The diversifer of the recipient’s shielded payment address. + /// The _diversifer_ of the recipient’s _shielded payment address_. pub diversifier: Diversifier, - /// The diversified transmission key of the recipient’s shielded + /// The _diversified transmission key_ of the recipient’s shielded /// payment address. pub transmission_key: TransmissionKey, - /// An integer representing the value of the note in zatoshi. + /// An integer representing the value of the _note_ in zatoshi. pub value: Amount, - /// Used as input to PRF^nf as part of deriving the nullier of the note. - pub rho: pallas::Base, // TODO: refine type - /// Sender-controlled randomness. - pub psi: pallas::Base, // TODO: refine type - /// A random commitment trapdoor used to produce the associated - /// note commitment. + /// Used as input to PRF^nf as part of deriving the _nullifier_ of the _note_. + pub rho: pallas::Base, // XXX: refine type? + /// Additional randomness used in deriving the _nullifier_. + pub psi: pallas::Base, // XXX: refine type + /// A random _commitment trapdoor_ used to produce the associated note + /// commitment. pub rcm: CommitmentRandomness, + /// The note memo, after decryption. + pub memo: Memo, } diff --git a/zebra-chain/src/orchard/note/nullifiers.rs b/zebra-chain/src/orchard/note/nullifiers.rs index 584e1983e..45d7ee7a0 100644 --- a/zebra-chain/src/orchard/note/nullifiers.rs +++ b/zebra-chain/src/orchard/note/nullifiers.rs @@ -1,27 +1,13 @@ #![allow(clippy::unit_arg)] #![allow(dead_code)] -use group::GroupEncoding; use halo2::{arithmetic::FieldExt, pasta::pallas}; -use super::super::{ - commitment::NoteCommitment, keys::NullifierDerivingKey, sinsemilla::*, tree::Position, -}; +use crate::serialization::serde_helpers; -/// Orchard ixing Pedersen hash Function -/// -/// Used to compute ρ from a note commitment and its position in the note -/// commitment tree. It takes as input a Pedersen commitment P, and hashes it -/// with another input x. -/// -/// MixingPedersenHash(P, x) := P + [x]GroupHash^P^(r)(“Zcash_P_”, “”) -/// -/// https://zips.z.cash/protocol/protocol.pdf#concretemixinghash -// TODO: I'M EXTRAPOLATING HERE, DOUBLE CHECK THE SPEC WHEN FINALIZED -#[allow(non_snake_case)] -pub fn mixing_pedersen_hash(P: pallas::Point, x: pallas::Scalar) -> pallas::Point { - P + pallas_group_hash(b"Zcash_P_", b"") * x -} +use super::super::{ + commitment::NoteCommitment, keys::NullifierDerivingKey, note::Note, sinsemilla::*, +}; /// A cryptographic permutation, defined in [poseidonhash]. /// @@ -32,54 +18,71 @@ fn poseidon_hash(_x: pallas::Base, _y: pallas::Base) -> pallas::Base { unimplemented!() } -/// Derive the nullifier for a Orchard note. +/// Used as part of deriving the _nullifier_ for a Orchard _note_. +/// +/// PRF^nfOrchard: F_𝑞P × F_𝑞P → F_𝑞P /// /// Instantiated using the PoseidonHash hash function defined in [§5.4.1.10 -/// ‘PoseidonHash Function’][poseidon] +/// ‘PoseidonHash Function’][poseidon]: /// /// PRF^nfOrchard(nk*, ρ*) := PoseidonHash(nk*, ρ*) /// /// [concreteprfs]: https://zips.z.cash/protocol/protocol.pdf#concreteprfs /// [poseidonhash]: https://zips.z.cash/protocol/nu5.pdf#poseidonhash -fn prf_nf(nk_bytes: [u8; 32], rho_bytes: [u8; 32]) -> [u8; 32] { - poseidon_hash( - pallas::Base::from_bytes(&nk_bytes).unwrap(), - pallas::Base::from_bytes(&rho_bytes).unwrap(), - ) - .into() +fn prf_nf(nk: pallas::Base, rho: pallas::Base) -> pallas::Base { + poseidon_hash(nk, rho) } /// A Nullifier for Orchard transactions -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] +#[derive( + Clone, + Copy, + Debug, + Eq, // Hash, + PartialEq, + Serialize, + Deserialize, +)] #[cfg_attr( any(test, feature = "proptest-impl"), derive(proptest_derive::Arbitrary) )] -pub struct Nullifier(pub [u8; 32]); +pub struct Nullifier(#[serde(with = "serde_helpers::Base")] pallas::Base); impl From<[u8; 32]> for Nullifier { - fn from(buf: [u8; 32]) -> Self { - Self(buf) + fn from(bytes: [u8; 32]) -> Self { + Self(pallas::Base::from_bytes(&bytes).unwrap()) } } -impl<'a> From<(NoteCommitment, Position, &'a NullifierDerivingKey)> for Nullifier { +impl From<(NullifierDerivingKey, Note, NoteCommitment)> for Nullifier { /// Derive a `Nullifier` for an Orchard _note_. /// - /// For a _note_, the _nullifier_ is derived as PRF^nfOrchard_nk(ρ*), where - /// k is a representation of the _nullifier deriving key_ associated with - /// the _note_ and ρ = repr_P(ρ). + /// nk is the _nullifier deriving key_ associated with the _note_; ρ and ψ + /// are part of the _note_; and cm is the _note commitment_. + /// + /// DeriveNullifier_nk(ρ, ψ, cm) = Extract_P(︀ [︀ (PRF^nfOrchard_nk(ρ) + ψ) mod q_P ]︀ K^Orchard + cm)︀ /// /// https://zips.z.cash/protocol/nu5.pdf#commitmentsandnullifiers - fn from((cm, pos, nk): (NoteCommitment, Position, &'a NullifierDerivingKey)) -> Self { - let rho = pallas::Affine::from(mixing_pedersen_hash(cm.0.into(), pos.0.into())); + #[allow(non_snake_case)] + fn from((nk, note, cm): (NullifierDerivingKey, Note, NoteCommitment)) -> Self { + // TODO: fill this in (K^Orchard) from the spec when defined: + // https://zips.z.cash/protocol/nu5.pdf#commitmentsandnullifiers + let K = pallas_group_hash(b"Zcash_P_", b""); - Nullifier(prf_nf(nk.into(), rho.to_bytes())) + // impl Add for pallas::Base reduces by the modulus (q_P) + // + // [︀ (PRF^nfOrchard_nk(ρ) + ψ) mod q_P ]︀ K^Orchard + cm + let scalar = + pallas::Scalar::from_bytes(&(prf_nf(nk.0, note.rho) + note.psi).to_bytes()).unwrap(); + + // Basically a new-gen Pedersen hash? + Nullifier(extract_p((K * scalar) + cm.0)) } } impl From for [u8; 32] { fn from(n: Nullifier) -> Self { - n.0 + n.0.into() } } diff --git a/zebra-chain/src/sapling/note/nullifiers.rs b/zebra-chain/src/sapling/note/nullifiers.rs index a3b8febd6..443e02e66 100644 --- a/zebra-chain/src/sapling/note/nullifiers.rs +++ b/zebra-chain/src/sapling/note/nullifiers.rs @@ -31,7 +31,7 @@ fn prf_nf(nk: [u8; 32], rho: [u8; 32]) -> [u8; 32] { any(test, feature = "proptest-impl"), derive(proptest_derive::Arbitrary) )] -pub struct Nullifier(pub [u8; 32]); +pub struct Nullifier([u8; 32]); impl From<[u8; 32]> for Nullifier { fn from(buf: [u8; 32]) -> Self {