1use std::{borrow::Borrow, path::Path, str::FromStr};
2
3use anyhow::Result;
4use num_bigint::BigUint;
5use p3_baby_bear::BabyBear;
6use p3_field::{AbstractField, PrimeField};
7use sp1_core_executor::subproof::SubproofVerifier;
8use sp1_core_machine::cpu::MAX_CPU_LOG_DEGREE;
9use sp1_primitives::{
10 consts::WORD_SIZE,
11 io::{blake3_hash, SP1PublicValues},
12};
13
14use sp1_recursion_circuit::machine::RootPublicValues;
15use sp1_recursion_core::{air::RecursionPublicValues, stark::BabyBearPoseidon2Outer};
16use sp1_recursion_gnark_ffi::{
17 Groth16Bn254Proof, Groth16Bn254Prover, PlonkBn254Proof, PlonkBn254Prover,
18};
19use sp1_stark::{
20 air::{PublicValues, POSEIDON_NUM_WORDS, PV_DIGEST_NUM_WORDS},
21 baby_bear_poseidon2::BabyBearPoseidon2,
22 MachineProof, MachineProver, MachineVerificationError, SP1ReduceProof, StarkGenericConfig,
23 Word,
24};
25use thiserror::Error;
26
27use crate::{
28 components::SP1ProverComponents,
29 utils::{is_recursion_public_values_valid, is_root_public_values_valid},
30 CoreSC, HashableKey, OuterSC, SP1CoreProofData, SP1Prover, SP1VerifyingKey,
31};
32
33#[derive(Error, Debug)]
34pub enum PlonkVerificationError {
35 #[error(
36 "the verifying key does not match the inner plonk bn254 proof's committed verifying key"
37 )]
38 InvalidVerificationKey,
39 #[error(
40 "the public values in the sp1 proof do not match the public values in the inner plonk bn254 proof"
41 )]
42 InvalidPublicValues,
43}
44
45#[derive(Error, Debug)]
46pub enum Groth16VerificationError {
47 #[error(
48 "the verifying key does not match the inner groth16 bn254 proof's committed verifying key"
49 )]
50 InvalidVerificationKey,
51 #[error(
52 "the public values in the sp1 proof do not match the public values in the inner groth16 bn254 proof"
53 )]
54 InvalidPublicValues,
55}
56
57impl<C: SP1ProverComponents> SP1Prover<C> {
58 pub fn verify(
61 &self,
62 proof: &SP1CoreProofData,
63 vk: &SP1VerifyingKey,
64 ) -> Result<(), MachineVerificationError<CoreSC>> {
65 if proof.0.is_empty() {
67 return Err(MachineVerificationError::EmptyProof);
68 }
69
70 let first_shard = proof.0.first().unwrap();
75 if !first_shard.contains_cpu() {
76 return Err(MachineVerificationError::MissingCpuInFirstShard);
77 }
78
79 for shard_proof in proof.0.iter() {
84 if shard_proof.contains_cpu() {
85 let log_degree_cpu = shard_proof.log_degree_cpu();
86 if log_degree_cpu > MAX_CPU_LOG_DEGREE {
87 return Err(MachineVerificationError::CpuLogDegreeTooLarge(log_degree_cpu));
88 }
89 }
90 }
91
92 let mut current_shard = BabyBear::zero();
100 for shard_proof in proof.0.iter() {
101 let public_values: &PublicValues<Word<_>, _> =
102 shard_proof.public_values.as_slice().borrow();
103 current_shard += BabyBear::one();
104 if public_values.shard != current_shard {
105 return Err(MachineVerificationError::InvalidPublicValues(
106 "shard index should be the previous shard index + 1 and start at 1",
107 ));
108 }
109 }
110
111 let mut current_execution_shard = BabyBear::zero();
121 for shard_proof in proof.0.iter() {
122 let public_values: &PublicValues<Word<_>, _> =
123 shard_proof.public_values.as_slice().borrow();
124 if shard_proof.contains_cpu() {
125 current_execution_shard += BabyBear::one();
126 if public_values.execution_shard != current_execution_shard {
127 return Err(MachineVerificationError::InvalidPublicValues(
128 "execution shard index should be the previous execution shard index + 1 if cpu exists and start at 1",
129 ));
130 }
131 }
132 }
133
134 let mut prev_next_pc = BabyBear::zero();
147 for (i, shard_proof) in proof.0.iter().enumerate() {
148 let public_values: &PublicValues<Word<_>, _> =
149 shard_proof.public_values.as_slice().borrow();
150 if i == 0 && public_values.start_pc != vk.vk.pc_start {
151 return Err(MachineVerificationError::InvalidPublicValues(
152 "start_pc != vk.start_pc: program counter should start at vk.start_pc",
153 ));
154 } else if i != 0 && public_values.start_pc != prev_next_pc {
155 return Err(MachineVerificationError::InvalidPublicValues(
156 "start_pc != next_pc_prev: start_pc should equal next_pc_prev for all shards",
157 ));
158 } else if !shard_proof.contains_cpu() && public_values.start_pc != public_values.next_pc
159 {
160 return Err(MachineVerificationError::InvalidPublicValues(
161 "start_pc != next_pc: start_pc should equal next_pc for non-cpu shards",
162 ));
163 } else if shard_proof.contains_cpu() && public_values.start_pc == BabyBear::zero() {
164 return Err(MachineVerificationError::InvalidPublicValues(
165 "start_pc == 0: execution should never start at halted state",
166 ));
167 } else if i == proof.0.len() - 1 && public_values.next_pc != BabyBear::zero() {
168 return Err(MachineVerificationError::InvalidPublicValues(
169 "next_pc != 0: execution should have halted",
170 ));
171 }
172 prev_next_pc = public_values.next_pc;
173 }
174
175 for shard_proof in proof.0.iter() {
179 let public_values: &PublicValues<Word<_>, _> =
180 shard_proof.public_values.as_slice().borrow();
181 if public_values.exit_code != BabyBear::zero() {
182 return Err(MachineVerificationError::InvalidPublicValues(
183 "exit_code != 0: exit code should be zero for all shards",
184 ));
185 }
186 }
187
188 let mut last_init_addr_bits_prev = [BabyBear::zero(); 32];
204 let mut last_finalize_addr_bits_prev = [BabyBear::zero(); 32];
205 for shard_proof in proof.0.iter() {
206 let public_values: &PublicValues<Word<_>, _> =
207 shard_proof.public_values.as_slice().borrow();
208 if public_values.previous_init_addr_bits != last_init_addr_bits_prev {
209 return Err(MachineVerificationError::InvalidPublicValues(
210 "previous_init_addr_bits != last_init_addr_bits_prev",
211 ));
212 } else if public_values.previous_finalize_addr_bits != last_finalize_addr_bits_prev {
213 return Err(MachineVerificationError::InvalidPublicValues(
214 "last_init_addr_bits != last_finalize_addr_bits_prev",
215 ));
216 } else if !shard_proof.contains_global_memory_init() &&
217 public_values.previous_init_addr_bits != public_values.last_init_addr_bits
218 {
219 return Err(MachineVerificationError::InvalidPublicValues(
220 "previous_init_addr_bits != last_init_addr_bits",
221 ));
222 } else if !shard_proof.contains_global_memory_finalize() &&
223 public_values.previous_finalize_addr_bits !=
224 public_values.last_finalize_addr_bits
225 {
226 return Err(MachineVerificationError::InvalidPublicValues(
227 "previous_finalize_addr_bits != last_finalize_addr_bits",
228 ));
229 }
230 last_init_addr_bits_prev = public_values.last_init_addr_bits;
231 last_finalize_addr_bits_prev = public_values.last_finalize_addr_bits;
232 }
233
234 let zero_committed_value_digest =
254 [Word([BabyBear::zero(); WORD_SIZE]); PV_DIGEST_NUM_WORDS];
255 let zero_deferred_proofs_digest = [BabyBear::zero(); POSEIDON_NUM_WORDS];
256 let mut committed_value_digest_prev = zero_committed_value_digest;
257 let mut deferred_proofs_digest_prev = zero_deferred_proofs_digest;
258 for shard_proof in proof.0.iter() {
259 let public_values: &PublicValues<Word<_>, _> =
260 shard_proof.public_values.as_slice().borrow();
261 if committed_value_digest_prev != zero_committed_value_digest &&
262 public_values.committed_value_digest != committed_value_digest_prev
263 {
264 return Err(MachineVerificationError::InvalidPublicValues(
265 "committed_value_digest != committed_value_digest_prev",
266 ));
267 } else if deferred_proofs_digest_prev != zero_deferred_proofs_digest &&
268 public_values.deferred_proofs_digest != deferred_proofs_digest_prev
269 {
270 return Err(MachineVerificationError::InvalidPublicValues(
271 "deferred_proofs_digest != deferred_proofs_digest_prev",
272 ));
273 } else if !shard_proof.contains_cpu() &&
274 public_values.committed_value_digest != committed_value_digest_prev
275 {
276 return Err(MachineVerificationError::InvalidPublicValues(
277 "committed_value_digest != committed_value_digest_prev",
278 ));
279 } else if !shard_proof.contains_cpu() &&
280 public_values.deferred_proofs_digest != deferred_proofs_digest_prev
281 {
282 return Err(MachineVerificationError::InvalidPublicValues(
283 "deferred_proofs_digest != deferred_proofs_digest_prev",
284 ));
285 }
286 committed_value_digest_prev = public_values.committed_value_digest;
287 deferred_proofs_digest_prev = public_values.deferred_proofs_digest;
288 }
289
290 if proof.0.len() >= 1 << 16 {
292 return Err(MachineVerificationError::TooManyShards);
293 }
294
295 let mut challenger = self.core_prover.config().challenger();
297 let machine_proof = MachineProof { shard_proofs: proof.0.to_vec() };
298 self.core_prover.machine().verify(&vk.vk, &machine_proof, &mut challenger)?;
299
300 Ok(())
301 }
302
303 pub fn verify_compressed(
305 &self,
306 proof: &SP1ReduceProof<BabyBearPoseidon2>,
307 vk: &SP1VerifyingKey,
308 ) -> Result<(), MachineVerificationError<CoreSC>> {
309 let SP1ReduceProof { vk: compress_vk, proof } = proof;
310 let mut challenger = self.compress_prover.config().challenger();
311 let machine_proof = MachineProof { shard_proofs: vec![proof.clone()] };
312 self.compress_prover.machine().verify(compress_vk, &machine_proof, &mut challenger)?;
313
314 let public_values: &RecursionPublicValues<_> = proof.public_values.as_slice().borrow();
316
317 if !is_recursion_public_values_valid(self.compress_prover.machine().config(), public_values)
318 {
319 return Err(MachineVerificationError::InvalidPublicValues(
320 "recursion public values are invalid",
321 ));
322 }
323
324 if public_values.vk_root != self.recursion_vk_root {
325 return Err(MachineVerificationError::InvalidPublicValues("vk_root mismatch"));
326 }
327
328 if self.vk_verification && !self.recursion_vk_map.contains_key(&compress_vk.hash_babybear())
329 {
330 return Err(MachineVerificationError::InvalidVerificationKey);
331 }
332
333 if public_values.is_complete != BabyBear::one() {
336 return Err(MachineVerificationError::InvalidPublicValues("is_complete is not 1"));
337 }
338
339 let vkey_hash = vk.hash_babybear();
341 if public_values.sp1_vk_digest != vkey_hash {
342 return Err(MachineVerificationError::InvalidPublicValues("sp1 vk hash mismatch"));
343 }
344
345 Ok(())
346 }
347
348 pub fn verify_shrink(
350 &self,
351 proof: &SP1ReduceProof<BabyBearPoseidon2>,
352 vk: &SP1VerifyingKey,
353 ) -> Result<(), MachineVerificationError<CoreSC>> {
354 let mut challenger = self.shrink_prover.config().challenger();
355 let machine_proof = MachineProof { shard_proofs: vec![proof.proof.clone()] };
356 self.shrink_prover.machine().verify(&proof.vk, &machine_proof, &mut challenger)?;
357
358 let public_values: &RecursionPublicValues<_> =
360 proof.proof.public_values.as_slice().borrow();
361 if !is_recursion_public_values_valid(self.compress_prover.machine().config(), public_values)
362 {
363 return Err(MachineVerificationError::InvalidPublicValues(
364 "recursion public values are invalid",
365 ));
366 }
367 if public_values.vk_root != self.recursion_vk_root {
368 return Err(MachineVerificationError::InvalidPublicValues("vk_root mismatch"));
369 }
370
371 if self.vk_verification && !self.recursion_vk_map.contains_key(&proof.vk.hash_babybear()) {
372 return Err(MachineVerificationError::InvalidVerificationKey);
373 }
374
375 if public_values.is_complete != BabyBear::one() {
378 return Err(MachineVerificationError::InvalidPublicValues("is_complete is not 1"));
379 }
380
381 let vkey_hash = vk.hash_babybear();
383 if public_values.sp1_vk_digest != vkey_hash {
384 return Err(MachineVerificationError::InvalidPublicValues("sp1 vk hash mismatch"));
385 }
386
387 Ok(())
388 }
389
390 pub fn verify_wrap_bn254(
392 &self,
393 proof: &SP1ReduceProof<BabyBearPoseidon2Outer>,
394 vk: &SP1VerifyingKey,
395 ) -> Result<(), MachineVerificationError<OuterSC>> {
396 let mut challenger = self.wrap_prover.config().challenger();
397 let machine_proof = MachineProof { shard_proofs: vec![proof.proof.clone()] };
398
399 let wrap_vk = self.wrap_vk.get().expect("Wrap verifier key not set");
400 self.wrap_prover.machine().verify(wrap_vk, &machine_proof, &mut challenger)?;
401
402 let public_values: &RootPublicValues<_> = proof.proof.public_values.as_slice().borrow();
404 if !is_root_public_values_valid(self.shrink_prover.machine().config(), public_values) {
405 return Err(MachineVerificationError::InvalidPublicValues(
406 "root public values are invalid",
407 ));
408 }
409
410 let vkey_hash = vk.hash_babybear();
412 if *public_values.sp1_vk_digest() != vkey_hash {
413 return Err(MachineVerificationError::InvalidPublicValues("sp1 vk hash mismatch"));
414 }
415
416 Ok(())
417 }
418
419 pub fn verify_plonk_bn254(
421 &self,
422 proof: &PlonkBn254Proof,
423 vk: &SP1VerifyingKey,
424 public_values: &SP1PublicValues,
425 build_dir: &Path,
426 ) -> Result<()> {
427 let prover = PlonkBn254Prover::new();
428
429 let vkey_hash = BigUint::from_str(&proof.public_inputs[0])?;
430 let committed_values_digest = BigUint::from_str(&proof.public_inputs[1])?;
431
432 prover.verify(proof, &vkey_hash, &committed_values_digest, build_dir)?;
434
435 verify_plonk_bn254_public_inputs(vk, public_values, &proof.public_inputs)?;
436
437 Ok(())
438 }
439
440 pub fn verify_groth16_bn254(
442 &self,
443 proof: &Groth16Bn254Proof,
444 vk: &SP1VerifyingKey,
445 public_values: &SP1PublicValues,
446 build_dir: &Path,
447 ) -> Result<()> {
448 let prover = Groth16Bn254Prover::new();
449
450 let vkey_hash = BigUint::from_str(&proof.public_inputs[0])?;
451 let committed_values_digest = BigUint::from_str(&proof.public_inputs[1])?;
452
453 prover.verify(proof, &vkey_hash, &committed_values_digest, build_dir)?;
455
456 verify_groth16_bn254_public_inputs(vk, public_values, &proof.public_inputs)?;
457
458 Ok(())
459 }
460}
461
462pub fn verify_plonk_bn254_public_inputs(
465 vk: &SP1VerifyingKey,
466 public_values: &SP1PublicValues,
467 plonk_bn254_public_inputs: &[String],
468) -> Result<()> {
469 let expected_vk_hash = BigUint::from_str(&plonk_bn254_public_inputs[0])?;
470 let expected_public_values_hash = BigUint::from_str(&plonk_bn254_public_inputs[1])?;
471
472 let vk_hash = vk.hash_bn254().as_canonical_biguint();
473 if vk_hash != expected_vk_hash {
474 return Err(PlonkVerificationError::InvalidVerificationKey.into());
475 }
476
477 verify_public_values(public_values, expected_public_values_hash)?;
478
479 Ok(())
480}
481
482pub fn verify_groth16_bn254_public_inputs(
485 vk: &SP1VerifyingKey,
486 public_values: &SP1PublicValues,
487 groth16_bn254_public_inputs: &[String],
488) -> Result<()> {
489 let expected_vk_hash = BigUint::from_str(&groth16_bn254_public_inputs[0])?;
490 let expected_public_values_hash = BigUint::from_str(&groth16_bn254_public_inputs[1])?;
491
492 let vk_hash = vk.hash_bn254().as_canonical_biguint();
493 if vk_hash != expected_vk_hash {
494 return Err(Groth16VerificationError::InvalidVerificationKey.into());
495 }
496
497 verify_public_values(public_values, expected_public_values_hash)?;
498
499 Ok(())
500}
501
502fn verify_public_values(
512 public_values: &SP1PublicValues,
513 expected_public_values_hash: BigUint,
514) -> Result<()> {
515 let sha256_public_values_hash = public_values.hash_bn254();
518 if sha256_public_values_hash != expected_public_values_hash {
519 let blake3_public_values_hash = public_values.hash_bn254_with_fn(blake3_hash);
520 if blake3_public_values_hash != expected_public_values_hash {
521 return Err(Groth16VerificationError::InvalidPublicValues.into());
522 }
523 }
524
525 Ok(())
526}
527
528impl<C: SP1ProverComponents> SubproofVerifier for SP1Prover<C> {
529 fn verify_deferred_proof(
530 &self,
531 proof: &sp1_core_machine::reduce::SP1ReduceProof<BabyBearPoseidon2>,
532 vk: &sp1_stark::StarkVerifyingKey<BabyBearPoseidon2>,
533 vk_hash: [u32; 8],
534 committed_value_digest: [u32; 8],
535 ) -> Result<(), MachineVerificationError<BabyBearPoseidon2>> {
536 if vk.hash_u32() != vk_hash {
538 return Err(MachineVerificationError::InvalidPublicValues(
539 "vk hash from syscall does not match vkey from input",
540 ));
541 }
542 self.verify_compressed(
544 &SP1ReduceProof { vk: proof.vk.clone(), proof: proof.proof.clone() },
545 &SP1VerifyingKey { vk: vk.clone() },
546 )?;
547 let public_values: &RecursionPublicValues<_> =
549 proof.proof.public_values.as_slice().borrow();
550 if public_values.vk_root != self.recursion_vk_root {
551 return Err(MachineVerificationError::InvalidPublicValues("vk_root mismatch"));
552 }
553
554 for (i, word) in public_values.committed_value_digest.iter().enumerate() {
555 if *word != committed_value_digest[i].into() {
556 return Err(MachineVerificationError::InvalidPublicValues(
557 "committed_value_digest does not match",
558 ));
559 }
560 }
561 Ok(())
562 }
563}