1use groth16_solana::{
2 decompression::{decompress_g1, decompress_g2},
3 groth16::{Groth16Verifier, Groth16Verifyingkey},
4};
5use thiserror::Error;
6
7use crate::verifying_keys::*;
8
9pub mod verifying_keys;
10#[derive(Debug, Error, PartialEq)]
11pub enum VerifierError {
12 #[error("PublicInputsTryIntoFailed")]
13 PublicInputsTryIntoFailed,
14 #[error("DecompressG1Failed")]
15 DecompressG1Failed,
16 #[error("DecompressG2Failed")]
17 DecompressG2Failed,
18 #[error("InvalidPublicInputsLength")]
19 InvalidPublicInputsLength,
20 #[error("CreateGroth16VerifierFailed")]
21 CreateGroth16VerifierFailed,
22 #[error("ProofVerificationFailed")]
23 ProofVerificationFailed,
24 #[error("InvalidBatchSize supported batch sizes are 1, 10, 100, 500, 1000")]
25 InvalidBatchSize,
26}
27
28impl From<VerifierError> for u32 {
29 fn from(e: VerifierError) -> u32 {
30 match e {
31 PublicInputsTryIntoFailed => 13001,
32 DecompressG1Failed => 13002,
33 DecompressG2Failed => 13003,
34 InvalidPublicInputsLength => 13004,
35 CreateGroth16VerifierFailed => 13005,
36 ProofVerificationFailed => 13006,
37 InvalidBatchSize => 13007,
38 }
39 }
40}
41
42#[cfg(feature = "solana")]
43impl From<VerifierError> for solana_program_error::ProgramError {
44 fn from(e: VerifierError) -> Self {
45 solana_program_error::ProgramError::Custom(e.into())
46 }
47}
48
49#[cfg(feature = "pinocchio")]
50impl From<VerifierError> for pinocchio::program_error::ProgramError {
51 fn from(e: VerifierError) -> Self {
52 pinocchio::program_error::ProgramError::Custom(e.into())
53 }
54}
55
56pub use light_compressed_account::instruction_data::compressed_proof::CompressedProof;
57use VerifierError::*;
58
59pub fn verify_create_addresses_proof(
60 address_roots: &[[u8; 32]],
61 addresses: &[[u8; 32]],
62 compressed_proof: &CompressedProof,
63) -> Result<(), VerifierError> {
64 let public_inputs = [address_roots, addresses].concat();
65 match addresses.len() {
66 1 => verify::<2>(
67 &public_inputs
68 .try_into()
69 .map_err(|_| PublicInputsTryIntoFailed)?,
70 compressed_proof,
71 &v1_non_inclusion_26_1::VERIFYINGKEY,
72 ),
73 2 => verify::<4>(
74 &public_inputs
75 .try_into()
76 .map_err(|_| PublicInputsTryIntoFailed)?,
77 compressed_proof,
78 &v1_non_inclusion_26_2::VERIFYINGKEY,
79 ),
80 3 => verify::<6>(
81 &public_inputs
82 .try_into()
83 .map_err(|_| PublicInputsTryIntoFailed)?,
84 compressed_proof,
85 &v1_non_inclusion_26_3::VERIFYINGKEY,
86 ),
87 4 => verify::<8>(
88 &public_inputs
89 .try_into()
90 .map_err(|_| PublicInputsTryIntoFailed)?,
91 compressed_proof,
92 &v1_non_inclusion_26_4::VERIFYINGKEY,
93 ),
94 8 => verify::<16>(
95 &public_inputs
96 .try_into()
97 .map_err(|_| PublicInputsTryIntoFailed)?,
98 compressed_proof,
99 &v1_non_inclusion_26_8::VERIFYINGKEY,
100 ),
101 _ => Err(InvalidPublicInputsLength),
102 }
103}
104
105#[inline(never)]
106pub fn verify_create_addresses_and_inclusion_proof(
107 roots: &[[u8; 32]],
108 leaves: &[[u8; 32]],
109 address_roots: &[[u8; 32]],
110 addresses: &[[u8; 32]],
111 compressed_proof: &CompressedProof,
112) -> Result<(), VerifierError> {
113 let public_inputs = [roots, leaves, address_roots, addresses].concat();
114 match public_inputs.len() {
124 4 => verify::<4>(
125 &public_inputs
126 .try_into()
127 .map_err(|_| PublicInputsTryIntoFailed)?,
128 compressed_proof,
129 &v1_combined_26_26_1_1::VERIFYINGKEY,
130 ),
131 6 => {
132 let verifying_key = if address_roots.len() == 1 {
133 &v1_combined_26_26_2_1::VERIFYINGKEY
134 } else {
135 &v1_combined_26_26_1_2::VERIFYINGKEY
136 };
137 verify::<6>(
138 &public_inputs
139 .try_into()
140 .map_err(|_| PublicInputsTryIntoFailed)?,
141 compressed_proof,
142 verifying_key,
143 )
144 }
145 8 => {
146 let verifying_key = if address_roots.len() == 1 {
147 &v1_combined_26_26_3_1::VERIFYINGKEY
148 } else {
149 &v1_combined_26_26_2_2::VERIFYINGKEY
150 };
151 verify::<8>(
152 &public_inputs
153 .try_into()
154 .map_err(|_| PublicInputsTryIntoFailed)?,
155 compressed_proof,
156 verifying_key,
157 )
158 }
159 10 => {
160 let verifying_key = if address_roots.len() == 1 {
161 &v1_combined_26_26_4_1::VERIFYINGKEY
162 } else {
163 &v1_combined_26_26_3_2::VERIFYINGKEY
164 };
165 verify::<10>(
166 &public_inputs
167 .try_into()
168 .map_err(|_| PublicInputsTryIntoFailed)?,
169 compressed_proof,
170 verifying_key,
171 )
172 }
173 12 => verify::<12>(
174 &public_inputs
175 .try_into()
176 .map_err(|_| PublicInputsTryIntoFailed)?,
177 compressed_proof,
178 &v1_combined_26_26_4_2::VERIFYINGKEY,
179 ),
180 _ => Err(InvalidPublicInputsLength),
181 }
182}
183
184#[inline(never)]
185pub fn verify_inclusion_proof(
186 roots: &[[u8; 32]],
187 leaves: &[[u8; 32]],
188 compressed_proof: &CompressedProof,
189) -> Result<(), VerifierError> {
190 let public_inputs = [roots, leaves].concat();
191
192 match public_inputs.len() {
199 2 => verify::<2>(
200 &public_inputs
201 .try_into()
202 .map_err(|_| PublicInputsTryIntoFailed)?,
203 compressed_proof,
204 &v1_inclusion_26_1::VERIFYINGKEY,
205 ),
206 4 => verify::<4>(
207 &public_inputs
208 .try_into()
209 .map_err(|_| PublicInputsTryIntoFailed)?,
210 compressed_proof,
211 &v1_inclusion_26_2::VERIFYINGKEY,
212 ),
213 6 => verify::<6>(
214 &public_inputs
215 .try_into()
216 .map_err(|_| PublicInputsTryIntoFailed)?,
217 compressed_proof,
218 &v1_inclusion_26_3::VERIFYINGKEY,
219 ),
220 8 => verify::<8>(
221 &public_inputs
222 .try_into()
223 .map_err(|_| PublicInputsTryIntoFailed)?,
224 compressed_proof,
225 &v1_inclusion_26_4::VERIFYINGKEY,
226 ),
227 16 => verify::<16>(
228 &public_inputs
229 .try_into()
230 .map_err(|_| PublicInputsTryIntoFailed)?,
231 compressed_proof,
232 &v1_inclusion_26_8::VERIFYINGKEY,
233 ),
234 _ => Err(InvalidPublicInputsLength),
235 }
236}
237
238pub fn select_verifying_key<'a>(
239 num_leaves: usize,
240 num_addresses: usize,
241) -> Result<&'a Groth16Verifyingkey<'static>, VerifierError> {
242 #[cfg(all(feature = "solana", target_os = "solana"))]
243 solana_msg::msg!(
244 "select_verifying_key num_leaves: {}, num_addresses: {}",
245 num_leaves,
246 num_addresses
247 );
248 match (num_leaves, num_addresses) {
249 (1, 1) => Ok(&v2_combined_32_40_1_1::VERIFYINGKEY),
251 (1, 2) => Ok(&v2_combined_32_40_1_2::VERIFYINGKEY),
252 (1, 3) => Ok(&v2_combined_32_40_1_3::VERIFYINGKEY),
253 (1, 4) => Ok(&v2_combined_32_40_1_4::VERIFYINGKEY),
254 (2, 1) => Ok(&v2_combined_32_40_2_1::VERIFYINGKEY),
255 (2, 2) => Ok(&v2_combined_32_40_2_2::VERIFYINGKEY),
256 (2, 3) => Ok(&v2_combined_32_40_2_3::VERIFYINGKEY),
257 (2, 4) => Ok(&v2_combined_32_40_2_4::VERIFYINGKEY),
258 (3, 1) => Ok(&v2_combined_32_40_3_1::VERIFYINGKEY),
259 (3, 2) => Ok(&v2_combined_32_40_3_2::VERIFYINGKEY),
260 (3, 3) => Ok(&v2_combined_32_40_3_3::VERIFYINGKEY),
261 (3, 4) => Ok(&v2_combined_32_40_3_4::VERIFYINGKEY),
262 (4, 1) => Ok(&v2_combined_32_40_4_1::VERIFYINGKEY),
263 (4, 2) => Ok(&v2_combined_32_40_4_2::VERIFYINGKEY),
264 (4, 3) => Ok(&v2_combined_32_40_4_3::VERIFYINGKEY),
265 (4, 4) => Ok(&v2_combined_32_40_4_4::VERIFYINGKEY),
266
267 (1, _) => Ok(&v2_inclusion_32_1::VERIFYINGKEY),
269 (2, _) => Ok(&v2_inclusion_32_2::VERIFYINGKEY),
270 (3, _) => Ok(&v2_inclusion_32_3::VERIFYINGKEY),
271 (4, _) => Ok(&v2_inclusion_32_4::VERIFYINGKEY),
272 (5, _) => Ok(&v2_inclusion_32_5::VERIFYINGKEY),
273 (6, _) => Ok(&v2_inclusion_32_6::VERIFYINGKEY),
274 (7, _) => Ok(&v2_inclusion_32_7::VERIFYINGKEY),
275 (8, _) => Ok(&v2_inclusion_32_8::VERIFYINGKEY),
276 (9, _) => Ok(&v2_inclusion_32_9::VERIFYINGKEY),
277 (10, _) => Ok(&v2_inclusion_32_10::VERIFYINGKEY),
278 (11, _) => Ok(&v2_inclusion_32_11::VERIFYINGKEY),
279 (12, _) => Ok(&v2_inclusion_32_12::VERIFYINGKEY),
280 (13, _) => Ok(&v2_inclusion_32_13::VERIFYINGKEY),
281 (14, _) => Ok(&v2_inclusion_32_14::VERIFYINGKEY),
282 (15, _) => Ok(&v2_inclusion_32_15::VERIFYINGKEY),
283 (16, _) => Ok(&v2_inclusion_32_16::VERIFYINGKEY),
284 (17, _) => Ok(&v2_inclusion_32_17::VERIFYINGKEY),
285 (18, _) => Ok(&v2_inclusion_32_18::VERIFYINGKEY),
286 (19, _) => Ok(&v2_inclusion_32_19::VERIFYINGKEY),
287 (20, _) => Ok(&v2_inclusion_32_20::VERIFYINGKEY),
288
289 (_, 1) => Ok(&v2_non_inclusion_40_1::VERIFYINGKEY),
291 (_, 2) => Ok(&v2_non_inclusion_40_2::VERIFYINGKEY),
292 (_, 3) => Ok(&v2_non_inclusion_40_3::VERIFYINGKEY),
293 (_, 4) => Ok(&v2_non_inclusion_40_4::VERIFYINGKEY),
294 (_, 5) => Ok(&v2_non_inclusion_40_5::VERIFYINGKEY),
295 (_, 6) => Ok(&v2_non_inclusion_40_6::VERIFYINGKEY),
296 (_, 7) => Ok(&v2_non_inclusion_40_7::VERIFYINGKEY),
297 (_, 8) => Ok(&v2_non_inclusion_40_8::VERIFYINGKEY),
298 (_, 9) => Ok(&v2_non_inclusion_40_9::VERIFYINGKEY),
299 (_, 10) => Ok(&v2_non_inclusion_40_10::VERIFYINGKEY),
300 (_, 11) => Ok(&v2_non_inclusion_40_11::VERIFYINGKEY),
301 (_, 12) => Ok(&v2_non_inclusion_40_12::VERIFYINGKEY),
302 (_, 13) => Ok(&v2_non_inclusion_40_13::VERIFYINGKEY),
303 (_, 14) => Ok(&v2_non_inclusion_40_14::VERIFYINGKEY),
304 (_, 15) => Ok(&v2_non_inclusion_40_15::VERIFYINGKEY),
305 (_, 16) => Ok(&v2_non_inclusion_40_16::VERIFYINGKEY),
306 (_, 17) => Ok(&v2_non_inclusion_40_17::VERIFYINGKEY),
307 (_, 18) => Ok(&v2_non_inclusion_40_18::VERIFYINGKEY),
308 (_, 19) => Ok(&v2_non_inclusion_40_19::VERIFYINGKEY),
309 (_, 20) => Ok(&v2_non_inclusion_40_20::VERIFYINGKEY),
310 (_, 21) => Ok(&v2_non_inclusion_40_21::VERIFYINGKEY),
311 (_, 22) => Ok(&v2_non_inclusion_40_22::VERIFYINGKEY),
312 (_, 23) => Ok(&v2_non_inclusion_40_23::VERIFYINGKEY),
313 (_, 24) => Ok(&v2_non_inclusion_40_24::VERIFYINGKEY),
314 (_, 25) => Ok(&v2_non_inclusion_40_25::VERIFYINGKEY),
315 (_, 26) => Ok(&v2_non_inclusion_40_26::VERIFYINGKEY),
316 (_, 27) => Ok(&v2_non_inclusion_40_27::VERIFYINGKEY),
317 (_, 28) => Ok(&v2_non_inclusion_40_28::VERIFYINGKEY),
318 (_, 29) => Ok(&v2_non_inclusion_40_29::VERIFYINGKEY),
319 (_, 30) => Ok(&v2_non_inclusion_40_30::VERIFYINGKEY),
320 (_, 31) => Ok(&v2_non_inclusion_40_31::VERIFYINGKEY),
321 (_, 32) => Ok(&v2_non_inclusion_40_32::VERIFYINGKEY),
322
323 _ => Err(InvalidPublicInputsLength),
325 }
326}
327
328#[inline(never)]
329pub fn verify<const N: usize>(
330 public_inputs: &[[u8; 32]; N],
331 proof: &CompressedProof,
332 vk: &Groth16Verifyingkey,
333) -> Result<(), VerifierError> {
334 let proof_a = decompress_g1(&proof.a).map_err(|_| DecompressG1Failed)?;
335 let proof_b = decompress_g2(&proof.b).map_err(|_| DecompressG2Failed)?;
336 let proof_c = decompress_g1(&proof.c).map_err(|_| DecompressG1Failed)?;
337 let mut verifier = Groth16Verifier::new(&proof_a, &proof_b, &proof_c, public_inputs, vk)
338 .map_err(|_| {
339 #[cfg(all(target_os = "solana", feature = "solana"))]
340 {
341 use solana_msg::msg;
342 msg!("Proof verification failed");
343 msg!("Public inputs: {:?}", public_inputs);
344 msg!("Proof A: {:?}", proof_a);
345 msg!("Proof B: {:?}", proof_b);
346 msg!("Proof C: {:?}", proof_c);
347 }
348 CreateGroth16VerifierFailed
349 })?;
350 verifier.verify().map_err(|_| {
351 #[cfg(all(target_os = "solana", feature = "solana"))]
352 {
353 use solana_msg::msg;
354 msg!("Proof verification failed");
355 msg!("Public inputs: {:?}", public_inputs);
356 msg!("Proof A: {:?}", proof_a);
357 msg!("Proof B: {:?}", proof_b);
358 msg!("Proof C: {:?}", proof_c);
359 }
360 ProofVerificationFailed
361 })?;
362 Ok(())
363}
364
365#[inline(never)]
366pub fn verify_batch_append_with_proofs(
367 batch_size: u64,
368 public_input_hash: [u8; 32],
369 compressed_proof: &CompressedProof,
370) -> Result<(), VerifierError> {
371 match batch_size {
372 10 => verify::<1>(
373 &[public_input_hash],
374 compressed_proof,
375 &batch_append_32_10::VERIFYINGKEY,
376 ),
377 500 => verify::<1>(
378 &[public_input_hash],
379 compressed_proof,
380 &batch_append_32_500::VERIFYINGKEY,
381 ),
382 _ => Err(InvalidPublicInputsLength),
383 }
384}
385
386#[inline(never)]
387pub fn verify_batch_update(
388 batch_size: u64,
389 public_input_hash: [u8; 32],
390 compressed_proof: &CompressedProof,
391) -> Result<(), VerifierError> {
392 match batch_size {
393 10 => verify::<1>(
394 &[public_input_hash],
395 compressed_proof,
396 &batch_update_32_10::VERIFYINGKEY,
397 ),
398 500 => verify::<1>(
399 &[public_input_hash],
400 compressed_proof,
401 &batch_update_32_500::VERIFYINGKEY,
402 ),
403 _ => Err(InvalidPublicInputsLength),
404 }
405}
406
407#[inline(never)]
408pub fn verify_batch_address_update(
409 batch_size: u64,
410 public_input_hash: [u8; 32],
411 compressed_proof: &CompressedProof,
412) -> Result<(), VerifierError> {
413 match batch_size {
414 10 => verify::<1>(
415 &[public_input_hash],
416 compressed_proof,
417 &batch_address_append_40_10::VERIFYINGKEY,
418 ),
419 250 => verify::<1>(
420 &[public_input_hash],
421 compressed_proof,
422 &batch_address_append_40_250::VERIFYINGKEY,
423 ),
424 _ => Err(InvalidPublicInputsLength),
425 }
426}