Flesh out Orchard note and nullifier derivation

This commit is contained in:
Deirdre Connolly 2021-03-27 20:31:08 -04:00 committed by Deirdre Connolly
parent 37c32e9c94
commit 376603d4c0
6 changed files with 94 additions and 18 deletions

View File

@ -7,8 +7,6 @@
#![doc(html_logo_url = "https://www.zfnd.org/images/zebra-icon.png")]
#![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_chain")]
#![allow(clippy::try_err)]
// Use the old lint name to build without warnings on stable until 1.51 is stable
#![allow(clippy::unknown_clippy_lints)]
// The actual lints we want to disable
#![allow(clippy::unnecessary_wraps)]
// Disable some broken or unwanted clippy nightly lints

View File

@ -18,8 +18,8 @@ use crate::{
};
use super::{
keys::{Diversifier, TransmissionKey},
note::Note,
keys::{prf_expand, Diversifier, TransmissionKey},
note::{self, SeedRandomness},
sinsemilla::*,
};
@ -36,10 +36,22 @@ where
pallas::Scalar::from_bytes_wide(&bytes)
}
/// The randomness used in the Simsemilla Hash for note commitment.
/// The randomness used in the Simsemilla hash for note commitment.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct CommitmentRandomness(pallas::Scalar);
impl From<SeedRandomness> for CommitmentRandomness {
/// rcm = ToScalar^Orchard((PRF^expand_rseed ([5]))
///
/// https://zips.z.cash/protocol/nu5.pdf#orchardsend
fn from(rseed: SeedRandomness) -> Self {
Self(pallas::Scalar::from_bytes_wide(&prf_expand(
rseed.0,
vec![&[5]],
)))
}
}
/// Note commitments for the output notes.
#[derive(Clone, Copy, Deserialize, PartialEq, Serialize)]
pub struct NoteCommitment(#[serde(with = "serde_helpers::Affine")] pub pallas::Affine);
@ -106,7 +118,8 @@ impl NoteCommitment {
diversifier: Diversifier,
transmission_key: TransmissionKey,
value: Amount<NonNegative>,
_note: Note,
rho: note::Rho,
psi: note::Psi,
) -> Option<(CommitmentRandomness, Self)>
where
T: RngCore + CryptoRng,
@ -126,13 +139,17 @@ impl NoteCommitment {
return None;
}
let pk_d_bytes = <[u8; 32]>::from(transmission_key);
let pk_d_bytes: [u8; 32] = transmission_key.into();
let v_bytes = value.to_bytes();
let rho_bytes: [u8; 32] = rho.into();
let psi_bytes: [u8; 32] = psi.into();
// g*d || pk*d || I2LEBSP64(v)
// g*d || pk*d || I2LEBSP_64(v) || I2LEBSP_l^Orchard_Base(ρ) || I2LEBSP_l^Orchard_base(ψ)
s.append(&mut BitVec::<Lsb0, u8>::from_slice(&g_d_bytes[..]));
s.append(&mut BitVec::<Lsb0, u8>::from_slice(&pk_d_bytes[..]));
s.append(&mut BitVec::<Lsb0, u8>::from_slice(&v_bytes[..]));
s.append(&mut BitVec::<Lsb0, u8>::from_slice(&rho_bytes[..]));
s.append(&mut BitVec::<Lsb0, u8>::from_slice(&psi_bytes[..]));
let rcm = CommitmentRandomness(generate_trapdoor(csprng));

View File

@ -63,9 +63,9 @@ fn prp_d(K: [u8; 32], d: [u8; 11]) -> [u8; 11] {
///
/// https://zips.z.cash/protocol/protocol.pdf#concreteprfs
// TODO: This is basically a duplicate of the one in our sapling module, its
// definition in the draft NU5 spec is incomplete so I'm putting it here in case
// definition in the draft Nu5 spec is incomplete so I'm putting it here in case
// it changes.
fn prf_expand(sk: [u8; 32], t: Vec<&[u8]>) -> [u8; 64] {
pub fn prf_expand(sk: [u8; 32], t: Vec<&[u8]>) -> [u8; 64] {
let mut state = blake2b_simd::Params::new()
.hash_length(64)
.personal(b"Zcash_ExpandSeed")

View File

@ -3,7 +3,9 @@
#![allow(clippy::unit_arg)]
#![allow(dead_code)]
use halo2::pasta::pallas;
use group::GroupEncoding;
use halo2::{arithmetic::FieldExt, pasta::pallas};
use rand_core::{CryptoRng, RngCore};
use crate::{
amount::{Amount, NonNegative},
@ -12,7 +14,8 @@ use crate::{
use super::{
commitment::CommitmentRandomness,
keys::{Diversifier, TransmissionKey},
keys::{prf_expand, Diversifier, TransmissionKey},
sinsemilla::extract_p,
};
#[cfg(any(test, feature = "proptest-impl"))]
@ -23,6 +26,63 @@ mod nullifiers;
pub use ciphertexts::{EncryptedNote, WrappedNoteKey};
pub use nullifiers::Nullifier;
#[derive(Clone, Debug)]
pub struct SeedRandomness(pub(crate) [u8; 32]);
/// Used as input to PRF^nf as part of deriving the _nullifier_ of the _note_.
///
/// When creating a new note from spending an old note, the new note's _rho_ is
/// the _nullifier_ of the previous note. If creating a note from scratch (like
/// a miner reward), a dummy note is constructed, and its nullifier as the _rho_
/// for the actual output note. When creating a dummy note, its _rho_ is chosen
/// as a random Pallas point's x-coordinate.
///
/// https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
#[derive(Clone, Debug)]
pub struct Rho(pub(crate) pallas::Base);
impl From<Rho> for [u8; 32] {
fn from(rho: Rho) -> Self {
rho.0.to_bytes()
}
}
impl Rho {
pub fn new<T>(csprng: &mut T) -> Self
where
T: RngCore + CryptoRng,
{
let mut bytes = [0u8; 32];
csprng.fill_bytes(&mut bytes);
Self(extract_p(pallas::Point::from_bytes(&bytes).unwrap()))
}
}
/// Additional randomness used in deriving the _nullifier_.
///
/// https://zips.z.cash/protocol/nu5.pdf#orchardsend
#[derive(Clone, Debug)]
pub struct Psi(pub(crate) pallas::Base);
impl From<Psi> for [u8; 32] {
fn from(psi: Psi) -> Self {
psi.0.to_bytes()
}
}
impl From<SeedRandomness> for Psi {
/// rcm = ToScalar^Orchard((PRF^expand_rseed ([9]))
///
/// https://zips.z.cash/protocol/nu5.pdf#orchardsend
fn from(rseed: SeedRandomness) -> Self {
Self(pallas::Base::from_bytes_wide(&prf_expand(
rseed.0,
vec![&[9]],
)))
}
}
/// A Note represents that a value is spendable by the recipient who
/// holds the spending key corresponding to a given shielded payment
/// address.
@ -36,9 +96,9 @@ pub struct Note {
/// An integer representing the value of the _note_ in zatoshi.
pub value: Amount<NonNegative>,
/// Used as input to PRF^nf as part of deriving the _nullifier_ of the _note_.
pub rho: pallas::Base, // XXX: refine type?
pub rho: Rho,
/// Additional randomness used in deriving the _nullifier_.
pub psi: pallas::Base, // XXX: refine type
pub psi: Psi,
/// A random _commitment trapdoor_ used to produce the associated note
/// commitment.
pub rcm: CommitmentRandomness,

View File

@ -69,7 +69,8 @@ impl From<(NullifierDerivingKey, Note, NoteCommitment)> for Nullifier {
//
// [ (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();
pallas::Scalar::from_bytes(&(prf_nf(nk.0, note.rho.0) + note.psi.0).to_bytes())
.unwrap();
// Basically a new-gen Pedersen hash?
Nullifier(extract_p((K * scalar) + cm.0))

View File

@ -37,7 +37,7 @@ pub enum NetworkUpgrade {
Heartwood,
/// The Zcash protocol after the Canopy upgrade.
Canopy,
/// The Zcash protocol after the NU5 upgrade.
/// The Zcash protocol after the Nu5 upgrade.
///
/// Note: Network Upgrade 5 includes the Orchard Shielded Protocol, and
/// other changes. The Nu5 code name has not been chosen yet.
@ -56,7 +56,7 @@ pub(crate) const MAINNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)]
(block::Height(653_600), Blossom),
(block::Height(903_000), Heartwood),
(block::Height(1_046_400), Canopy),
// TODO: Add NU5 mainnet activation height
// TODO: Add Nu5 mainnet activation height
];
/// Testnet network upgrade activation heights.
@ -71,7 +71,7 @@ pub(crate) const TESTNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)]
(block::Height(584_000), Blossom),
(block::Height(903_800), Heartwood),
(block::Height(1_028_500), Canopy),
// TODO: Add NU5 testnet activation height
// TODO: Add Nu5 testnet activation height
];
/// The Consensus Branch Id, used to bind transactions and blocks to a