Flesh out Orchard note and nullifier derivation
This commit is contained in:
parent
37c32e9c94
commit
376603d4c0
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue