sapling_crypto/pczt/
verify.rs1use core::fmt;
2
3use crate::{keys::FullViewingKey, value::ValueCommitment, Note, ViewingKey};
4
5impl super::Spend {
6 pub fn verify_cv(&self) -> Result<(), VerifyError> {
12 let value = self.value.ok_or(VerifyError::MissingValue)?;
13 let rcv = self
14 .rcv
15 .clone()
16 .ok_or(VerifyError::MissingValueCommitTrapdoor)?;
17
18 let cv_net = ValueCommitment::derive(value, rcv);
19 if cv_net.to_bytes() == self.cv.to_bytes() {
20 Ok(())
21 } else {
22 Err(VerifyError::InvalidValueCommitment)
23 }
24 }
25
26 fn vk_for_validation(
30 &self,
31 expected_fvk: Option<&FullViewingKey>,
32 ) -> Result<ViewingKey, VerifyError> {
33 let vk = self
34 .proof_generation_key
35 .as_ref()
36 .map(|proof_generation_key| proof_generation_key.to_viewing_key());
37
38 match (expected_fvk, vk, self.value.as_ref()) {
39 (Some(expected_fvk), Some(vk), _)
40 if vk.ak == expected_fvk.vk.ak && vk.nk == expected_fvk.vk.nk =>
41 {
42 Ok(vk)
43 }
44 (Some(_), Some(vk), Some(value)) if value.inner() == 0 => Ok(vk),
46 (Some(_), Some(_), _) => Err(VerifyError::MismatchedFullViewingKey),
47 (Some(expected_fvk), None, _) => Ok(expected_fvk.vk.clone()),
48 (None, Some(vk), _) => Ok(vk),
49 (None, None, _) => Err(VerifyError::MissingProofGenerationKey),
50 }
51 }
52
53 pub fn verify_nullifier(
68 &self,
69 expected_fvk: Option<&FullViewingKey>,
70 ) -> Result<(), VerifyError> {
71 let vk = self.vk_for_validation(expected_fvk)?;
72
73 let note = Note::from_parts(
74 self.recipient.ok_or(VerifyError::MissingRecipient)?,
75 self.value.ok_or(VerifyError::MissingValue)?,
76 self.rseed.ok_or(VerifyError::MissingRandomSeed)?,
77 );
78
79 if vk.to_payment_address(*note.recipient().diversifier()) != Some(note.recipient()) {
83 return Err(VerifyError::WrongFvkForNote);
84 }
85
86 let merkle_path = self.witness().as_ref().ok_or(VerifyError::MissingWitness)?;
87
88 if note.nf(&vk.nk, merkle_path.position().into()) == self.nullifier {
89 Ok(())
90 } else {
91 Err(VerifyError::InvalidNullifier)
92 }
93 }
94
95 pub fn verify_rk(&self, expected_fvk: Option<&FullViewingKey>) -> Result<(), VerifyError> {
104 let vk = self.vk_for_validation(expected_fvk)?;
105
106 let alpha = self
107 .alpha
108 .as_ref()
109 .ok_or(VerifyError::MissingSpendAuthRandomizer)?;
110
111 if vk.ak.randomize(alpha) == self.rk {
112 Ok(())
113 } else {
114 Err(VerifyError::InvalidRandomizedVerificationKey)
115 }
116 }
117}
118
119impl super::Output {
120 pub fn verify_cv(&self) -> Result<(), VerifyError> {
126 let value = self.value.ok_or(VerifyError::MissingValue)?;
127 let rcv = self
128 .rcv
129 .clone()
130 .ok_or(VerifyError::MissingValueCommitTrapdoor)?;
131
132 let cv_net = ValueCommitment::derive(value, rcv);
133 if cv_net.to_bytes() == self.cv.to_bytes() {
134 Ok(())
135 } else {
136 Err(VerifyError::InvalidValueCommitment)
137 }
138 }
139
140 pub fn verify_note_commitment(&self) -> Result<(), VerifyError> {
147 let note = Note::from_parts(
148 self.recipient.ok_or(VerifyError::MissingRecipient)?,
149 self.value.ok_or(VerifyError::MissingValue)?,
150 crate::Rseed::AfterZip212(self.rseed.ok_or(VerifyError::MissingRandomSeed)?),
151 );
152
153 if note.cmu() == self.cmu {
154 Ok(())
155 } else {
156 Err(VerifyError::InvalidExtractedNoteCommitment)
157 }
158 }
159}
160
161#[derive(Debug)]
163#[non_exhaustive]
164pub enum VerifyError {
165 InvalidExtractedNoteCommitment,
167 InvalidNullifier,
169 InvalidRandomizedVerificationKey,
171 InvalidValueCommitment,
173 MismatchedFullViewingKey,
175 MissingProofGenerationKey,
177 MissingRandomSeed,
179 MissingRecipient,
181 MissingSpendAuthRandomizer,
183 MissingValue,
185 MissingValueCommitTrapdoor,
187 MissingWitness,
189 WrongFvkForNote,
191}
192
193impl fmt::Display for VerifyError {
194 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195 match self {
196 VerifyError::InvalidExtractedNoteCommitment => {
197 write!(f, "output note doesn't match `cmu`")
198 }
199 VerifyError::InvalidNullifier => write!(f, "spent note doesn't match `nullifier`"),
200 VerifyError::InvalidRandomizedVerificationKey => {
201 write!(f, "spend's `fvk` and `alpha` do not match `rk`")
202 }
203 VerifyError::InvalidValueCommitment => {
204 write!(f, "`cv` doesn't match the note value and `rcv`")
205 }
206 VerifyError::MismatchedFullViewingKey => {
207 write!(
208 f,
209 "Provided full viewing key doesn't match the `proof_generation_key` field"
210 )
211 }
212 VerifyError::MissingProofGenerationKey => {
213 write!(f, "`proof_generation_key` missing for dummy note")
214 }
215 VerifyError::MissingRandomSeed => {
216 write!(f, "`rseed` missing for `nullifier` verification")
217 }
218 VerifyError::MissingRecipient => {
219 write!(f, "`recipient` missing for `nullifier` verification")
220 }
221 VerifyError::MissingSpendAuthRandomizer => {
222 write!(f, "`alpha` missing for `rk` verification")
223 }
224 VerifyError::MissingValue => write!(f, "`value` missing"),
225 VerifyError::MissingValueCommitTrapdoor => {
226 write!(f, "`rcv` missing for `cv` verification")
227 }
228 VerifyError::MissingWitness => {
229 write!(f, "`witness` missing for `nullifier` verification")
230 }
231 VerifyError::WrongFvkForNote => write!(f, "`fvk` does not own the spent note"),
232 }
233 }
234}
235
236#[cfg(feature = "std")]
237impl std::error::Error for VerifyError {}