1use group::{GroupEncoding, ff::Field};
2use rand_core::{CryptoRng, Rng};
3use zcash_spec::PrfExpand;
4
5use crate::{
6 keys::{ExpandedSpendingKey, FullViewingKey},
7 zip32::ExtendedSpendingKey,
8};
9
10use super::{
11 Nullifier, NullifierDerivingKey, PaymentAddress, keys::EphemeralSecretKey, value::NoteValue,
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 mut sk_bytes = [0; 32];
161 rng.fill_bytes(&mut sk_bytes);
162
163 let extsk = ExtendedSpendingKey::master(&sk_bytes[..]);
164 let fvk = extsk.to_diversifiable_full_viewing_key().fvk().clone();
165 let recipient = extsk.default_address();
166
167 let mut rseed_bytes = [0; 32];
168 rng.fill_bytes(&mut rseed_bytes);
169 let rseed = Rseed::AfterZip212(rseed_bytes);
170
171 let note = Note::from_parts(recipient.1, NoteValue::ZERO, rseed);
172
173 (extsk.expsk, fvk, note)
174 }
175}
176
177#[cfg(any(test, feature = "test-dependencies"))]
178pub(super) mod testing {
179 use proptest::{collection::vec, prelude::*};
180
181 use super::{
182 super::{testing::arb_payment_address, value::NoteValue},
183 ExtractedNoteCommitment, Note, Rseed,
184 };
185
186 prop_compose! {
187 pub fn arb_note(value: NoteValue)(
188 recipient in arb_payment_address(),
189 rseed in prop::array::uniform32(prop::num::u8::ANY).prop_map(Rseed::AfterZip212)
190 ) -> Note {
191 Note {
192 recipient,
193 value,
194 rseed
195 }
196 }
197 }
198
199 prop_compose! {
200 pub(crate) fn arb_cmu()(
201 cmu in vec(any::<u8>(), 64)
202 .prop_map(|v| <[u8;64]>::try_from(v.as_slice()).unwrap())
203 .prop_map(|v| bls12_381::Scalar::from_bytes_wide(&v)),
204 ) -> ExtractedNoteCommitment {
205 ExtractedNoteCommitment(cmu)
206 }
207 }
208}