2021-12-07 10:02:03 -08:00
|
|
|
use group::{ff::PrimeField, Group};
|
2022-01-28 07:53:03 -08:00
|
|
|
use halo2_proofs::arithmetic::CurveExt;
|
2021-12-07 10:02:03 -08:00
|
|
|
use pasta_curves::pallas;
|
2021-04-14 21:14:34 -07:00
|
|
|
use rand::RngCore;
|
2021-04-21 08:57:48 -07:00
|
|
|
use subtle::CtOption;
|
2021-03-15 18:27:08 -07:00
|
|
|
|
|
|
|
use super::NoteCommitment;
|
|
|
|
use crate::{
|
|
|
|
keys::NullifierDerivingKey,
|
|
|
|
spec::{extract_p, mod_r_p},
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A unique nullifier for a note.
|
2022-02-23 17:29:07 -08:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
2021-04-27 06:49:49 -07:00
|
|
|
pub struct Nullifier(pub(crate) pallas::Base);
|
2021-03-15 18:27:08 -07:00
|
|
|
|
|
|
|
impl Nullifier {
|
2021-04-14 21:14:34 -07:00
|
|
|
/// Generates a dummy nullifier for use as $\rho$ in dummy spent notes.
|
|
|
|
///
|
|
|
|
/// Nullifiers are required by consensus to be unique. For dummy output notes, we get
|
|
|
|
/// this restriction as intended: the note's $\rho$ value is set to the nullifier of
|
|
|
|
/// the accompanying spent note within the action, which is constrained by consensus
|
|
|
|
/// to be unique. In the case of dummy spent notes, we get this restriction by
|
|
|
|
/// following the chain backwards: the nullifier of the dummy spent note will be
|
|
|
|
/// constrained by consensus to be unique, and the nullifier's uniqueness is derived
|
|
|
|
/// from the uniqueness of $\rho$.
|
|
|
|
///
|
|
|
|
/// Instead of explicitly sampling for a unique nullifier, we rely here on the size of
|
2021-09-27 19:52:16 -07:00
|
|
|
/// the base field to make the chance of sampling a colliding nullifier negligible.
|
2021-04-14 21:14:34 -07:00
|
|
|
pub(crate) fn dummy(rng: &mut impl RngCore) -> Self {
|
2021-04-22 05:54:17 -07:00
|
|
|
Nullifier(extract_p(&pallas::Point::random(rng)))
|
2021-04-14 21:14:34 -07:00
|
|
|
}
|
|
|
|
|
2021-04-21 08:57:48 -07:00
|
|
|
/// Deserialize the nullifier from a byte array.
|
|
|
|
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
|
2021-12-07 10:02:03 -08:00
|
|
|
pallas::Base::from_repr(*bytes).map(Nullifier)
|
2021-04-21 08:57:48 -07:00
|
|
|
}
|
|
|
|
|
2021-04-21 13:32:54 -07:00
|
|
|
/// Serialize the nullifier to its canonical byte representation.
|
2021-05-28 04:11:22 -07:00
|
|
|
pub fn to_bytes(self) -> [u8; 32] {
|
2021-12-07 10:02:03 -08:00
|
|
|
self.0.to_repr()
|
2021-04-21 13:32:54 -07:00
|
|
|
}
|
|
|
|
|
2021-03-15 18:27:08 -07:00
|
|
|
/// $DeriveNullifier$.
|
|
|
|
///
|
|
|
|
/// Defined in [Zcash Protocol Spec § 4.16: Note Commitments and Nullifiers][commitmentsandnullifiers].
|
|
|
|
///
|
|
|
|
/// [commitmentsandnullifiers]: https://zips.z.cash/protocol/nu5.pdf#commitmentsandnullifiers
|
|
|
|
pub(super) fn derive(
|
2021-03-29 17:52:20 -07:00
|
|
|
nk: &NullifierDerivingKey,
|
2021-03-15 18:27:08 -07:00
|
|
|
rho: pallas::Base,
|
|
|
|
psi: pallas::Base,
|
|
|
|
cm: NoteCommitment,
|
|
|
|
) -> Self {
|
|
|
|
let k = pallas::Point::hash_to_curve("z.cash:Orchard")(b"K");
|
|
|
|
|
|
|
|
Nullifier(extract_p(&(k * mod_r_p(nk.prf_nf(rho) + psi) + cm.0)))
|
|
|
|
}
|
|
|
|
}
|
2021-04-27 06:49:49 -07:00
|
|
|
|
|
|
|
/// Generators for property testing.
|
|
|
|
#[cfg(any(test, feature = "test-dependencies"))]
|
2021-12-17 14:08:58 -08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
|
2021-04-27 06:49:49 -07:00
|
|
|
pub mod testing {
|
2021-06-04 16:13:11 -07:00
|
|
|
use group::Group;
|
|
|
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
|
|
|
use proptest::collection::vec;
|
2021-04-27 06:49:49 -07:00
|
|
|
use proptest::prelude::*;
|
|
|
|
|
|
|
|
use super::Nullifier;
|
2021-05-05 12:39:41 -07:00
|
|
|
use crate::spec::extract_p;
|
2021-04-27 06:49:49 -07:00
|
|
|
|
|
|
|
prop_compose! {
|
|
|
|
/// Generate a uniformly distributed nullifier value.
|
2021-05-05 12:39:41 -07:00
|
|
|
pub fn arb_nullifier()(
|
2021-06-04 16:13:11 -07:00
|
|
|
bytes in vec(any::<u8>(), 64)
|
2021-05-05 12:39:41 -07:00
|
|
|
) -> Nullifier {
|
2021-06-04 16:13:11 -07:00
|
|
|
let point = pallas::Point::generator() * pallas::Scalar::from_bytes_wide(&<[u8; 64]>::try_from(bytes).unwrap());
|
|
|
|
Nullifier(extract_p(&point))
|
2021-04-27 06:49:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|