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_logo_url = "https://www.zfnd.org/images/zebra-icon.png")]
|
||||||
#![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_chain")]
|
#![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_chain")]
|
||||||
#![allow(clippy::try_err)]
|
#![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
|
// The actual lints we want to disable
|
||||||
#![allow(clippy::unnecessary_wraps)]
|
#![allow(clippy::unnecessary_wraps)]
|
||||||
// Disable some broken or unwanted clippy nightly lints
|
// Disable some broken or unwanted clippy nightly lints
|
||||||
|
|
|
@ -18,8 +18,8 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
keys::{Diversifier, TransmissionKey},
|
keys::{prf_expand, Diversifier, TransmissionKey},
|
||||||
note::Note,
|
note::{self, SeedRandomness},
|
||||||
sinsemilla::*,
|
sinsemilla::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -36,10 +36,22 @@ where
|
||||||
pallas::Scalar::from_bytes_wide(&bytes)
|
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)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct CommitmentRandomness(pallas::Scalar);
|
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.
|
/// Note commitments for the output notes.
|
||||||
#[derive(Clone, Copy, Deserialize, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||||
pub struct NoteCommitment(#[serde(with = "serde_helpers::Affine")] pub pallas::Affine);
|
pub struct NoteCommitment(#[serde(with = "serde_helpers::Affine")] pub pallas::Affine);
|
||||||
|
@ -106,7 +118,8 @@ impl NoteCommitment {
|
||||||
diversifier: Diversifier,
|
diversifier: Diversifier,
|
||||||
transmission_key: TransmissionKey,
|
transmission_key: TransmissionKey,
|
||||||
value: Amount<NonNegative>,
|
value: Amount<NonNegative>,
|
||||||
_note: Note,
|
rho: note::Rho,
|
||||||
|
psi: note::Psi,
|
||||||
) -> Option<(CommitmentRandomness, Self)>
|
) -> Option<(CommitmentRandomness, Self)>
|
||||||
where
|
where
|
||||||
T: RngCore + CryptoRng,
|
T: RngCore + CryptoRng,
|
||||||
|
@ -126,13 +139,17 @@ impl NoteCommitment {
|
||||||
return None;
|
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 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(&g_d_bytes[..]));
|
||||||
s.append(&mut BitVec::<Lsb0, u8>::from_slice(&pk_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(&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));
|
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
|
/// https://zips.z.cash/protocol/protocol.pdf#concreteprfs
|
||||||
// TODO: This is basically a duplicate of the one in our sapling module, its
|
// 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.
|
// 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()
|
let mut state = blake2b_simd::Params::new()
|
||||||
.hash_length(64)
|
.hash_length(64)
|
||||||
.personal(b"Zcash_ExpandSeed")
|
.personal(b"Zcash_ExpandSeed")
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
#![allow(clippy::unit_arg)]
|
#![allow(clippy::unit_arg)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use halo2::pasta::pallas;
|
use group::GroupEncoding;
|
||||||
|
use halo2::{arithmetic::FieldExt, pasta::pallas};
|
||||||
|
use rand_core::{CryptoRng, RngCore};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
amount::{Amount, NonNegative},
|
amount::{Amount, NonNegative},
|
||||||
|
@ -12,7 +14,8 @@ use crate::{
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
commitment::CommitmentRandomness,
|
commitment::CommitmentRandomness,
|
||||||
keys::{Diversifier, TransmissionKey},
|
keys::{prf_expand, Diversifier, TransmissionKey},
|
||||||
|
sinsemilla::extract_p,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(test, feature = "proptest-impl"))]
|
#[cfg(any(test, feature = "proptest-impl"))]
|
||||||
|
@ -23,6 +26,63 @@ mod nullifiers;
|
||||||
pub use ciphertexts::{EncryptedNote, WrappedNoteKey};
|
pub use ciphertexts::{EncryptedNote, WrappedNoteKey};
|
||||||
pub use nullifiers::Nullifier;
|
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
|
/// A Note represents that a value is spendable by the recipient who
|
||||||
/// holds the spending key corresponding to a given shielded payment
|
/// holds the spending key corresponding to a given shielded payment
|
||||||
/// address.
|
/// address.
|
||||||
|
@ -36,9 +96,9 @@ pub struct Note {
|
||||||
/// An integer representing the value of the _note_ in zatoshi.
|
/// An integer representing the value of the _note_ in zatoshi.
|
||||||
pub value: Amount<NonNegative>,
|
pub value: Amount<NonNegative>,
|
||||||
/// Used as input to PRF^nf as part of deriving the _nullifier_ of the _note_.
|
/// 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_.
|
/// 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
|
/// A random _commitment trapdoor_ used to produce the associated note
|
||||||
/// commitment.
|
/// commitment.
|
||||||
pub rcm: CommitmentRandomness,
|
pub rcm: CommitmentRandomness,
|
||||||
|
|
|
@ -69,7 +69,8 @@ impl From<(NullifierDerivingKey, Note, NoteCommitment)> for Nullifier {
|
||||||
//
|
//
|
||||||
// [︀ (PRF^nfOrchard_nk(ρ) + ψ) mod q_P ]︀ K^Orchard + cm
|
// [︀ (PRF^nfOrchard_nk(ρ) + ψ) mod q_P ]︀ K^Orchard + cm
|
||||||
let scalar =
|
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?
|
// Basically a new-gen Pedersen hash?
|
||||||
Nullifier(extract_p((K * scalar) + cm.0))
|
Nullifier(extract_p((K * scalar) + cm.0))
|
||||||
|
|
|
@ -37,7 +37,7 @@ pub enum NetworkUpgrade {
|
||||||
Heartwood,
|
Heartwood,
|
||||||
/// The Zcash protocol after the Canopy upgrade.
|
/// The Zcash protocol after the Canopy upgrade.
|
||||||
Canopy,
|
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
|
/// Note: Network Upgrade 5 includes the Orchard Shielded Protocol, and
|
||||||
/// other changes. The Nu5 code name has not been chosen yet.
|
/// 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(653_600), Blossom),
|
||||||
(block::Height(903_000), Heartwood),
|
(block::Height(903_000), Heartwood),
|
||||||
(block::Height(1_046_400), Canopy),
|
(block::Height(1_046_400), Canopy),
|
||||||
// TODO: Add NU5 mainnet activation height
|
// TODO: Add Nu5 mainnet activation height
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Testnet network upgrade activation heights.
|
/// 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(584_000), Blossom),
|
||||||
(block::Height(903_800), Heartwood),
|
(block::Height(903_800), Heartwood),
|
||||||
(block::Height(1_028_500), Canopy),
|
(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
|
/// The Consensus Branch Id, used to bind transactions and blocks to a
|
||||||
|
|
Loading…
Reference in New Issue