sapling_crypto/pczt/verify.rs
1use crate::{keys::FullViewingKey, value::ValueCommitment, Note, ViewingKey};
2
3impl super::Spend {
4 /// Verifies that the `cv` field is consistent with the note fields.
5 ///
6 /// Requires that the following optional fields are set:
7 /// - `value`
8 /// - `rcv`
9 pub fn verify_cv(&self) -> Result<(), VerifyError> {
10 let value = self.value.ok_or(VerifyError::MissingValue)?;
11 let rcv = self
12 .rcv
13 .clone()
14 .ok_or(VerifyError::MissingValueCommitTrapdoor)?;
15
16 let cv_net = ValueCommitment::derive(value, rcv);
17 if cv_net.to_bytes() == self.cv.to_bytes() {
18 Ok(())
19 } else {
20 Err(VerifyError::InvalidValueCommitment)
21 }
22 }
23
24 /// Returns the [`ViewingKey`] to use when validating this note.
25 ///
26 /// Handles dummy notes when the `value` field is set.
27 fn vk_for_validation(
28 &self,
29 expected_fvk: Option<&FullViewingKey>,
30 ) -> Result<ViewingKey, VerifyError> {
31 let vk = self
32 .proof_generation_key
33 .as_ref()
34 .map(|proof_generation_key| proof_generation_key.to_viewing_key());
35
36 match (expected_fvk, vk, self.value.as_ref()) {
37 (Some(expected_fvk), Some(vk), _)
38 if vk.ak == expected_fvk.vk.ak && vk.nk == expected_fvk.vk.nk =>
39 {
40 Ok(vk)
41 }
42 // `expected_fvk` is ignored if the spent note is a dummy note.
43 (Some(_), Some(vk), Some(value)) if value.inner() == 0 => Ok(vk),
44 (Some(_), Some(_), _) => Err(VerifyError::MismatchedFullViewingKey),
45 (Some(expected_fvk), None, _) => Ok(expected_fvk.vk.clone()),
46 (None, Some(vk), _) => Ok(vk),
47 (None, None, _) => Err(VerifyError::MissingProofGenerationKey),
48 }
49 }
50
51 /// Verifies that the `nullifier` field is consistent with the note fields.
52 ///
53 /// Requires that the following optional fields are set:
54 /// - `recipient`
55 /// - `value`
56 /// - `rseed`
57 /// - `witness`
58 ///
59 /// In addition, at least one of the `proof_generation_key` field or `expected_fvk`
60 /// must be provided.
61 ///
62 /// The provided [`FullViewingKey`] is ignored if the spent note is a dummy note.
63 /// Otherwise, it will be checked against the `proof_generation_key` field (if both
64 /// are set).
65 pub fn verify_nullifier(
66 &self,
67 expected_fvk: Option<&FullViewingKey>,
68 ) -> Result<(), VerifyError> {
69 let vk = self.vk_for_validation(expected_fvk)?;
70
71 let note = Note::from_parts(
72 self.recipient.ok_or(VerifyError::MissingRecipient)?,
73 self.value.ok_or(VerifyError::MissingValue)?,
74 self.rseed.ok_or(VerifyError::MissingRandomSeed)?,
75 );
76
77 // We need both the note and the VK to verify the nullifier; we have everything
78 // needed to also verify that the correct VK was provided (the nullifier check
79 // itself only constrains `nk` within the VK).
80 if vk.to_payment_address(*note.recipient().diversifier()) != Some(note.recipient()) {
81 return Err(VerifyError::WrongFvkForNote);
82 }
83
84 let merkle_path = self.witness().as_ref().ok_or(VerifyError::MissingWitness)?;
85
86 if note.nf(&vk.nk, merkle_path.position().into()) == self.nullifier {
87 Ok(())
88 } else {
89 Err(VerifyError::InvalidNullifier)
90 }
91 }
92
93 /// Verifies that the `rk` field is consistent with the given FVK.
94 ///
95 /// Requires that the following optional fields are set:
96 /// - `alpha`
97 ///
98 /// The provided [`FullViewingKey`] is ignored if the spent note is a dummy note
99 /// (which can only be determined if the `value` field is set). Otherwise, it will be
100 /// checked against the `proof_generation_key` field (if set).
101 pub fn verify_rk(&self, expected_fvk: Option<&FullViewingKey>) -> Result<(), VerifyError> {
102 let vk = self.vk_for_validation(expected_fvk)?;
103
104 let alpha = self
105 .alpha
106 .as_ref()
107 .ok_or(VerifyError::MissingSpendAuthRandomizer)?;
108
109 if vk.ak.randomize(alpha) == self.rk {
110 Ok(())
111 } else {
112 Err(VerifyError::InvalidRandomizedVerificationKey)
113 }
114 }
115}
116
117impl super::Output {
118 /// Verifies that the `cv` field is consistent with the note fields.
119 ///
120 /// Requires that the following optional fields are set:
121 /// - `value`
122 /// - `rcv`
123 pub fn verify_cv(&self) -> Result<(), VerifyError> {
124 let value = self.value.ok_or(VerifyError::MissingValue)?;
125 let rcv = self
126 .rcv
127 .clone()
128 .ok_or(VerifyError::MissingValueCommitTrapdoor)?;
129
130 let cv_net = ValueCommitment::derive(value, rcv);
131 if cv_net.to_bytes() == self.cv.to_bytes() {
132 Ok(())
133 } else {
134 Err(VerifyError::InvalidValueCommitment)
135 }
136 }
137
138 /// Verifies that the `cmu` field is consistent with the note fields.
139 ///
140 /// Requires that the following optional fields are set:
141 /// - `recipient`
142 /// - `value`
143 /// - `rseed`
144 pub fn verify_note_commitment(&self) -> Result<(), VerifyError> {
145 let note = Note::from_parts(
146 self.recipient.ok_or(VerifyError::MissingRecipient)?,
147 self.value.ok_or(VerifyError::MissingValue)?,
148 crate::Rseed::AfterZip212(self.rseed.ok_or(VerifyError::MissingRandomSeed)?),
149 );
150
151 if note.cmu() == self.cmu {
152 Ok(())
153 } else {
154 Err(VerifyError::InvalidExtractedNoteCommitment)
155 }
156 }
157}
158
159/// Errors that can occur while verifying a PCZT bundle.
160#[derive(Debug)]
161pub enum VerifyError {
162 /// The output note's components do not produce the expected `cmx`.
163 InvalidExtractedNoteCommitment,
164 /// The spent note's components do not produce the expected `nullifier`.
165 InvalidNullifier,
166 /// The Spend's FVK and `alpha` do not produce the expected `rk`.
167 InvalidRandomizedVerificationKey,
168 /// The action's `cv_net` does not match the provided note values and `rcv`.
169 InvalidValueCommitment,
170 /// The spend or output's `fvk` field does not match the provided FVK.
171 MismatchedFullViewingKey,
172 /// Dummy notes must have their `proof_generation_key` field set in order to be verified.
173 MissingProofGenerationKey,
174 /// `nullifier` verification requires `rseed` to be set.
175 MissingRandomSeed,
176 /// `nullifier` verification requires `recipient` to be set.
177 MissingRecipient,
178 /// `rk` verification requires `alpha` to be set.
179 MissingSpendAuthRandomizer,
180 /// Verification requires all `value` fields to be set.
181 MissingValue,
182 /// `cv_net` verification requires `rcv` to be set.
183 MissingValueCommitTrapdoor,
184 /// `nullifier` verification requires `witness` to be set.
185 MissingWitness,
186 /// The provided `fvk` does not own the spent note.
187 WrongFvkForNote,
188}