use group::{ff::PrimeField, Group};
use memuse::DynamicUsage;
use pasta_curves::{arithmetic::CurveExt, pallas};
use rand::RngCore;
use subtle::{ConstantTimeEq, CtOption};
use super::NoteCommitment;
use crate::{
keys::NullifierDerivingKey,
spec::{extract_p, mod_r_p},
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Nullifier(pub(crate) pallas::Base);
memuse::impl_no_dynamic_usage!(Nullifier);
impl Nullifier {
pub(crate) fn dummy(rng: &mut impl RngCore) -> Self {
Nullifier(extract_p(&pallas::Point::random(rng)))
}
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
pallas::Base::from_repr(*bytes).map(Nullifier)
}
pub fn to_bytes(self) -> [u8; 32] {
self.0.to_repr()
}
pub(super) fn derive(
nk: &NullifierDerivingKey,
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)))
}
}
impl ConstantTimeEq for Nullifier {
fn ct_eq(&self, other: &Self) -> subtle::Choice {
self.0.ct_eq(&other.0)
}
}
#[cfg(any(test, feature = "test-dependencies"))]
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
pub mod testing {
use group::{ff::FromUniformBytes, Group};
use pasta_curves::pallas;
use proptest::collection::vec;
use proptest::prelude::*;
use super::Nullifier;
use crate::spec::extract_p;
prop_compose! {
pub fn arb_nullifier()(
bytes in vec(any::<u8>(), 64)
) -> Nullifier {
let point = pallas::Point::generator() * pallas::Scalar::from_uniform_bytes(&<[u8; 64]>::try_from(bytes).unwrap());
Nullifier(extract_p(&point))
}
}
}