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), _) if vk == expected_fvk.vk => Ok(vk),
40 (Some(_), Some(vk), Some(value)) if value.inner() == 0 => Ok(vk),
42 (Some(_), Some(_), _) => Err(VerifyError::MismatchedFullViewingKey),
43 (Some(expected_fvk), None, _) => Ok(expected_fvk.vk.clone()),
44 (None, Some(vk), _) => Ok(vk),
45 (None, None, _) => Err(VerifyError::MissingProofGenerationKey),
46 }
47 }
48
49 pub fn verify_nullifier(
64 &self,
65 expected_fvk: Option<&FullViewingKey>,
66 ) -> Result<(), VerifyError> {
67 let vk = self.vk_for_validation(expected_fvk)?;
68
69 let note = Note::from_parts(
70 self.recipient.ok_or(VerifyError::MissingRecipient)?,
71 self.value.ok_or(VerifyError::MissingValue)?,
72 self.rseed.ok_or(VerifyError::MissingRandomSeed)?,
73 );
74
75 if vk.to_payment_address(*note.recipient().diversifier()) != Some(note.recipient()) {
79 return Err(VerifyError::WrongFvkForNote);
80 }
81
82 let merkle_path = self.witness().as_ref().ok_or(VerifyError::MissingWitness)?;
83
84 if note.nf(vk.nk(), merkle_path.position().into()) == self.nullifier {
85 Ok(())
86 } else {
87 Err(VerifyError::InvalidNullifier)
88 }
89 }
90
91 pub fn verify_rk(&self, expected_fvk: Option<&FullViewingKey>) -> Result<(), VerifyError> {
100 let vk = self.vk_for_validation(expected_fvk)?;
101
102 let alpha = self
103 .alpha
104 .as_ref()
105 .ok_or(VerifyError::MissingSpendAuthRandomizer)?;
106
107 if vk.ak().randomize(alpha) == self.rk {
108 Ok(())
109 } else {
110 Err(VerifyError::InvalidRandomizedVerificationKey)
111 }
112 }
113}
114
115impl super::Output {
116 pub fn verify_cv(&self) -> Result<(), VerifyError> {
122 let value = self.value.ok_or(VerifyError::MissingValue)?;
123 let rcv = self
124 .rcv
125 .clone()
126 .ok_or(VerifyError::MissingValueCommitTrapdoor)?;
127
128 let cv_net = ValueCommitment::derive(value, rcv);
129 if cv_net.to_bytes() == self.cv.to_bytes() {
130 Ok(())
131 } else {
132 Err(VerifyError::InvalidValueCommitment)
133 }
134 }
135
136 pub fn verify_note_commitment(&self) -> Result<(), VerifyError> {
143 let note = Note::from_parts(
144 self.recipient.ok_or(VerifyError::MissingRecipient)?,
145 self.value.ok_or(VerifyError::MissingValue)?,
146 crate::Rseed::AfterZip212(self.rseed.ok_or(VerifyError::MissingRandomSeed)?),
147 );
148
149 if note.cmu() == self.cmu {
150 Ok(())
151 } else {
152 Err(VerifyError::InvalidExtractedNoteCommitment)
153 }
154 }
155}
156
157#[derive(Debug)]
159#[non_exhaustive]
160pub enum VerifyError {
161 InvalidExtractedNoteCommitment,
163 InvalidNullifier,
165 InvalidRandomizedVerificationKey,
167 InvalidValueCommitment,
169 MismatchedFullViewingKey,
171 MissingProofGenerationKey,
173 MissingRandomSeed,
175 MissingRecipient,
177 MissingSpendAuthRandomizer,
179 MissingValue,
181 MissingValueCommitTrapdoor,
183 MissingWitness,
185 WrongFvkForNote,
187}
188
189impl fmt::Display for VerifyError {
190 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191 match self {
192 VerifyError::InvalidExtractedNoteCommitment => {
193 write!(f, "output note doesn't match `cmu`")
194 }
195 VerifyError::InvalidNullifier => write!(f, "spent note doesn't match `nullifier`"),
196 VerifyError::InvalidRandomizedVerificationKey => {
197 write!(f, "spend's `fvk` and `alpha` do not match `rk`")
198 }
199 VerifyError::InvalidValueCommitment => {
200 write!(f, "`cv` doesn't match the note value and `rcv`")
201 }
202 VerifyError::MismatchedFullViewingKey => {
203 write!(
204 f,
205 "Provided full viewing key doesn't match the `proof_generation_key` field"
206 )
207 }
208 VerifyError::MissingProofGenerationKey => {
209 write!(f, "`proof_generation_key` missing for dummy note")
210 }
211 VerifyError::MissingRandomSeed => {
212 write!(f, "`rseed` missing for `nullifier` verification")
213 }
214 VerifyError::MissingRecipient => {
215 write!(f, "`recipient` missing for `nullifier` verification")
216 }
217 VerifyError::MissingSpendAuthRandomizer => {
218 write!(f, "`alpha` missing for `rk` verification")
219 }
220 VerifyError::MissingValue => write!(f, "`value` missing"),
221 VerifyError::MissingValueCommitTrapdoor => {
222 write!(f, "`rcv` missing for `cv` verification")
223 }
224 VerifyError::MissingWitness => {
225 write!(f, "`witness` missing for `nullifier` verification")
226 }
227 VerifyError::WrongFvkForNote => write!(f, "`fvk` does not own the spent note"),
228 }
229 }
230}
231
232#[cfg(feature = "std")]
233impl std::error::Error for VerifyError {}