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