1use group::{ff::Field, GroupEncoding};
2use rand_core::{CryptoRng, RngCore};
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: RngCore + CryptoRng>(
131 &self,
132 rng: &mut R,
133 ) -> EphemeralSecretKey {
134 self.generate_or_derive_esk_internal(rng)
135 }
136
137 pub(crate) fn generate_or_derive_esk_internal<R: RngCore>(
138 &self,
139 rng: &mut R,
140 ) -> EphemeralSecretKey {
141 match self.derive_esk() {
142 None => EphemeralSecretKey(jubjub::Fr::random(rng)),
143 Some(esk) => esk,
144 }
145 }
146
147 pub(crate) fn derive_esk(&self) -> Option<EphemeralSecretKey> {
149 match self.rseed {
150 Rseed::BeforeZip212(_) => None,
151 Rseed::AfterZip212(rseed) => Some(EphemeralSecretKey(jubjub::Fr::from_bytes_wide(
152 &PrfExpand::SAPLING_ESK.with(&rseed),
153 ))),
154 }
155 }
156
157 pub(crate) fn dummy<R: RngCore>(mut rng: R) -> (ExpandedSpendingKey, FullViewingKey, Self) {
163 let mut sk_bytes = [0; 32];
164 rng.fill_bytes(&mut sk_bytes);
165
166 let extsk = ExtendedSpendingKey::master(&sk_bytes[..]);
167 let fvk = extsk.to_diversifiable_full_viewing_key().fvk().clone();
168 let recipient = extsk.default_address();
169
170 let mut rseed_bytes = [0; 32];
171 rng.fill_bytes(&mut rseed_bytes);
172 let rseed = Rseed::AfterZip212(rseed_bytes);
173
174 let note = Note::from_parts(recipient.1, NoteValue::ZERO, rseed);
175
176 (extsk.expsk, fvk, note)
177 }
178}
179
180#[cfg(any(test, feature = "test-dependencies"))]
181pub(super) mod testing {
182 use proptest::{collection::vec, prelude::*};
183
184 use super::{
185 super::{testing::arb_payment_address, value::NoteValue},
186 ExtractedNoteCommitment, Note, Rseed,
187 };
188
189 prop_compose! {
190 pub fn arb_note(value: NoteValue)(
191 recipient in arb_payment_address(),
192 rseed in prop::array::uniform32(prop::num::u8::ANY).prop_map(Rseed::AfterZip212)
193 ) -> Note {
194 Note {
195 recipient,
196 value,
197 rseed
198 }
199 }
200 }
201
202 prop_compose! {
203 pub(crate) fn arb_cmu()(
204 cmu in vec(any::<u8>(), 64)
205 .prop_map(|v| <[u8;64]>::try_from(v.as_slice()).unwrap())
206 .prop_map(|v| bls12_381::Scalar::from_bytes_wide(&v)),
207 ) -> ExtractedNoteCommitment {
208 ExtractedNoteCommitment(cmu)
209 }
210 }
211}