orchard/note/
nullifier.rs1use group::{ff::PrimeField, Group};
2use memuse::DynamicUsage;
3use pasta_curves::{arithmetic::CurveExt, pallas};
4use rand::RngCore;
5use subtle::{ConstantTimeEq, CtOption};
6
7use super::NoteCommitment;
8use crate::{
9 keys::NullifierDerivingKey,
10 spec::{extract_p, mod_r_p},
11};
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
15pub struct Nullifier(pub pallas::Base);
16
17memuse::impl_no_dynamic_usage!(Nullifier);
19
20impl Nullifier {
21 pub(crate) fn dummy(rng: &mut impl RngCore) -> Self {
34 Nullifier(extract_p(&pallas::Point::random(rng)))
35 }
36
37 pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
39 pallas::Base::from_repr(*bytes).map(Nullifier)
40 }
41
42 pub fn to_bytes(self) -> [u8; 32] {
44 self.0.to_repr()
45 }
46
47 pub(super) fn derive(
53 nk: &NullifierDerivingKey,
54 rho: pallas::Base,
55 psi: pallas::Base,
56 cm: NoteCommitment,
57 ) -> Self {
58 let k = pallas::Point::hash_to_curve("z.cash:Orchard")(b"K");
59
60 Nullifier(extract_p(&(k * mod_r_p(nk.prf_nf(rho) + psi) + cm.0)))
61 }
62}
63
64impl ConstantTimeEq for Nullifier {
65 fn ct_eq(&self, other: &Self) -> subtle::Choice {
66 self.0.ct_eq(&other.0)
67 }
68}
69
70#[cfg(any(test, feature = "test-dependencies"))]
72#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
73pub mod testing {
74 use group::{ff::FromUniformBytes, Group};
75 use pasta_curves::pallas;
76 use proptest::collection::vec;
77 use proptest::prelude::*;
78
79 use super::Nullifier;
80 use crate::spec::extract_p;
81
82 prop_compose! {
83 pub fn arb_nullifier()(
85 bytes in vec(any::<u8>(), 64)
86 ) -> Nullifier {
87 let point = pallas::Point::generator() * pallas::Scalar::from_uniform_bytes(&<[u8; 64]>::try_from(bytes).unwrap());
88 Nullifier(extract_p(&point))
89 }
90 }
91}