1#![allow(clippy::too_many_arguments)]
11#![allow(clippy::new_without_default)]
12#![allow(clippy::collapsible_else_if)]
13
14pub mod build;
15pub mod components;
16pub mod gas;
17pub mod shapes;
18pub mod types;
19pub mod utils;
20pub mod verify;
21
22use std::{
23 borrow::Borrow,
24 collections::BTreeMap,
25 env,
26 error::Error,
27 num::NonZeroUsize,
28 path::Path,
29 sync::{
30 atomic::{AtomicUsize, Ordering},
31 mpsc::{channel, sync_channel},
32 Arc, Mutex, OnceLock,
33 },
34 thread,
35};
36
37use crate::shapes::SP1CompressProgramShape;
38use lru::LruCache;
39use p3_baby_bear::BabyBear;
40use p3_field::{AbstractField, PrimeField, PrimeField32};
41use p3_matrix::dense::RowMajorMatrix;
42use shapes::SP1ProofShape;
43use sp1_core_executor::{
44 estimator::RecordEstimator, ExecutionError, ExecutionReport, Executor, Program, RiscvAirId,
45 SP1Context,
46};
47use sp1_core_machine::{
48 io::SP1Stdin,
49 reduce::SP1ReduceProof,
50 riscv::RiscvAir,
51 shape::CoreShapeConfig,
52 utils::{concurrency::TurnBasedSync, SP1CoreProverError},
53};
54use sp1_primitives::hash_deferred_proof;
55pub use sp1_primitives::io::SP1PublicValues;
56use sp1_recursion_circuit::{
57 hash::FieldHasher,
58 machine::{
59 PublicValuesOutputDigest, SP1CompressRootVerifierWithVKey, SP1CompressShape,
60 SP1CompressWithVKeyVerifier, SP1CompressWithVKeyWitnessValues, SP1CompressWithVkeyShape,
61 SP1CompressWitnessValues, SP1DeferredVerifier, SP1DeferredWitnessValues,
62 SP1MerkleProofWitnessValues, SP1RecursionShape, SP1RecursionWitnessValues,
63 SP1RecursiveVerifier,
64 },
65 merkle_tree::MerkleTree,
66 witness::Witnessable,
67 WrapConfig,
68};
69use sp1_recursion_compiler::{
70 circuit::AsmCompiler,
71 config::InnerConfig,
72 ir::{Builder, DslIrProgram, Witness},
73};
74use sp1_recursion_core::{
75 air::RecursionPublicValues,
76 machine::RecursionAir,
77 runtime::ExecutionRecord,
78 shape::{RecursionShape, RecursionShapeConfig},
79 stark::BabyBearPoseidon2Outer,
80 RecursionProgram, Runtime as RecursionRuntime,
81};
82pub use sp1_recursion_gnark_ffi::proof::{Groth16Bn254Proof, PlonkBn254Proof};
83use sp1_recursion_gnark_ffi::{groth16_bn254::Groth16Bn254Prover, plonk_bn254::PlonkBn254Prover};
84use sp1_stark::{
85 baby_bear_poseidon2::BabyBearPoseidon2,
86 shape::{OrderedShape, Shape},
87 Challenge, MachineProver, MachineProvingKey, SP1ProverOpts, ShardProof, SplitOpts,
88 StarkGenericConfig, StarkVerifyingKey, Val, Word, DIGEST_SIZE,
89};
90use tracing::instrument;
91
92pub use types::*;
93use utils::{sp1_committed_values_digest_bn254, sp1_vkey_digest_bn254, words_to_bytes};
94
95use components::{CpuProverComponents, SP1ProverComponents};
96
97pub use sp1_stark::{CoreSC, InnerSC};
98
99pub const SP1_CIRCUIT_VERSION: &str = include_str!("../SP1_VERSION");
105
106pub type OuterSC = BabyBearPoseidon2Outer;
108
109pub type DeviceProvingKey<C> = <<C as SP1ProverComponents>::CoreProver as MachineProver<
110 BabyBearPoseidon2,
111 RiscvAir<BabyBear>,
112>>::DeviceProvingKey;
113
114const COMPRESS_DEGREE: usize = 3;
115const SHRINK_DEGREE: usize = 3;
116const WRAP_DEGREE: usize = 9;
117
118const CORE_CACHE_SIZE: usize = 5;
119pub const REDUCE_BATCH_SIZE: usize = 2;
120
121pub type CompressAir<F> = RecursionAir<F, COMPRESS_DEGREE>;
122pub type ShrinkAir<F> = RecursionAir<F, SHRINK_DEGREE>;
123pub type WrapAir<F> = RecursionAir<F, WRAP_DEGREE>;
124
125pub struct SP1Prover<C: SP1ProverComponents = CpuProverComponents> {
130 pub core_prover: C::CoreProver,
132 pub compress_prover: C::CompressProver,
134 pub shrink_prover: C::ShrinkProver,
136 pub wrap_prover: C::WrapProver,
138 pub lift_programs_lru: Mutex<LruCache<SP1RecursionShape, Arc<RecursionProgram<BabyBear>>>>,
140 pub lift_cache_misses: AtomicUsize,
142 pub join_programs_map: BTreeMap<SP1CompressWithVkeyShape, Arc<RecursionProgram<BabyBear>>>,
144 pub join_cache_misses: AtomicUsize,
146 pub recursion_vk_root: <InnerSC as FieldHasher<BabyBear>>::Digest,
148 pub recursion_vk_map: BTreeMap<<InnerSC as FieldHasher<BabyBear>>::Digest, usize>,
150 pub recursion_vk_tree: MerkleTree<BabyBear, InnerSC>,
152 pub core_shape_config: Option<CoreShapeConfig<BabyBear>>,
154 pub compress_shape_config: Option<RecursionShapeConfig<BabyBear, CompressAir<BabyBear>>>,
156 pub wrap_program: OnceLock<Arc<RecursionProgram<BabyBear>>>,
158 pub wrap_vk: OnceLock<StarkVerifyingKey<OuterSC>>,
160 pub vk_verification: bool,
162}
163
164impl<C: SP1ProverComponents> SP1Prover<C> {
165 #[instrument(name = "initialize prover", level = "debug", skip_all)]
167 pub fn new() -> Self {
168 Self::uninitialized()
169 }
170
171 pub fn uninitialized() -> Self {
173 let core_machine = RiscvAir::machine(CoreSC::default());
175 let core_prover = C::CoreProver::new(core_machine);
176
177 let compress_machine = CompressAir::compress_machine(InnerSC::default());
178 let compress_prover = C::CompressProver::new(compress_machine);
179
180 let shrink_machine = ShrinkAir::shrink_machine(InnerSC::compressed());
181 let shrink_prover = C::ShrinkProver::new(shrink_machine);
182
183 let wrap_machine = WrapAir::wrap_machine(OuterSC::default());
184 let wrap_prover = C::WrapProver::new(wrap_machine);
185
186 let core_cache_size = NonZeroUsize::new(
187 env::var("PROVER_CORE_CACHE_SIZE")
188 .unwrap_or_else(|_| CORE_CACHE_SIZE.to_string())
189 .parse()
190 .unwrap_or(CORE_CACHE_SIZE),
191 )
192 .expect("PROVER_CORE_CACHE_SIZE must be a non-zero usize");
193
194 let core_shape_config = env::var("FIX_CORE_SHAPES")
195 .map(|v| v.eq_ignore_ascii_case("true"))
196 .unwrap_or(true)
197 .then_some(CoreShapeConfig::default());
198
199 let recursion_shape_config = env::var("FIX_RECURSION_SHAPES")
200 .map(|v| v.eq_ignore_ascii_case("true"))
201 .unwrap_or(true)
202 .then_some(RecursionShapeConfig::default());
203
204 let vk_verification =
205 env::var("VERIFY_VK").map(|v| v.eq_ignore_ascii_case("true")).unwrap_or(true);
206 tracing::debug!("vk verification: {}", vk_verification);
207
208 let allowed_vk_map: BTreeMap<[BabyBear; DIGEST_SIZE], usize> = if vk_verification {
210 bincode::deserialize(include_bytes!(concat!(env!("OUT_DIR"), "/vk_map.bin"))).unwrap()
211 } else {
212 bincode::deserialize(include_bytes!("vk_map_dummy.bin")).unwrap()
213 };
214
215 let (root, merkle_tree) = MerkleTree::commit(allowed_vk_map.keys().copied().collect());
216
217 let mut compress_programs = BTreeMap::new();
218 let program_cache_disabled = env::var("SP1_DISABLE_PROGRAM_CACHE")
219 .map(|v| v.eq_ignore_ascii_case("true"))
220 .unwrap_or(false);
221 if !program_cache_disabled {
222 if let Some(config) = &recursion_shape_config {
223 SP1ProofShape::generate_compress_shapes(config, REDUCE_BATCH_SIZE).for_each(
224 |shape| {
225 let compress_shape = SP1CompressWithVkeyShape {
226 compress_shape: shape.into(),
227 merkle_tree_height: merkle_tree.height,
228 };
229 let input = SP1CompressWithVKeyWitnessValues::dummy(
230 compress_prover.machine(),
231 &compress_shape,
232 );
233 let program = compress_program_from_input::<C>(
234 recursion_shape_config.as_ref(),
235 &compress_prover,
236 vk_verification,
237 &input,
238 );
239 let program = Arc::new(program);
240 compress_programs.insert(compress_shape, program);
241 },
242 );
243 }
244 }
245
246 Self {
247 core_prover,
248 compress_prover,
249 shrink_prover,
250 wrap_prover,
251 lift_programs_lru: Mutex::new(LruCache::new(core_cache_size)),
252 lift_cache_misses: AtomicUsize::new(0),
253 join_programs_map: compress_programs,
254 join_cache_misses: AtomicUsize::new(0),
255 recursion_vk_root: root,
256 recursion_vk_tree: merkle_tree,
257 recursion_vk_map: allowed_vk_map,
258 core_shape_config,
259 compress_shape_config: recursion_shape_config,
260 vk_verification,
261 wrap_program: OnceLock::new(),
262 wrap_vk: OnceLock::new(),
263 }
264 }
265
266 #[instrument(name = "setup", level = "debug", skip_all)]
268 pub fn setup(
269 &self,
270 elf: &[u8],
271 ) -> (SP1ProvingKey, DeviceProvingKey<C>, Program, SP1VerifyingKey) {
272 let program = self.get_program(elf).unwrap();
273 let (pk, vk) = self.core_prover.setup(&program);
274 let vk = SP1VerifyingKey { vk };
275 let pk = SP1ProvingKey {
276 pk: self.core_prover.pk_to_host(&pk),
277 elf: elf.to_vec(),
278 vk: vk.clone(),
279 };
280 let pk_d = self.core_prover.pk_to_device(&pk.pk);
281 (pk, pk_d, program, vk)
282 }
283
284 pub fn get_program(&self, elf: &[u8]) -> eyre::Result<Program> {
286 let mut program = Program::from(elf)?;
287 if let Some(core_shape_config) = &self.core_shape_config {
288 core_shape_config.fix_preprocessed_shape(&mut program)?;
289 }
290 Ok(program)
291 }
292
293 fn get_gas_calculator(
294 &self,
295 preprocessed_shape: Shape<RiscvAirId>,
296 split_opts: SplitOpts,
297 ) -> impl FnMut(&RecordEstimator) -> Result<u64, Box<dyn Error>> + '_ {
298 move |estimator: &RecordEstimator| -> Result<u64, Box<dyn Error>> {
299 let est_records = gas::estimated_records(&split_opts, estimator);
300 let raw_gas =
301 gas::fit_records_to_shapes(self.core_shape_config.as_ref().unwrap(), est_records)
302 .enumerate()
303 .map(|(i, shape)| {
304 let mut shape: Shape<RiscvAirId> = shape.map_err(Box::new)?;
305 shape.extend(preprocessed_shape.iter().map(|(k, v)| (*k, *v)));
306 tracing::debug!("shape for estimated shard {i}: {:?}", &shape.inner);
307 Ok(gas::predict(enum_map::EnumMap::from_iter(shape).as_array()))
308 })
309 .sum::<Result<_, Box<dyn Error>>>()?;
310 let gas = gas::final_transform(raw_gas).map_err(Box::new)?;
311 Ok(gas)
312 }
313 }
314
315 #[instrument(name = "execute", level = "info", skip_all)]
317 pub fn execute<'a>(
318 &'a self,
319 elf: &[u8],
320 stdin: &SP1Stdin,
321 mut context: SP1Context<'a>,
322 ) -> Result<(SP1PublicValues, [u8; 32], ExecutionReport), ExecutionError> {
323 context.subproof_verifier = Some(self);
324
325 let calculate_gas = context.calculate_gas;
326
327 let (opts, program) = if calculate_gas {
328 (gas::GAS_OPTS, self.get_program(elf).unwrap())
329 } else {
330 (sp1_stark::SP1CoreOpts::default(), Program::from(elf).unwrap())
331 };
332 let preprocessed_shape = program.preprocessed_shape.clone();
333
334 let mut runtime = Executor::with_context(program, opts, context);
335
336 if calculate_gas {
337 runtime.maximal_shapes = self.core_shape_config.as_ref().map(|config| {
339 config.maximal_core_shapes(opts.shard_size.ilog2() as usize).into_iter().collect()
340 });
341 runtime.record_estimator = Some(Box::default());
342 }
343
344 runtime.maybe_setup_profiler(elf);
345
346 runtime.write_vecs(&stdin.buffer);
347 for (proof, vkey) in stdin.proofs.iter() {
348 runtime.write_proof(proof.clone(), vkey.clone());
349 }
350 runtime.run_fast()?;
351
352 if calculate_gas {
353 let gas = self.get_gas_calculator(preprocessed_shape.unwrap(), opts.split_opts)(
354 runtime.record_estimator.as_ref().unwrap(),
355 );
356 runtime.report.gas = gas
357 .inspect(|g| tracing::info!("gas: {}", g))
358 .inspect_err(|e| tracing::error!("Encountered error while calculating gas: {}", e))
359 .ok();
360 }
361
362 let mut committed_value_digest = [0u8; 32];
363 runtime.record.public_values.committed_value_digest.iter().enumerate().for_each(
364 |(i, word)| {
365 let bytes = word.to_le_bytes();
366 committed_value_digest[i * 4..(i + 1) * 4].copy_from_slice(&bytes);
367 },
368 );
369
370 Ok((
371 SP1PublicValues::from(&runtime.state.public_values_stream),
372 committed_value_digest,
373 runtime.report,
374 ))
375 }
376
377 #[instrument(name = "prove_core", level = "info", skip_all)]
380 pub fn prove_core<'a>(
381 &'a self,
382 pk_d: &<<C as SP1ProverComponents>::CoreProver as MachineProver<
383 BabyBearPoseidon2,
384 RiscvAir<BabyBear>,
385 >>::DeviceProvingKey,
386 program: Program,
387 stdin: &SP1Stdin,
388 opts: SP1ProverOpts,
389 mut context: SP1Context<'a>,
390 ) -> Result<SP1CoreProof, SP1CoreProverError> {
391 context.subproof_verifier = Some(self);
392
393 let span = tracing::Span::current().clone();
396 std::thread::scope(|s| {
397 let _span = span.enter();
398 let (proof_tx, proof_rx) = channel();
399 let (shape_tx, shape_rx) = channel();
400
401 let span = tracing::Span::current().clone();
402 let handle = s.spawn(move || {
403 let _span = span.enter();
404
405 let pk = pk_d;
407
408 #[allow(clippy::type_complexity)]
415 let gas_calculator = (context.calculate_gas
416 && std::env::var("SP1_FORCE_GAS").is_ok())
417 .then(
418 || -> Box<dyn FnOnce(&RecordEstimator) -> Result<u64, Box<dyn Error>> + '_> {
419 tracing::info!("Forcing calculation of gas while proving.");
420 if opts.core_opts == gas::GAS_OPTS {
421 tracing::info!(
422 "The SP1CoreOpts matches the gas opts, so gas will be consistent."
423 );
424 } else {
425 tracing::warn!(
426 "The SP1CoreOpts does not match the gas opts. \
427 Gas will likely disagree with the standard gas calculated when executing."
428 );
429 }
430 let preprocessed_shape = program.preprocessed_shape.clone().unwrap();
431 Box::new(
432 self.get_gas_calculator(preprocessed_shape, opts.core_opts.split_opts),
433 )
434 },
435 );
436
437 sp1_core_machine::utils::prove_core_stream::<_, C::CoreProver>(
439 &self.core_prover,
440 pk,
441 program,
442 stdin,
443 opts.core_opts,
444 context,
445 self.core_shape_config.as_ref(),
446 proof_tx,
447 shape_tx,
448 None,
449 gas_calculator,
450 )
451 });
452
453 for _ in 0..3 {
455 if let Ok((shape, is_complete)) = shape_rx.recv() {
456 let recursion_shape =
457 SP1RecursionShape { proof_shapes: vec![shape], is_complete };
458
459 let compress_shape = SP1CompressProgramShape::Recursion(recursion_shape);
462
463 self.program_from_shape(compress_shape, None);
465 }
466 }
467
468 let shard_proofs: Vec<ShardProof<_>> = proof_rx.iter().collect();
470 let (public_values_stream, cycles) = handle.join().unwrap().unwrap();
471 let public_values = SP1PublicValues::from(&public_values_stream);
472 Self::check_for_high_cycles(cycles);
473 Ok(SP1CoreProof {
474 proof: SP1CoreProofData(shard_proofs),
475 stdin: stdin.clone(),
476 public_values,
477 cycles,
478 })
479 })
480 }
481
482 #[instrument(name = "compress", level = "info", skip_all)]
484 pub fn compress(
485 &self,
486 vk: &SP1VerifyingKey,
487 proof: SP1CoreProof,
488 deferred_proofs: Vec<SP1ReduceProof<InnerSC>>,
489 opts: SP1ProverOpts,
490 ) -> Result<SP1ReduceProof<InnerSC>, SP1RecursionProverError> {
491 #[allow(clippy::type_complexity)]
492 enum TracesOrInput {
493 ProgramRecordTraces(
494 Box<(
495 Arc<RecursionProgram<BabyBear>>,
496 ExecutionRecord<BabyBear>,
497 Vec<(String, RowMajorMatrix<BabyBear>)>,
498 )>,
499 ),
500 CircuitWitness(Box<SP1CircuitWitness>),
501 }
502
503 let batch_size = REDUCE_BATCH_SIZE;
505 let first_layer_batch_size = 1;
507
508 let shard_proofs = &proof.proof.0;
509
510 let first_layer_inputs =
512 self.get_first_layer_inputs(vk, shard_proofs, &deferred_proofs, first_layer_batch_size);
513
514 let mut expected_height = if first_layer_inputs.len() == 1 { 0 } else { 1 };
516 let num_first_layer_inputs = first_layer_inputs.len();
517 let mut num_layer_inputs = num_first_layer_inputs;
518 while num_layer_inputs > batch_size {
519 num_layer_inputs = num_layer_inputs.div_ceil(2);
520 expected_height += 1;
521 }
522
523 let span = tracing::Span::current().clone();
525 let (vk, proof) = thread::scope(|s| {
526 let _span = span.enter();
527
528 let input_sync = Arc::new(TurnBasedSync::new());
530 let (input_tx, input_rx) = sync_channel::<(usize, usize, SP1CircuitWitness, bool)>(
531 opts.recursion_opts.checkpoints_channel_capacity,
532 );
533 let input_tx = Arc::new(Mutex::new(input_tx));
534 {
535 let input_tx = Arc::clone(&input_tx);
536 let input_sync = Arc::clone(&input_sync);
537 s.spawn(move || {
538 for (index, input) in first_layer_inputs.into_iter().enumerate() {
539 input_sync.wait_for_turn(index);
540 input_tx.lock().unwrap().send((index, 0, input, false)).unwrap();
541 input_sync.advance_turn();
542 }
543 });
544 }
545
546 let record_and_trace_sync = Arc::new(TurnBasedSync::new());
548 let (record_and_trace_tx, record_and_trace_rx) =
549 sync_channel::<(usize, usize, TracesOrInput)>(
550 opts.recursion_opts.records_and_traces_channel_capacity,
551 );
552 let record_and_trace_tx = Arc::new(Mutex::new(record_and_trace_tx));
553 let record_and_trace_rx = Arc::new(Mutex::new(record_and_trace_rx));
554 let input_rx = Arc::new(Mutex::new(input_rx));
555 for _ in 0..opts.recursion_opts.trace_gen_workers {
556 let record_and_trace_sync = Arc::clone(&record_and_trace_sync);
557 let record_and_trace_tx = Arc::clone(&record_and_trace_tx);
558 let input_rx = Arc::clone(&input_rx);
559 let span = tracing::debug_span!("generate records and traces");
560 s.spawn(move || {
561 let _span = span.enter();
562 loop {
563 let received = { input_rx.lock().unwrap().recv() };
564 if let Ok((index, height, input, false)) = received {
565 let (program, witness_stream) = tracing::debug_span!(
567 "get program and witness stream"
568 )
569 .in_scope(|| match input {
570 SP1CircuitWitness::Core(input) => {
571 let mut witness_stream = Vec::new();
572 Witnessable::<InnerConfig>::write(&input, &mut witness_stream);
573 (self.recursion_program(&input), witness_stream)
574 }
575 SP1CircuitWitness::Deferred(input) => {
576 let mut witness_stream = Vec::new();
577 Witnessable::<InnerConfig>::write(&input, &mut witness_stream);
578 (self.deferred_program(&input), witness_stream)
579 }
580 SP1CircuitWitness::Compress(input) => {
581 let mut witness_stream = Vec::new();
582
583 let input_with_merkle = self.make_merkle_proofs(input);
584
585 Witnessable::<InnerConfig>::write(
586 &input_with_merkle,
587 &mut witness_stream,
588 );
589
590 (self.compress_program(&input_with_merkle), witness_stream)
591 }
592 });
593
594 let record = tracing::debug_span!("execute runtime").in_scope(|| {
596 let mut runtime =
597 RecursionRuntime::<Val<InnerSC>, Challenge<InnerSC>, _>::new(
598 program.clone(),
599 self.compress_prover.config().perm.clone(),
600 );
601 runtime.witness_stream = witness_stream.into();
602 runtime
603 .run()
604 .map_err(|e| {
605 SP1RecursionProverError::RuntimeError(e.to_string())
606 })
607 .unwrap();
608 runtime.record
609 });
610
611 let mut records = vec![record];
613 tracing::debug_span!("generate dependencies").in_scope(|| {
614 self.compress_prover.machine().generate_dependencies(
615 &mut records,
616 &opts.recursion_opts,
617 None,
618 )
619 });
620
621 let record = records.into_iter().next().unwrap();
623 let traces = tracing::debug_span!("generate traces")
624 .in_scope(|| self.compress_prover.generate_traces(&record));
625
626 record_and_trace_sync.wait_for_turn(index);
628
629 record_and_trace_tx
631 .lock()
632 .unwrap()
633 .send((
634 index,
635 height,
636 TracesOrInput::ProgramRecordTraces(Box::new((
637 program, record, traces,
638 ))),
639 ))
640 .unwrap();
641
642 record_and_trace_sync.advance_turn();
644 } else if let Ok((index, height, input, true)) = received {
645 record_and_trace_sync.wait_for_turn(index);
646
647 record_and_trace_tx
649 .lock()
650 .unwrap()
651 .send((
652 index,
653 height,
654 TracesOrInput::CircuitWitness(Box::new(input)),
655 ))
656 .unwrap();
657
658 record_and_trace_sync.advance_turn();
660 } else {
661 break;
662 }
663 }
664 });
665 }
666
667 let proofs_sync = Arc::new(TurnBasedSync::new());
669 let (proofs_tx, proofs_rx) =
670 sync_channel::<(usize, usize, StarkVerifyingKey<InnerSC>, ShardProof<InnerSC>)>(
671 num_first_layer_inputs * 2,
672 );
673 let proofs_tx = Arc::new(Mutex::new(proofs_tx));
674 let proofs_rx = Arc::new(Mutex::new(proofs_rx));
675 let mut prover_handles = Vec::new();
676 for _ in 0..opts.recursion_opts.shard_batch_size {
677 let prover_sync = Arc::clone(&proofs_sync);
678 let record_and_trace_rx = Arc::clone(&record_and_trace_rx);
679 let proofs_tx = Arc::clone(&proofs_tx);
680 let span = tracing::debug_span!("prove");
681 let handle = s.spawn(move || {
682 let _span = span.enter();
683 loop {
684 let received = { record_and_trace_rx.lock().unwrap().recv() };
685 if let Ok((index, height, TracesOrInput::ProgramRecordTraces(boxed_prt))) =
686 received
687 {
688 let (program, record, traces) = *boxed_prt;
689 tracing::debug_span!("batch").in_scope(|| {
690 let (pk, vk) = tracing::debug_span!("Setup compress program")
692 .in_scope(|| self.compress_prover.setup(&program));
693
694 let mut challenger = self.compress_prover.config().challenger();
696 tracing::debug_span!("observe proving key").in_scope(|| {
697 pk.observe_into(&mut challenger);
698 });
699
700 #[cfg(feature = "debug")]
701 self.compress_prover.debug_constraints(
702 &self.compress_prover.pk_to_host(&pk),
703 vec![record.clone()],
704 &mut challenger.clone(),
705 );
706
707 let data = tracing::debug_span!("commit")
709 .in_scope(|| self.compress_prover.commit(&record, traces));
710
711 let proof = tracing::debug_span!("open").in_scope(|| {
713 self.compress_prover.open(&pk, data, &mut challenger).unwrap()
714 });
715
716 #[cfg(feature = "debug")]
718 self.compress_prover
719 .machine()
720 .verify(
721 &vk,
722 &sp1_stark::MachineProof {
723 shard_proofs: vec![proof.clone()],
724 },
725 &mut self.compress_prover.config().challenger(),
726 )
727 .unwrap();
728
729 prover_sync.wait_for_turn(index);
731
732 proofs_tx.lock().unwrap().send((index, height, vk, proof)).unwrap();
734
735 prover_sync.advance_turn();
737 });
738 } else if let Ok((
739 index,
740 height,
741 TracesOrInput::CircuitWitness(witness_box),
742 )) = received
743 {
744 let witness = *witness_box;
745 if let SP1CircuitWitness::Compress(inner_witness) = witness {
746 let SP1CompressWitnessValues { vks_and_proofs, is_complete: _ } =
747 inner_witness;
748 assert!(vks_and_proofs.len() == 1);
749 let (vk, proof) = vks_and_proofs.last().unwrap();
750 prover_sync.wait_for_turn(index);
752
753 proofs_tx
755 .lock()
756 .unwrap()
757 .send((index, height, vk.clone(), proof.clone()))
758 .unwrap();
759
760 prover_sync.advance_turn();
762 }
763 } else {
764 break;
765 }
766 }
767 });
768 prover_handles.push(handle);
769 }
770
771 let handle = {
773 let input_tx = Arc::clone(&input_tx);
774 let proofs_rx = Arc::clone(&proofs_rx);
775 let span = tracing::debug_span!("generate next layer inputs");
776 s.spawn(move || {
777 let _span = span.enter();
778 let mut count = num_first_layer_inputs;
779 let mut batch: Vec<(
780 usize,
781 usize,
782 StarkVerifyingKey<InnerSC>,
783 ShardProof<InnerSC>,
784 )> = Vec::new();
785 loop {
786 if expected_height == 0 {
787 break;
788 }
789 let received = { proofs_rx.lock().unwrap().recv() };
790 if let Ok((index, height, vk, proof)) = received {
791 batch.push((index, height, vk, proof));
792
793 if batch.len() < batch_size {
795 continue;
796 }
797
798 let mut is_last = false;
800 if let Some(first) = batch.first() {
801 is_last = first.1 != height;
802 }
803
804 let inputs =
807 if is_last { vec![batch[0].clone()] } else { batch.clone() };
808
809 let next_input_height = inputs[0].1 + 1;
810
811 let is_complete = next_input_height == expected_height;
812
813 let vks_and_proofs = inputs
814 .into_iter()
815 .map(|(_, _, vk, proof)| (vk, proof))
816 .collect::<Vec<_>>();
817 let input = SP1CircuitWitness::Compress(SP1CompressWitnessValues {
818 vks_and_proofs,
819 is_complete,
820 });
821
822 input_sync.wait_for_turn(count);
823 input_tx
824 .lock()
825 .unwrap()
826 .send((count, next_input_height, input, is_last))
827 .unwrap();
828 input_sync.advance_turn();
829 count += 1;
830
831 if is_complete {
833 break;
834 }
835
836 if is_last {
839 batch = vec![batch[1].clone()];
840 } else {
841 batch = Vec::new();
842 }
843 } else {
844 break;
845 }
846 }
847 })
848 };
849
850 drop(input_tx);
852 drop(record_and_trace_tx);
853 drop(proofs_tx);
854
855 for handle in prover_handles {
856 handle.join().unwrap();
857 }
858 handle.join().unwrap();
859 tracing::debug!("joined handles");
860
861 let (_, _, vk, proof) = proofs_rx.lock().unwrap().recv().unwrap();
862 (vk, proof)
863 });
864
865 Ok(SP1ReduceProof { vk, proof })
866 }
867
868 #[instrument(name = "shrink", level = "info", skip_all)]
870 pub fn shrink(
871 &self,
872 reduced_proof: SP1ReduceProof<InnerSC>,
873 opts: SP1ProverOpts,
874 ) -> Result<SP1ReduceProof<InnerSC>, SP1RecursionProverError> {
875 let SP1ReduceProof { vk: compressed_vk, proof: compressed_proof } = reduced_proof;
877 let input = SP1CompressWitnessValues {
878 vks_and_proofs: vec![(compressed_vk.clone(), compressed_proof)],
879 is_complete: true,
880 };
881
882 let input_with_merkle = self.make_merkle_proofs(input);
883
884 let program =
885 self.shrink_program(ShrinkAir::<BabyBear>::shrink_shape(), &input_with_merkle);
886
887 let mut runtime = RecursionRuntime::<Val<InnerSC>, Challenge<InnerSC>, _>::new(
889 program.clone(),
890 self.shrink_prover.config().perm.clone(),
891 );
892
893 let mut witness_stream = Vec::new();
894 Witnessable::<InnerConfig>::write(&input_with_merkle, &mut witness_stream);
895
896 runtime.witness_stream = witness_stream.into();
897
898 runtime.run().map_err(|e| SP1RecursionProverError::RuntimeError(e.to_string()))?;
899
900 runtime.print_stats();
901 tracing::debug!("Shrink program executed successfully");
902
903 let (shrink_pk, shrink_vk) =
904 tracing::debug_span!("setup shrink").in_scope(|| self.shrink_prover.setup(&program));
905
906 let mut compress_challenger = self.shrink_prover.config().challenger();
908 let mut compress_proof = self
909 .shrink_prover
910 .prove(&shrink_pk, vec![runtime.record], &mut compress_challenger, opts.recursion_opts)
911 .unwrap();
912
913 Ok(SP1ReduceProof { vk: shrink_vk, proof: compress_proof.shard_proofs.pop().unwrap() })
914 }
915
916 #[instrument(name = "wrap_bn254", level = "info", skip_all)]
918 pub fn wrap_bn254(
919 &self,
920 compressed_proof: SP1ReduceProof<InnerSC>,
921 opts: SP1ProverOpts,
922 ) -> Result<SP1ReduceProof<OuterSC>, SP1RecursionProverError> {
923 let SP1ReduceProof { vk: compressed_vk, proof: compressed_proof } = compressed_proof;
924 let input = SP1CompressWitnessValues {
925 vks_and_proofs: vec![(compressed_vk, compressed_proof)],
926 is_complete: true,
927 };
928 let input_with_vk = self.make_merkle_proofs(input);
929
930 let program = self.wrap_program();
931
932 let mut runtime = RecursionRuntime::<Val<InnerSC>, Challenge<InnerSC>, _>::new(
934 program.clone(),
935 self.shrink_prover.config().perm.clone(),
936 );
937
938 let mut witness_stream = Vec::new();
939 Witnessable::<InnerConfig>::write(&input_with_vk, &mut witness_stream);
940
941 runtime.witness_stream = witness_stream.into();
942
943 runtime.run().map_err(|e| SP1RecursionProverError::RuntimeError(e.to_string()))?;
944
945 runtime.print_stats();
946 tracing::debug!("wrap program executed successfully");
947
948 let (wrap_pk, wrap_vk) =
950 tracing::debug_span!("setup wrap").in_scope(|| self.wrap_prover.setup(&program));
951
952 if self.wrap_vk.set(wrap_vk.clone()).is_ok() {
953 tracing::debug!("wrap verifier key set");
954 }
955
956 let mut wrap_challenger = self.wrap_prover.config().challenger();
958 let time = std::time::Instant::now();
959 let mut wrap_proof = self
960 .wrap_prover
961 .prove(&wrap_pk, vec![runtime.record], &mut wrap_challenger, opts.recursion_opts)
962 .unwrap();
963 let elapsed = time.elapsed();
964 tracing::debug!("wrap proving time: {:?}", elapsed);
965 let mut wrap_challenger = self.wrap_prover.config().challenger();
966 self.wrap_prover.machine().verify(&wrap_vk, &wrap_proof, &mut wrap_challenger).unwrap();
967 tracing::debug!("wrapping successful");
968
969 Ok(SP1ReduceProof { vk: wrap_vk, proof: wrap_proof.shard_proofs.pop().unwrap() })
970 }
971
972 #[instrument(name = "wrap_plonk_bn254", level = "info", skip_all)]
974 pub fn wrap_plonk_bn254(
975 &self,
976 proof: SP1ReduceProof<OuterSC>,
977 build_dir: &Path,
978 ) -> PlonkBn254Proof {
979 let input = SP1CompressWitnessValues {
980 vks_and_proofs: vec![(proof.vk.clone(), proof.proof.clone())],
981 is_complete: true,
982 };
983 let vkey_hash = sp1_vkey_digest_bn254(&proof);
984 let committed_values_digest = sp1_committed_values_digest_bn254(&proof);
985
986 let mut witness = Witness::default();
987 input.write(&mut witness);
988 witness.write_committed_values_digest(committed_values_digest);
989 witness.write_vkey_hash(vkey_hash);
990
991 let prover = PlonkBn254Prover::new();
992 let proof = prover.prove(witness, build_dir.to_path_buf());
993
994 prover
996 .verify(
997 &proof,
998 &vkey_hash.as_canonical_biguint(),
999 &committed_values_digest.as_canonical_biguint(),
1000 build_dir,
1001 )
1002 .unwrap();
1003
1004 proof
1005 }
1006
1007 #[instrument(name = "wrap_groth16_bn254", level = "info", skip_all)]
1009 pub fn wrap_groth16_bn254(
1010 &self,
1011 proof: SP1ReduceProof<OuterSC>,
1012 build_dir: &Path,
1013 ) -> Groth16Bn254Proof {
1014 let input = SP1CompressWitnessValues {
1015 vks_and_proofs: vec![(proof.vk.clone(), proof.proof.clone())],
1016 is_complete: true,
1017 };
1018 let vkey_hash = sp1_vkey_digest_bn254(&proof);
1019 let committed_values_digest = sp1_committed_values_digest_bn254(&proof);
1020
1021 let mut witness = Witness::default();
1022 input.write(&mut witness);
1023 witness.write_committed_values_digest(committed_values_digest);
1024 witness.write_vkey_hash(vkey_hash);
1025
1026 let prover = Groth16Bn254Prover::new();
1027 let proof = prover.prove(witness, build_dir.to_path_buf());
1028
1029 prover
1031 .verify(
1032 &proof,
1033 &vkey_hash.as_canonical_biguint(),
1034 &committed_values_digest.as_canonical_biguint(),
1035 build_dir,
1036 )
1037 .unwrap();
1038
1039 proof
1040 }
1041
1042 pub fn recursion_program(
1043 &self,
1044 input: &SP1RecursionWitnessValues<CoreSC>,
1045 ) -> Arc<RecursionProgram<BabyBear>> {
1046 let mut cache = self.lift_programs_lru.lock().unwrap_or_else(|e| e.into_inner());
1048 let shape = input.shape();
1049 let program = cache.get(&shape).cloned();
1050 drop(cache);
1051 match program {
1052 Some(program) => program,
1053 None => {
1054 let misses = self.lift_cache_misses.fetch_add(1, Ordering::Relaxed);
1055 tracing::debug!("core cache miss, misses: {}", misses);
1056 let builder_span = tracing::debug_span!("build recursion program").entered();
1058 let mut builder = Builder::<InnerConfig>::default();
1059
1060 let input =
1061 tracing::debug_span!("read input").in_scope(|| input.read(&mut builder));
1062 tracing::debug_span!("verify").in_scope(|| {
1063 SP1RecursiveVerifier::verify(&mut builder, self.core_prover.machine(), input)
1064 });
1065 let block =
1066 tracing::debug_span!("build block").in_scope(|| builder.into_root_block());
1067 builder_span.exit();
1068 let dsl_program = unsafe { DslIrProgram::new_unchecked(block) };
1071
1072 let compiler_span = tracing::debug_span!("compile recursion program").entered();
1074 let mut compiler = AsmCompiler::<InnerConfig>::default();
1075 let mut program = compiler.compile(dsl_program);
1076 if let Some(inn_recursion_shape_config) = &self.compress_shape_config {
1077 inn_recursion_shape_config.fix_shape(&mut program);
1078 }
1079 let program = Arc::new(program);
1080 compiler_span.exit();
1081
1082 let mut cache = self.lift_programs_lru.lock().unwrap_or_else(|e| e.into_inner());
1084 cache.put(shape, program.clone());
1085 drop(cache);
1086 program
1087 }
1088 }
1089 }
1090
1091 pub fn compress_program(
1092 &self,
1093 input: &SP1CompressWithVKeyWitnessValues<InnerSC>,
1094 ) -> Arc<RecursionProgram<BabyBear>> {
1095 self.join_programs_map.get(&input.shape()).cloned().unwrap_or_else(|| {
1096 tracing::warn!("join program not found in map, recomputing join program.");
1097 Arc::new(compress_program_from_input::<C>(
1099 self.compress_shape_config.as_ref(),
1100 &self.compress_prover,
1101 self.vk_verification,
1102 input,
1103 ))
1104 })
1105 }
1106
1107 pub fn shrink_program(
1108 &self,
1109 shrink_shape: RecursionShape,
1110 input: &SP1CompressWithVKeyWitnessValues<InnerSC>,
1111 ) -> Arc<RecursionProgram<BabyBear>> {
1112 let builder_span = tracing::debug_span!("build shrink program").entered();
1114 let mut builder = Builder::<InnerConfig>::default();
1115 let input = input.read(&mut builder);
1116 SP1CompressRootVerifierWithVKey::verify(
1118 &mut builder,
1119 self.compress_prover.machine(),
1120 input,
1121 self.vk_verification,
1122 PublicValuesOutputDigest::Reduce,
1123 );
1124 let block = builder.into_root_block();
1125 builder_span.exit();
1126 let dsl_program = unsafe { DslIrProgram::new_unchecked(block) };
1129
1130 let compiler_span = tracing::debug_span!("compile shrink program").entered();
1132 let mut compiler = AsmCompiler::<InnerConfig>::default();
1133 let mut program = compiler.compile(dsl_program);
1134
1135 *program.shape_mut() = Some(shrink_shape);
1136 let program = Arc::new(program);
1137 compiler_span.exit();
1138 program
1139 }
1140
1141 pub fn wrap_program(&self) -> Arc<RecursionProgram<BabyBear>> {
1142 self.wrap_program
1143 .get_or_init(|| {
1144 let builder_span = tracing::debug_span!("build compress program").entered();
1146 let mut builder = Builder::<WrapConfig>::default();
1147
1148 let shrink_shape: OrderedShape = ShrinkAir::<BabyBear>::shrink_shape().into();
1149 let input_shape = SP1CompressShape::from(vec![shrink_shape]);
1150 let shape = SP1CompressWithVkeyShape {
1151 compress_shape: input_shape,
1152 merkle_tree_height: self.recursion_vk_tree.height,
1153 };
1154 let dummy_input =
1155 SP1CompressWithVKeyWitnessValues::dummy(self.shrink_prover.machine(), &shape);
1156
1157 let input = dummy_input.read(&mut builder);
1158
1159 let root = input.merkle_var.root;
1161 for (val, expected) in root.iter().zip(self.recursion_vk_root.iter()) {
1162 builder.assert_felt_eq(*val, *expected);
1163 }
1164 SP1CompressRootVerifierWithVKey::verify(
1166 &mut builder,
1167 self.shrink_prover.machine(),
1168 input,
1169 self.vk_verification,
1170 PublicValuesOutputDigest::Root,
1171 );
1172
1173 let block = builder.into_root_block();
1174 builder_span.exit();
1175 let dsl_program = unsafe { DslIrProgram::new_unchecked(block) };
1178
1179 let compiler_span = tracing::debug_span!("compile compress program").entered();
1181 let mut compiler = AsmCompiler::<WrapConfig>::default();
1182 let program = Arc::new(compiler.compile(dsl_program));
1183 compiler_span.exit();
1184 program
1185 })
1186 .clone()
1187 }
1188
1189 pub fn deferred_program(
1190 &self,
1191 input: &SP1DeferredWitnessValues<InnerSC>,
1192 ) -> Arc<RecursionProgram<BabyBear>> {
1193 let operations_span =
1197 tracing::debug_span!("get operations for the deferred program").entered();
1198 let mut builder = Builder::<InnerConfig>::default();
1199 let input_read_span = tracing::debug_span!("Read input values").entered();
1200 let input = input.read(&mut builder);
1201 input_read_span.exit();
1202 let verify_span = tracing::debug_span!("Verify deferred program").entered();
1203
1204 SP1DeferredVerifier::verify(
1206 &mut builder,
1207 self.compress_prover.machine(),
1208 input,
1209 self.vk_verification,
1210 );
1211 verify_span.exit();
1212 let block = builder.into_root_block();
1213 operations_span.exit();
1214 let dsl_program = unsafe { DslIrProgram::new_unchecked(block) };
1217
1218 let compiler_span = tracing::debug_span!("compile deferred program").entered();
1219 let mut compiler = AsmCompiler::<InnerConfig>::default();
1220 let mut program = compiler.compile(dsl_program);
1221 if let Some(recursion_shape_config) = &self.compress_shape_config {
1222 recursion_shape_config.fix_shape(&mut program);
1223 }
1224 let program = Arc::new(program);
1225 compiler_span.exit();
1226 program
1227 }
1228
1229 pub fn get_recursion_core_inputs(
1230 &self,
1231 vk: &StarkVerifyingKey<CoreSC>,
1232 shard_proofs: &[ShardProof<CoreSC>],
1233 batch_size: usize,
1234 is_complete: bool,
1235 deferred_digest: [Val<CoreSC>; 8],
1236 ) -> Vec<SP1RecursionWitnessValues<CoreSC>> {
1237 let mut core_inputs = Vec::new();
1238
1239 for (batch_idx, batch) in shard_proofs.chunks(batch_size).enumerate() {
1241 let proofs = batch.to_vec();
1242
1243 core_inputs.push(SP1RecursionWitnessValues {
1244 vk: vk.clone(),
1245 shard_proofs: proofs.clone(),
1246 is_complete,
1247 is_first_shard: batch_idx == 0,
1248 vk_root: self.recursion_vk_root,
1249 reconstruct_deferred_digest: deferred_digest,
1250 });
1251 }
1252 core_inputs
1253 }
1254
1255 pub fn get_recursion_deferred_inputs_with_initial_digest<'a>(
1256 &'a self,
1257 vk: &'a StarkVerifyingKey<CoreSC>,
1258 deferred_proofs: &[SP1ReduceProof<InnerSC>],
1259 mut deferred_digest: [Val<CoreSC>; 8],
1260 batch_size: usize,
1261 ) -> (Vec<SP1DeferredWitnessValues<InnerSC>>, [BabyBear; 8]) {
1262 let mut deferred_inputs = Vec::new();
1264
1265 for batch in deferred_proofs.chunks(batch_size) {
1266 let vks_and_proofs =
1267 batch.iter().cloned().map(|proof| (proof.vk, proof.proof)).collect::<Vec<_>>();
1268
1269 let input = SP1CompressWitnessValues { vks_and_proofs, is_complete: true };
1270 let input = self.make_merkle_proofs(input);
1271 let SP1CompressWithVKeyWitnessValues { compress_val, merkle_val } = input;
1272
1273 deferred_inputs.push(SP1DeferredWitnessValues {
1274 vks_and_proofs: compress_val.vks_and_proofs,
1275 vk_merkle_data: merkle_val,
1276 start_reconstruct_deferred_digest: deferred_digest,
1277 is_complete: false,
1278 sp1_vk_digest: vk.hash_babybear(),
1279 end_pc: vk.pc_start,
1280 end_shard: BabyBear::one(),
1281 end_execution_shard: BabyBear::one(),
1282 init_addr_bits: [BabyBear::zero(); 32],
1283 finalize_addr_bits: [BabyBear::zero(); 32],
1284 committed_value_digest: [Word::<BabyBear>([BabyBear::zero(); 4]); 8],
1285 deferred_proofs_digest: [BabyBear::zero(); 8],
1286 });
1287
1288 deferred_digest = Self::hash_deferred_proofs(deferred_digest, batch);
1289 }
1290 (deferred_inputs, deferred_digest)
1291 }
1292
1293 pub fn get_recursion_deferred_inputs<'a>(
1294 &'a self,
1295 vk: &'a StarkVerifyingKey<CoreSC>,
1296 deferred_proofs: &[SP1ReduceProof<InnerSC>],
1297 batch_size: usize,
1298 ) -> (Vec<SP1DeferredWitnessValues<InnerSC>>, [BabyBear; 8]) {
1299 self.get_recursion_deferred_inputs_with_initial_digest(
1300 vk,
1301 deferred_proofs,
1302 [Val::<CoreSC>::zero(); DIGEST_SIZE],
1303 batch_size,
1304 )
1305 }
1306
1307 #[allow(clippy::type_complexity)]
1309 pub fn get_first_layer_inputs<'a>(
1310 &'a self,
1311 vk: &'a SP1VerifyingKey,
1312 shard_proofs: &[ShardProof<InnerSC>],
1313 deferred_proofs: &[SP1ReduceProof<InnerSC>],
1314 batch_size: usize,
1315 ) -> Vec<SP1CircuitWitness> {
1316 let (deferred_inputs, deferred_digest) =
1317 self.get_recursion_deferred_inputs(&vk.vk, deferred_proofs, batch_size);
1318
1319 let is_complete = shard_proofs.len() == 1 && deferred_proofs.is_empty();
1320 let core_inputs = self.get_recursion_core_inputs(
1321 &vk.vk,
1322 shard_proofs,
1323 batch_size,
1324 is_complete,
1325 deferred_digest,
1326 );
1327
1328 let mut inputs = Vec::new();
1329 inputs.extend(deferred_inputs.into_iter().map(SP1CircuitWitness::Deferred));
1330 inputs.extend(core_inputs.into_iter().map(SP1CircuitWitness::Core));
1331 inputs
1332 }
1333
1334 pub fn hash_deferred_proofs(
1336 prev_digest: [Val<CoreSC>; DIGEST_SIZE],
1337 deferred_proofs: &[SP1ReduceProof<InnerSC>],
1338 ) -> [Val<CoreSC>; 8] {
1339 let mut digest = prev_digest;
1340 for proof in deferred_proofs.iter() {
1341 let pv: &RecursionPublicValues<Val<CoreSC>> =
1342 proof.proof.public_values.as_slice().borrow();
1343 let committed_values_digest = words_to_bytes(&pv.committed_value_digest);
1344 digest = hash_deferred_proof(
1345 &digest,
1346 &pv.sp1_vk_digest,
1347 &committed_values_digest.try_into().unwrap(),
1348 );
1349 }
1350 digest
1351 }
1352
1353 pub fn make_merkle_proofs(
1354 &self,
1355 input: SP1CompressWitnessValues<CoreSC>,
1356 ) -> SP1CompressWithVKeyWitnessValues<CoreSC> {
1357 let num_vks = self.recursion_vk_map.len();
1358 let (vk_indices, vk_digest_values): (Vec<_>, Vec<_>) = if self.vk_verification {
1359 input
1360 .vks_and_proofs
1361 .iter()
1362 .map(|(vk, _)| {
1363 let vk_digest = vk.hash_babybear();
1364 let index = self.recursion_vk_map.get(&vk_digest).expect("vk not allowed");
1365 (index, vk_digest)
1366 })
1367 .unzip()
1368 } else {
1369 input
1370 .vks_and_proofs
1371 .iter()
1372 .map(|(vk, _)| {
1373 let vk_digest = vk.hash_babybear();
1374 let index = (vk_digest[0].as_canonical_u32() as usize) % num_vks;
1375 (index, [BabyBear::from_canonical_usize(index); 8])
1376 })
1377 .unzip()
1378 };
1379
1380 let proofs = vk_indices
1381 .iter()
1382 .map(|index| {
1383 let (_, proof) = MerkleTree::open(&self.recursion_vk_tree, *index);
1384 proof
1385 })
1386 .collect();
1387
1388 let merkle_val = SP1MerkleProofWitnessValues {
1389 root: self.recursion_vk_root,
1390 values: vk_digest_values,
1391 vk_merkle_proofs: proofs,
1392 };
1393
1394 SP1CompressWithVKeyWitnessValues { compress_val: input, merkle_val }
1395 }
1396
1397 fn check_for_high_cycles(cycles: u64) {
1398 if cycles > 100_000_000 {
1399 tracing::warn!(
1400 "High cycle count detected ({}M cycles). For better performance, consider using the Succinct Prover Network: https://docs.succinct.xyz/docs/sp1/generating-proofs/prover-network",
1401 cycles / 1_000_000
1402 );
1403 }
1404 }
1405}
1406
1407pub fn compress_program_from_input<C: SP1ProverComponents>(
1408 config: Option<&RecursionShapeConfig<BabyBear, CompressAir<BabyBear>>>,
1409 compress_prover: &C::CompressProver,
1410 vk_verification: bool,
1411 input: &SP1CompressWithVKeyWitnessValues<BabyBearPoseidon2>,
1412) -> RecursionProgram<BabyBear> {
1413 let builder_span = tracing::debug_span!("build compress program").entered();
1414 let mut builder = Builder::<InnerConfig>::default();
1415 let input = input.read(&mut builder);
1417 SP1CompressWithVKeyVerifier::verify(
1419 &mut builder,
1420 compress_prover.machine(),
1421 input,
1422 vk_verification,
1423 PublicValuesOutputDigest::Reduce,
1424 );
1425 let block = builder.into_root_block();
1426 builder_span.exit();
1427 let dsl_program = unsafe { DslIrProgram::new_unchecked(block) };
1430
1431 let compiler_span = tracing::debug_span!("compile compress program").entered();
1433 let mut compiler = AsmCompiler::<InnerConfig>::default();
1434 let mut program = compiler.compile(dsl_program);
1435 if let Some(config) = config {
1436 config.fix_shape(&mut program);
1437 }
1438 compiler_span.exit();
1439
1440 program
1441}
1442
1443#[cfg(test)]
1444pub mod tests {
1445 #![allow(clippy::print_stdout)]
1446
1447 use std::{
1448 collections::BTreeSet,
1449 fs::File,
1450 io::{Read, Write},
1451 };
1452
1453 use super::*;
1454
1455 use crate::build::try_build_plonk_bn254_artifacts_dev;
1456 use anyhow::Result;
1457 use build::{build_constraints_and_witness, try_build_groth16_bn254_artifacts_dev};
1458 use itertools::Itertools;
1459 use p3_field::PrimeField32;
1460
1461 use shapes::SP1ProofShape;
1462 use sp1_recursion_core::air::RecursionPublicValues;
1463
1464 #[cfg(test)]
1465 use serial_test::serial;
1466 #[cfg(test)]
1467 use sp1_core_machine::utils::setup_logger;
1468 use utils::sp1_vkey_digest_babybear;
1469
1470 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1471 pub enum Test {
1472 Core,
1473 Compress,
1474 Shrink,
1475 Wrap,
1476 CircuitTest,
1477 All,
1478 }
1479
1480 pub fn test_e2e_prover<C: SP1ProverComponents>(
1481 prover: &SP1Prover<C>,
1482 elf: &[u8],
1483 stdin: SP1Stdin,
1484 opts: SP1ProverOpts,
1485 test_kind: Test,
1486 ) -> Result<()> {
1487 run_e2e_prover_with_options(prover, elf, stdin, opts, test_kind, true)
1488 }
1489
1490 pub fn bench_e2e_prover<C: SP1ProverComponents>(
1491 prover: &SP1Prover<C>,
1492 elf: &[u8],
1493 stdin: SP1Stdin,
1494 opts: SP1ProverOpts,
1495 test_kind: Test,
1496 ) -> Result<()> {
1497 run_e2e_prover_with_options(prover, elf, stdin, opts, test_kind, false)
1498 }
1499
1500 pub fn run_e2e_prover_with_options<C: SP1ProverComponents>(
1501 prover: &SP1Prover<C>,
1502 elf: &[u8],
1503 stdin: SP1Stdin,
1504 opts: SP1ProverOpts,
1505 test_kind: Test,
1506 verify: bool,
1507 ) -> Result<()> {
1508 tracing::info!("initializing prover");
1509 let context = SP1Context::default();
1510
1511 tracing::info!("setup elf");
1512 let (_, pk_d, program, vk) = prover.setup(elf);
1513
1514 tracing::info!("prove core");
1515 let core_proof = prover.prove_core(&pk_d, program, &stdin, opts, context)?;
1516 let public_values = core_proof.public_values.clone();
1517
1518 if env::var("COLLECT_SHAPES").is_ok() {
1519 let mut shapes = BTreeSet::new();
1520 for proof in core_proof.proof.0.iter() {
1521 let shape = SP1ProofShape::Recursion(proof.shape());
1522 shapes.insert(shape);
1523 }
1524
1525 let mut file = File::create("../shapes.bin").unwrap();
1526 bincode::serialize_into(&mut file, &shapes).unwrap();
1527 }
1528
1529 if verify {
1530 tracing::info!("verify core");
1531 prover.verify(&core_proof.proof, &vk)?;
1532 }
1533
1534 if test_kind == Test::Core {
1535 return Ok(());
1536 }
1537
1538 tracing::info!("compress");
1539 let compress_span = tracing::debug_span!("compress").entered();
1540 let compressed_proof = prover.compress(&vk, core_proof, vec![], opts)?;
1541 compress_span.exit();
1542
1543 if verify {
1544 tracing::info!("verify compressed");
1545 prover.verify_compressed(&compressed_proof, &vk)?;
1546 }
1547
1548 if test_kind == Test::Compress {
1549 return Ok(());
1550 }
1551
1552 tracing::info!("shrink");
1553 let shrink_proof = prover.shrink(compressed_proof, opts)?;
1554
1555 if verify {
1556 tracing::info!("verify shrink");
1557 prover.verify_shrink(&shrink_proof, &vk)?;
1558 }
1559
1560 if test_kind == Test::Shrink {
1561 return Ok(());
1562 }
1563
1564 tracing::info!("wrap bn254");
1565 let wrapped_bn254_proof = prover.wrap_bn254(shrink_proof, opts)?;
1566 let bytes = bincode::serialize(&wrapped_bn254_proof).unwrap();
1567
1568 let mut file = File::create("proof-with-pis.bin").unwrap();
1570 file.write_all(bytes.as_slice()).unwrap();
1571
1572 let mut file = File::open("proof-with-pis.bin").unwrap();
1574 let mut bytes = Vec::new();
1575 file.read_to_end(&mut bytes).unwrap();
1576
1577 let wrapped_bn254_proof = bincode::deserialize(&bytes).unwrap();
1578
1579 if verify {
1580 tracing::info!("verify wrap bn254");
1581 prover.verify_wrap_bn254(&wrapped_bn254_proof, &vk).unwrap();
1582 }
1583
1584 if test_kind == Test::Wrap {
1585 return Ok(());
1586 }
1587
1588 tracing::info!("checking vkey hash babybear");
1589 let vk_digest_babybear = sp1_vkey_digest_babybear(&wrapped_bn254_proof);
1590 assert_eq!(vk_digest_babybear, vk.hash_babybear());
1591
1592 tracing::info!("checking vkey hash bn254");
1593 let vk_digest_bn254 = sp1_vkey_digest_bn254(&wrapped_bn254_proof);
1594 assert_eq!(vk_digest_bn254, vk.hash_bn254());
1595
1596 tracing::info!("Test the outer Plonk circuit");
1597 let (constraints, witness) =
1598 build_constraints_and_witness(&wrapped_bn254_proof.vk, &wrapped_bn254_proof.proof);
1599 PlonkBn254Prover::test(constraints, witness);
1600 tracing::info!("Circuit test succeeded");
1601
1602 if test_kind == Test::CircuitTest {
1603 return Ok(());
1604 }
1605
1606 tracing::info!("generate plonk bn254 proof");
1607 let artifacts_dir = try_build_plonk_bn254_artifacts_dev(
1608 &wrapped_bn254_proof.vk,
1609 &wrapped_bn254_proof.proof,
1610 );
1611 let plonk_bn254_proof =
1612 prover.wrap_plonk_bn254(wrapped_bn254_proof.clone(), &artifacts_dir);
1613 println!("{plonk_bn254_proof:?}");
1614
1615 prover.verify_plonk_bn254(&plonk_bn254_proof, &vk, &public_values, &artifacts_dir)?;
1616
1617 tracing::info!("generate groth16 bn254 proof");
1618 let artifacts_dir = try_build_groth16_bn254_artifacts_dev(
1619 &wrapped_bn254_proof.vk,
1620 &wrapped_bn254_proof.proof,
1621 );
1622 let groth16_bn254_proof = prover.wrap_groth16_bn254(wrapped_bn254_proof, &artifacts_dir);
1623 println!("{groth16_bn254_proof:?}");
1624
1625 if verify {
1626 prover.verify_groth16_bn254(
1627 &groth16_bn254_proof,
1628 &vk,
1629 &public_values,
1630 &artifacts_dir,
1631 )?;
1632 }
1633
1634 Ok(())
1635 }
1636
1637 pub fn test_e2e_with_deferred_proofs_prover<C: SP1ProverComponents>(
1638 opts: SP1ProverOpts,
1639 ) -> Result<()> {
1640 let keccak_elf = test_artifacts::KECCAK256_ELF;
1642
1643 let verify_elf = test_artifacts::VERIFY_PROOF_ELF;
1645
1646 tracing::info!("initializing prover");
1647 let prover = SP1Prover::<C>::new();
1648
1649 tracing::info!("setup keccak elf");
1650 let (_, keccak_pk_d, keccak_program, keccak_vk) = prover.setup(keccak_elf);
1651
1652 tracing::info!("setup verify elf");
1653 let (_, verify_pk_d, verify_program, verify_vk) = prover.setup(verify_elf);
1654
1655 tracing::info!("prove subproof 1");
1656 let mut stdin = SP1Stdin::new();
1657 stdin.write(&1usize);
1658 stdin.write(&vec![0u8, 0, 0]);
1659 let deferred_proof_1 = prover.prove_core(
1660 &keccak_pk_d,
1661 keccak_program.clone(),
1662 &stdin,
1663 opts,
1664 Default::default(),
1665 )?;
1666 let pv_1 = deferred_proof_1.public_values.as_slice().to_vec().clone();
1667
1668 tracing::info!("prove subproof 2");
1670 let mut stdin = SP1Stdin::new();
1671 stdin.write(&3usize);
1672 stdin.write(&vec![0u8, 1, 2]);
1673 stdin.write(&vec![2, 3, 4]);
1674 stdin.write(&vec![5, 6, 7]);
1675 let deferred_proof_2 =
1676 prover.prove_core(&keccak_pk_d, keccak_program, &stdin, opts, Default::default())?;
1677 let pv_2 = deferred_proof_2.public_values.as_slice().to_vec().clone();
1678
1679 tracing::info!("compress subproof 1");
1681 let deferred_reduce_1 = prover.compress(&keccak_vk, deferred_proof_1, vec![], opts)?;
1682 prover.verify_compressed(&deferred_reduce_1, &keccak_vk)?;
1683
1684 tracing::info!("compress subproof 2");
1686 let deferred_reduce_2 = prover.compress(&keccak_vk, deferred_proof_2, vec![], opts)?;
1687 prover.verify_compressed(&deferred_reduce_2, &keccak_vk)?;
1688
1689 let mut stdin = SP1Stdin::new();
1691 let vkey_digest = keccak_vk.hash_babybear();
1692 let vkey_digest: [u32; 8] = vkey_digest
1693 .iter()
1694 .map(|n| n.as_canonical_u32())
1695 .collect::<Vec<_>>()
1696 .try_into()
1697 .unwrap();
1698 stdin.write(&vkey_digest);
1699 stdin.write(&vec![pv_1.clone(), pv_2.clone(), pv_2.clone()]);
1700 stdin.write_proof(deferred_reduce_1.clone(), keccak_vk.vk.clone());
1701 stdin.write_proof(deferred_reduce_2.clone(), keccak_vk.vk.clone());
1702 stdin.write_proof(deferred_reduce_2.clone(), keccak_vk.vk.clone());
1703
1704 tracing::info!("proving verify program (core)");
1705 let verify_proof =
1706 prover.prove_core(&verify_pk_d, verify_program, &stdin, opts, Default::default())?;
1707 tracing::info!("compress verify program");
1711 let verify_reduce = prover.compress(
1712 &verify_vk,
1713 verify_proof,
1714 vec![deferred_reduce_1, deferred_reduce_2.clone(), deferred_reduce_2],
1715 opts,
1716 )?;
1717 let reduce_pv: &RecursionPublicValues<_> =
1718 verify_reduce.proof.public_values.as_slice().borrow();
1719 println!("deferred_hash: {:?}", reduce_pv.deferred_proofs_digest);
1720 println!("complete: {:?}", reduce_pv.is_complete);
1721
1722 tracing::info!("verify verify program");
1723 prover.verify_compressed(&verify_reduce, &verify_vk)?;
1724
1725 let shrink_proof = prover.shrink(verify_reduce, opts)?;
1726
1727 tracing::info!("verify shrink");
1728 prover.verify_shrink(&shrink_proof, &verify_vk)?;
1729
1730 tracing::info!("wrap bn254");
1731 let wrapped_bn254_proof = prover.wrap_bn254(shrink_proof, opts)?;
1732
1733 tracing::info!("verify wrap bn254");
1734 println!("verify wrap bn254 {:#?}", wrapped_bn254_proof.vk.commit);
1735 prover.verify_wrap_bn254(&wrapped_bn254_proof, &verify_vk).unwrap();
1736
1737 Ok(())
1738 }
1739
1740 #[test]
1747 #[serial]
1748 fn test_e2e() -> Result<()> {
1749 let elf = test_artifacts::FIBONACCI_ELF;
1750 setup_logger();
1751 let opts = SP1ProverOpts::auto();
1752 let prover = SP1Prover::<CpuProverComponents>::new();
1756 test_e2e_prover::<CpuProverComponents>(&prover, elf, SP1Stdin::default(), opts, Test::All)
1757 }
1758
1759 #[test]
1762 #[serial]
1763 fn test_e2e_with_deferred_proofs() -> Result<()> {
1764 setup_logger();
1765 test_e2e_with_deferred_proofs_prover::<CpuProverComponents>(SP1ProverOpts::auto())
1766 }
1767
1768 #[test]
1786 fn sp1_verifier_valid() {
1787 use sp1_verifier::compressed::internal::{
1788 self, COMPRESS_DEGREE, RECURSION_VK_ROOT, RECURSION_VK_SET,
1789 };
1790
1791 type F = BabyBear;
1793 type SC = BabyBearPoseidon2;
1794 let _: Option<internal::F> = Option::<F>::None;
1795 let _: Option<internal::SC> = Option::<SC>::None;
1796
1797 assert_eq!(COMPRESS_DEGREE, super::COMPRESS_DEGREE);
1799
1800 let prover = SP1Prover::<CpuProverComponents>::new();
1801
1802 assert_eq!(RECURSION_VK_ROOT.map(F::from_canonical_u32), prover.recursion_vk_root);
1804 assert_eq!(
1806 RECURSION_VK_SET.iter().find(|digest| !prover
1807 .recursion_vk_map
1808 .contains_key(&digest.map(F::from_canonical_u32))),
1809 None
1810 );
1811 assert!(RECURSION_VK_SET.is_sorted());
1813 assert!(RECURSION_VK_SET.iter().all_unique());
1814 }
1815}