1use group::{ff::Field, GroupEncoding};
2use rand_core::{CryptoRng, Rng};
3use zcash_spec::PrfExpand;
4
5use crate::{
6 keys::{ExpandedSpendingKey, FullViewingKey},
7 zip32::ExtendedSpendingKey,
8};
9
10use super::{
11 keys::EphemeralSecretKey, value::NoteValue, Nullifier, NullifierDerivingKey, PaymentAddress,
12};
13
14mod commitment;
15pub use self::commitment::{ExtractedNoteCommitment, NoteCommitment};
16
17pub(super) mod nullifier;
18
19#[derive(Copy, Clone, Debug, PartialEq, Eq)]
25pub enum Rseed {
26 BeforeZip212(jubjub::Fr),
27 AfterZip212([u8; 32]),
28}
29
30impl Rseed {
31 pub(crate) fn rcm(&self) -> commitment::NoteCommitTrapdoor {
35 commitment::NoteCommitTrapdoor(match self {
36 Rseed::BeforeZip212(rcm) => *rcm,
37 Rseed::AfterZip212(rseed) => {
38 jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_RCM.with(rseed))
39 }
40 })
41 }
42}
43
44#[derive(Clone, Debug)]
46pub struct Note {
47 recipient: PaymentAddress,
49 value: NoteValue,
51 rseed: Rseed,
53}
54
55impl PartialEq for Note {
56 fn eq(&self, other: &Self) -> bool {
57 self.cmu().eq(&other.cmu())
59 }
60}
61
62impl Eq for Note {}
63
64impl Note {
65 pub fn from_parts(recipient: PaymentAddress, value: NoteValue, rseed: Rseed) -> Self {
78 Note {
79 recipient,
80 value,
81 rseed,
82 }
83 }
84
85 pub fn recipient(&self) -> PaymentAddress {
87 self.recipient
88 }
89
90 pub fn value(&self) -> NoteValue {
92 self.value
93 }
94
95 pub fn rseed(&self) -> &Rseed {
97 &self.rseed
98 }
99
100 fn cm_full_point(&self) -> NoteCommitment {
102 NoteCommitment::derive(
103 self.recipient.g_d().to_bytes(),
104 self.recipient.pk_d().to_bytes(),
105 self.value,
106 self.rseed.rcm(),
107 )
108 }
109
110 pub fn nf(&self, nk: &NullifierDerivingKey, position: u64) -> Nullifier {
113 Nullifier::derive(nk, self.cm_full_point(), position)
114 }
115
116 pub fn cmu(&self) -> ExtractedNoteCommitment {
118 self.cm_full_point().into()
119 }
120
121 pub fn rcm(&self) -> jubjub::Fr {
125 self.rseed.rcm().0
126 }
127
128 pub fn generate_or_derive_esk<R: Rng + CryptoRng>(&self, rng: &mut R) -> EphemeralSecretKey {
131 self.generate_or_derive_esk_internal(rng)
132 }
133
134 pub(crate) fn generate_or_derive_esk_internal<R: Rng>(
135 &self,
136 rng: &mut R,
137 ) -> EphemeralSecretKey {
138 match self.derive_esk() {
139 None => EphemeralSecretKey(jubjub::Fr::random(rng)),
140 Some(esk) => esk,
141 }
142 }
143
144 pub(crate) fn derive_esk(&self) -> Option<EphemeralSecretKey> {
146 match self.rseed {
147 Rseed::BeforeZip212(_) => None,
148 Rseed::AfterZip212(rseed) => Some(EphemeralSecretKey(jubjub::Fr::from_bytes_wide(
149 &PrfExpand::SAPLING_ESK.with(&rseed),
150 ))),
151 }
152 }
153
154 pub(crate) fn dummy<R: Rng>(mut rng: R) -> (ExpandedSpendingKey, FullViewingKey, Self) {
160 let extsk = loop {
162 let mut sk_bytes = [0; 32];
163 rng.fill_bytes(&mut sk_bytes);
164 if let Some(extsk) = ExtendedSpendingKey::master(&sk_bytes[..]) {
165 break extsk;
166 }
167 };
168 let fvk = extsk.to_diversifiable_full_viewing_key().fvk().clone();
169 let recipient = extsk.default_address();
170
171 let mut rseed_bytes = [0; 32];
172 rng.fill_bytes(&mut rseed_bytes);
173 let rseed = Rseed::AfterZip212(rseed_bytes);
174
175 let note = Note::from_parts(recipient.1, NoteValue::ZERO, rseed);
176
177 (extsk.expsk().clone(), fvk, note)
178 }
179}
180
181#[cfg(any(test, feature = "test-dependencies"))]
182pub(super) mod testing {
183 use proptest::{collection::vec, prelude::*};
184
185 use super::{
186 super::{testing::arb_payment_address, value::NoteValue},
187 ExtractedNoteCommitment, Note, Rseed,
188 };
189
190 prop_compose! {
191 pub fn arb_note(value: NoteValue)(
192 recipient in arb_payment_address(),
193 rseed in prop::array::uniform32(prop::num::u8::ANY).prop_map(Rseed::AfterZip212)
194 ) -> Note {
195 Note {
196 recipient,
197 value,
198 rseed
199 }
200 }
201 }
202
203 prop_compose! {
204 pub(crate) fn arb_cmu()(
205 cmu in vec(any::<u8>(), 64)
206 .prop_map(|v| <[u8;64]>::try_from(v.as_slice()).unwrap())
207 .prop_map(|v| bls12_381::Scalar::from_bytes_wide(&v)),
208 ) -> ExtractedNoteCommitment {
209 ExtractedNoteCommitment(cmu)
210 }
211 }
212}