Orchard: update nullifiers
This commit is contained in:
parent
c892b93f61
commit
5ede33b231
|
|
@ -11,7 +11,6 @@ use halo2::{
|
||||||
arithmetic::{CurveAffine, FieldExt},
|
arithmetic::{CurveAffine, FieldExt},
|
||||||
pasta::pallas,
|
pasta::pallas,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use rand_core::{CryptoRng, RngCore};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -23,6 +22,7 @@ use crate::{
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
keys::{Diversifier, TransmissionKey},
|
keys::{Diversifier, TransmissionKey},
|
||||||
|
note::Note,
|
||||||
sinsemilla::*,
|
sinsemilla::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -95,15 +95,21 @@ impl NoteCommitment {
|
||||||
/// before it is encrypted as part of an output of an _Action_. This is a
|
/// before it is encrypted as part of an output of an _Action_. This is a
|
||||||
/// higher level function that calls `NoteCommit^Orchard_rcm` internally.
|
/// 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, ρ, ψ) :=
|
/// NoteCommit^Orchard_rcm(repr_P(gd),repr_P(pkd), v, ρ, ψ) :=
|
||||||
///
|
///
|
||||||
/// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit
|
/// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
// TODO: update to handle rseed, psi, rho
|
||||||
pub fn new<T>(
|
pub fn new<T>(
|
||||||
csprng: &mut T,
|
csprng: &mut T,
|
||||||
diversifier: Diversifier,
|
diversifier: Diversifier,
|
||||||
transmission_key: TransmissionKey,
|
transmission_key: TransmissionKey,
|
||||||
value: Amount<NonNegative>,
|
value: Amount<NonNegative>,
|
||||||
|
note: Note,
|
||||||
) -> Option<(CommitmentRandomness, Self)>
|
) -> Option<(CommitmentRandomness, Self)>
|
||||||
where
|
where
|
||||||
T: RngCore + CryptoRng,
|
T: RngCore + CryptoRng,
|
||||||
|
|
@ -151,8 +157,8 @@ impl NoteCommitment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A homomorphic Pedersen commitment to the net value of a note, used in Action
|
/// A homomorphic Pedersen commitment to the net value of a _note_, used in
|
||||||
/// descriptions.
|
/// Action descriptions.
|
||||||
///
|
///
|
||||||
/// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit
|
/// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit
|
||||||
#[derive(Clone, Copy, Deserialize, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,22 @@
|
||||||
|
|
||||||
use halo2::pasta::pallas;
|
use halo2::pasta::pallas;
|
||||||
|
|
||||||
mod ciphertexts;
|
use crate::{
|
||||||
mod nullifiers;
|
amount::{Amount, NonNegative},
|
||||||
|
transaction::Memo,
|
||||||
#[cfg(any(test, feature = "proptest-impl"))]
|
};
|
||||||
mod arbitrary;
|
|
||||||
|
|
||||||
use crate::amount::{Amount, NonNegative};
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
commitment::CommitmentRandomness,
|
commitment::CommitmentRandomness,
|
||||||
keys::{Diversifier, TransmissionKey},
|
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;
|
pub use nullifiers::Nullifier;
|
||||||
|
|
||||||
/// A Note represents that a value is spendable by the recipient who
|
/// A Note represents that a value is spendable by the recipient who
|
||||||
|
|
@ -27,18 +28,20 @@ pub use nullifiers::Nullifier;
|
||||||
/// address.
|
/// address.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Note {
|
pub struct Note {
|
||||||
/// The diversifer of the recipient’s shielded payment address.
|
/// The _diversifer_ of the recipient’s _shielded payment address_.
|
||||||
pub diversifier: Diversifier,
|
pub diversifier: Diversifier,
|
||||||
/// The diversified transmission key of the recipient’s shielded
|
/// The _diversified transmission key_ of the recipient’s shielded
|
||||||
/// payment address.
|
/// payment address.
|
||||||
pub transmission_key: TransmissionKey,
|
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<NonNegative>,
|
pub value: Amount<NonNegative>,
|
||||||
/// Used as input to PRF^nf as part of deriving the nullier of the note.
|
/// Used as input to PRF^nf as part of deriving the _nullifier_ of the _note_.
|
||||||
pub rho: pallas::Base, // TODO: refine type
|
pub rho: pallas::Base, // XXX: refine type?
|
||||||
/// Sender-controlled randomness.
|
/// Additional randomness used in deriving the _nullifier_.
|
||||||
pub psi: pallas::Base, // TODO: refine type
|
pub psi: pallas::Base, // XXX: refine type
|
||||||
/// A random commitment trapdoor used to produce the associated
|
/// A random _commitment trapdoor_ used to produce the associated note
|
||||||
/// note commitment.
|
/// commitment.
|
||||||
pub rcm: CommitmentRandomness,
|
pub rcm: CommitmentRandomness,
|
||||||
|
/// The note memo, after decryption.
|
||||||
|
pub memo: Memo,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,13 @@
|
||||||
#![allow(clippy::unit_arg)]
|
#![allow(clippy::unit_arg)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use group::GroupEncoding;
|
|
||||||
use halo2::{arithmetic::FieldExt, pasta::pallas};
|
use halo2::{arithmetic::FieldExt, pasta::pallas};
|
||||||
|
|
||||||
use super::super::{
|
use crate::serialization::serde_helpers;
|
||||||
commitment::NoteCommitment, keys::NullifierDerivingKey, sinsemilla::*, tree::Position,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Orchard ixing Pedersen hash Function
|
use super::super::{
|
||||||
///
|
commitment::NoteCommitment, keys::NullifierDerivingKey, note::Note, sinsemilla::*,
|
||||||
/// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A cryptographic permutation, defined in [poseidonhash].
|
/// A cryptographic permutation, defined in [poseidonhash].
|
||||||
///
|
///
|
||||||
|
|
@ -32,54 +18,71 @@ fn poseidon_hash(_x: pallas::Base, _y: pallas::Base) -> pallas::Base {
|
||||||
unimplemented!()
|
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
|
/// Instantiated using the PoseidonHash hash function defined in [§5.4.1.10
|
||||||
/// ‘PoseidonHash Function’][poseidon]
|
/// ‘PoseidonHash Function’][poseidon]:
|
||||||
///
|
///
|
||||||
/// PRF^nfOrchard(nk*, ρ*) := PoseidonHash(nk*, ρ*)
|
/// PRF^nfOrchard(nk*, ρ*) := PoseidonHash(nk*, ρ*)
|
||||||
///
|
///
|
||||||
/// [concreteprfs]: https://zips.z.cash/protocol/protocol.pdf#concreteprfs
|
/// [concreteprfs]: https://zips.z.cash/protocol/protocol.pdf#concreteprfs
|
||||||
/// [poseidonhash]: https://zips.z.cash/protocol/nu5.pdf#poseidonhash
|
/// [poseidonhash]: https://zips.z.cash/protocol/nu5.pdf#poseidonhash
|
||||||
fn prf_nf(nk_bytes: [u8; 32], rho_bytes: [u8; 32]) -> [u8; 32] {
|
fn prf_nf(nk: pallas::Base, rho: pallas::Base) -> pallas::Base {
|
||||||
poseidon_hash(
|
poseidon_hash(nk, rho)
|
||||||
pallas::Base::from_bytes(&nk_bytes).unwrap(),
|
|
||||||
pallas::Base::from_bytes(&rho_bytes).unwrap(),
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Nullifier for Orchard transactions
|
/// 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(
|
#[cfg_attr(
|
||||||
any(test, feature = "proptest-impl"),
|
any(test, feature = "proptest-impl"),
|
||||||
derive(proptest_derive::Arbitrary)
|
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 {
|
impl From<[u8; 32]> for Nullifier {
|
||||||
fn from(buf: [u8; 32]) -> Self {
|
fn from(bytes: [u8; 32]) -> Self {
|
||||||
Self(buf)
|
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_.
|
/// Derive a `Nullifier` for an Orchard _note_.
|
||||||
///
|
///
|
||||||
/// For a _note_, the _nullifier_ is derived as PRF^nfOrchard_nk(ρ*), where
|
/// nk is the _nullifier deriving key_ associated with the _note_; ρ and ψ
|
||||||
/// k is a representation of the _nullifier deriving key_ associated with
|
/// are part of the _note_; and cm is the _note commitment_.
|
||||||
/// the _note_ and ρ = repr_P(ρ).
|
///
|
||||||
|
/// DeriveNullifier_nk(ρ, ψ, cm) = Extract_P(︀ [︀ (PRF^nfOrchard_nk(ρ) + ψ) mod q_P ]︀ K^Orchard + cm)︀
|
||||||
///
|
///
|
||||||
/// https://zips.z.cash/protocol/nu5.pdf#commitmentsandnullifiers
|
/// https://zips.z.cash/protocol/nu5.pdf#commitmentsandnullifiers
|
||||||
fn from((cm, pos, nk): (NoteCommitment, Position, &'a NullifierDerivingKey)) -> Self {
|
#[allow(non_snake_case)]
|
||||||
let rho = pallas::Affine::from(mixing_pedersen_hash(cm.0.into(), pos.0.into()));
|
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<Nullifier> for [u8; 32] {
|
impl From<Nullifier> for [u8; 32] {
|
||||||
fn from(n: Nullifier) -> Self {
|
fn from(n: Nullifier) -> Self {
|
||||||
n.0
|
n.0.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ fn prf_nf(nk: [u8; 32], rho: [u8; 32]) -> [u8; 32] {
|
||||||
any(test, feature = "proptest-impl"),
|
any(test, feature = "proptest-impl"),
|
||||||
derive(proptest_derive::Arbitrary)
|
derive(proptest_derive::Arbitrary)
|
||||||
)]
|
)]
|
||||||
pub struct Nullifier(pub [u8; 32]);
|
pub struct Nullifier([u8; 32]);
|
||||||
|
|
||||||
impl From<[u8; 32]> for Nullifier {
|
impl From<[u8; 32]> for Nullifier {
|
||||||
fn from(buf: [u8; 32]) -> Self {
|
fn from(buf: [u8; 32]) -> Self {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue