sp1_prover/
lib.rs

1//! An end-to-end-prover implementation for the SP1 RISC-V zkVM.
2//!
3//! Separates the proof generation process into multiple stages:
4//!
5//! 1. Generate shard proofs which split up and prove the valid execution of a RISC-V program.
6//! 2. Compress shard proofs into a single shard proof.
7//! 3. Wrap the shard proof into a SNARK-friendly field.
8//! 4. Wrap the last shard proof, proven over the SNARK-friendly field, into a PLONK proof.
9
10#![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
97/// The global version for all components of SP1.
98///
99/// This string should be updated whenever any step in verifying an SP1 proof changes, including
100/// core, recursion, and plonk-bn254. This string is used to download SP1 artifacts and the gnark
101/// docker image.
102pub const SP1_CIRCUIT_VERSION: &str = include_str!("../SP1_VERSION");
103
104/// The configuration for the core prover.
105pub type CoreSC = BabyBearPoseidon2;
106
107/// The configuration for the inner prover.
108pub type InnerSC = BabyBearPoseidon2;
109
110/// The configuration for the outer prover.
111pub 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
129/// A end-to-end for the SP1 RISC-V zkVM.
130///
131/// This object coordinates the proving along all the steps: core, compression, shrinkage, and
132/// wrapping.
133pub struct SP1Prover<C: SP1ProverComponents = CpuProverComponents> {
134    /// The core prover.
135    pub core_prover: C::CoreProver,
136    /// The compress prover (for both lift and join).
137    pub compress_prover: C::CompressProver,
138    /// The shrink prover.
139    pub shrink_prover: C::ShrinkProver,
140    /// The wrap prover.
141    pub wrap_prover: C::WrapProver,
142    /// The cache of compiled recursion programs.
143    pub lift_programs_lru: Mutex<LruCache<SP1RecursionShape, Arc<RecursionProgram<BabyBear>>>>,
144    /// The number of cache misses for recursion programs.
145    pub lift_cache_misses: AtomicUsize,
146    /// The cache of compiled compression programs.
147    pub join_programs_map: BTreeMap<SP1CompressWithVkeyShape, Arc<RecursionProgram<BabyBear>>>,
148    /// The number of cache misses for compression programs.
149    pub join_cache_misses: AtomicUsize,
150    /// The root of the allowed recursion verification keys.
151    pub recursion_vk_root: <InnerSC as FieldHasher<BabyBear>>::Digest,
152    /// The allowed VKs and their corresponding indices.
153    pub recursion_vk_map: BTreeMap<<InnerSC as FieldHasher<BabyBear>>::Digest, usize>,
154    /// The Merkle tree for the allowed VKs.
155    pub recursion_vk_tree: MerkleTree<BabyBear, InnerSC>,
156    /// The core shape configuration.
157    pub core_shape_config: Option<CoreShapeConfig<BabyBear>>,
158    /// The recursion shape configuration.
159    pub compress_shape_config: Option<RecursionShapeConfig<BabyBear, CompressAir<BabyBear>>>,
160    /// The program for wrapping.
161    pub wrap_program: OnceLock<Arc<RecursionProgram<BabyBear>>>,
162    /// The verifying key for wrapping.
163    pub wrap_vk: OnceLock<StarkVerifyingKey<OuterSC>>,
164    /// Whether to verify verification keys.
165    pub vk_verification: bool,
166}
167
168impl<C: SP1ProverComponents> SP1Prover<C> {
169    /// Initializes a new [SP1Prover].
170    #[instrument(name = "initialize prover", level = "debug", skip_all)]
171    pub fn new() -> Self {
172        Self::uninitialized()
173    }
174
175    /// Creates a new [SP1Prover] with lazily initialized components.
176    pub fn uninitialized() -> Self {
177        // Initialize the provers.
178        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        // Read the shapes from the shapes directory and deserialize them into memory.
213        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    /// Creates a proving key and a verifying key for a given RISC-V ELF.
271    #[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    /// Get a program with an allowed preprocessed shape.
289    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    /// Execute an SP1 program with the specified inputs.
320    #[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            // Needed to figure out where the shard boundaries are.
342            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    /// Generate shard proofs which split up and prove the valid execution of a RISC-V program with
382    /// the core prover. Uses the provided context.
383    #[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        // Launch two threads to simultaneously prove the core and compile the first few
398        // recursion programs in parallel.
399        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                // Copy the proving key to the device.
410                let pk = pk_d;
411
412                // We may calculate gas while proving if the opts match the hardcoded variant.
413                // This ensures that the gas number is consistent between `execute` and `prove_core`.
414                // This behavior is undocumented because it is confusing and not very useful.
415                //
416                // If `context.calculate_gas` is set, we use the logic from the `gas` module
417                // after checkpoint execution to print gas as part of the execution report.
418                #[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                // Prove the core and stream the proofs and shapes.
442                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            // Receive the first few shapes and comile the recursion programs.
458            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                    // Only need to compile the recursion program if we're not in the one-shard
464                    // case.
465                    let compress_shape = SP1CompressProgramShape::Recursion(recursion_shape);
466
467                    // Insert the program into the cache.
468                    self.program_from_shape(compress_shape, None);
469                }
470            }
471
472            // Collect the shard proofs and the public values stream.
473            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    /// Reduce shards proofs to a single shard proof using the recursion prover.
487    #[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        // The batch size for reducing two layers of recursion.
508        let batch_size = REDUCE_BATCH_SIZE;
509        // The batch size for reducing the first layer of recursion.
510        let first_layer_batch_size = 1;
511
512        let shard_proofs = &proof.proof.0;
513
514        // Generate the first layer inputs.
515        let first_layer_inputs =
516            self.get_first_layer_inputs(vk, shard_proofs, &deferred_proofs, first_layer_batch_size);
517
518        // Calculate the expected height of the tree.
519        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        // Generate the proofs.
528        let span = tracing::Span::current().clone();
529        let (vk, proof) = thread::scope(|s| {
530            let _span = span.enter();
531
532            // Spawn a worker that sends the first layer inputs to a bounded channel.
533            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            // Spawn workers who generate the records and traces.
551            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                            // Get the program and witness stream.
570                            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                            // Execute the runtime.
599                            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                            // Generate the dependencies.
616                            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                            // Generate the traces.
626                            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                            // Wait for our turn to update the state.
631                            record_and_trace_sync.wait_for_turn(index);
632
633                            // Send the record and traces to the worker.
634                            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                            // Advance the turn.
647                            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                            // Send the record and traces to the worker.
652                            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                            // Advance the turn.
663                            record_and_trace_sync.advance_turn();
664                        } else {
665                            break;
666                        }
667                    }
668                });
669            }
670
671            // Spawn workers who generate the compress proofs.
672            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                                // Get the keys.
695                                let (pk, vk) = tracing::debug_span!("Setup compress program")
696                                    .in_scope(|| self.compress_prover.setup(&program));
697
698                                // Observe the proving key.
699                                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                                // Commit to the record and traces.
712                                let data = tracing::debug_span!("commit")
713                                    .in_scope(|| self.compress_prover.commit(&record, traces));
714
715                                // Generate the proof.
716                                let proof = tracing::debug_span!("open").in_scope(|| {
717                                    self.compress_prover.open(&pk, data, &mut challenger).unwrap()
718                                });
719
720                                // Verify the proof.
721                                #[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                                // Wait for our turn to update the state.
734                                prover_sync.wait_for_turn(index);
735
736                                // Send the proof.
737                                proofs_tx.lock().unwrap().send((index, height, vk, proof)).unwrap();
738
739                                // Advance the turn.
740                                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                                // Wait for our turn to update the state.
755                                prover_sync.wait_for_turn(index);
756
757                                // Send the proof.
758                                proofs_tx
759                                    .lock()
760                                    .unwrap()
761                                    .send((index, height, vk.clone(), proof.clone()))
762                                    .unwrap();
763
764                                // Advance the turn.
765                                prover_sync.advance_turn();
766                            }
767                        } else {
768                            break;
769                        }
770                    }
771                });
772                prover_handles.push(handle);
773            }
774
775            // Spawn a worker that generates inputs for the next layer.
776            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 we haven't reached the batch size, continue.
798                            if batch.len() < batch_size {
799                                continue;
800                            }
801
802                            // Compute whether we're at the last input of a layer.
803                            let mut is_last = false;
804                            if let Some(first) = batch.first() {
805                                is_last = first.1 != height;
806                            }
807
808                            // If we're at the last input of a layer, we need to only include the
809                            // first input, otherwise we include all inputs.
810                            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 we're at the root of the tree, stop generating inputs.
836                            if is_complete {
837                                break;
838                            }
839
840                            // If we were at the last input of a layer, we keep everything but the
841                            // first input. Otherwise, we empty the batch.
842                            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            // Wait for all the provers to finish.
855            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    /// Wrap a reduce proof into a STARK proven over a SNARK-friendly field.
873    #[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        // Make the compress proof.
880        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        // Run the compress program.
892        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        // Prove the compress program.
911        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    /// Wrap a reduce proof into a STARK proven over a SNARK-friendly field.
921    #[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        // Run the compress program.
937        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        // Setup the wrap program.
953        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        // Prove the wrap program.
961        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    /// Wrap the STARK proven over a SNARK-friendly field into a PLONK proof.
977    #[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        // Verify the proof.
999        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    /// Wrap the STARK proven over a SNARK-friendly field into a Groth16 proof.
1012    #[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        // Verify the proof.
1034        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        // Check if the program is in the cache.
1051        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                // Get the operations.
1061                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                // SAFETY: The circuit is well-formed. It does not use synchronization primitives
1073                // (or possibly other means) to violate the invariants.
1074                let dsl_program = unsafe { DslIrProgram::new_unchecked(block) };
1075
1076                // Compile the program.
1077                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                // Insert the program into the cache.
1087                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            // Get the operations.
1102            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        // Get the operations.
1117        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        // Verify the proof.
1121        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        // SAFETY: The circuit is well-formed. It does not use synchronization primitives
1131        // (or possibly other means) to violate the invariants.
1132        let dsl_program = unsafe { DslIrProgram::new_unchecked(block) };
1133
1134        // Compile the program.
1135        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                // Get the operations.
1149                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                // Attest that the merkle tree root is correct.
1164                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                // Verify the proof.
1169                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                // SAFETY: The circuit is well-formed. It does not use synchronization primitives
1180                // (or possibly other means) to violate the invariants.
1181                let dsl_program = unsafe { DslIrProgram::new_unchecked(block) };
1182
1183                // Compile the program.
1184                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        // Compile the program.
1198
1199        // Get the operations.
1200        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        // Verify the proof.
1209        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        // SAFETY: The circuit is well-formed. It does not use synchronization primitives
1219        // (or possibly other means) to violate the invariants.
1220        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        // Prepare the inputs for the recursion programs.
1244        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        // Prepare the inputs for the deferred proofs recursive verification.
1267        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    /// Generate the inputs for the first layer of recursive proofs.
1312    #[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    /// Accumulate deferred proofs into a single digest.
1339    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    // read the input.
1420    let input = input.read(&mut builder);
1421    // Verify the proof.
1422    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    // SAFETY: The circuit is well-formed. It does not use synchronization primitives
1432    // (or possibly other means) to violate the invariants.
1433    let dsl_program = unsafe { DslIrProgram::new_unchecked(block) };
1434
1435    // Compile the program.
1436    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        // Save the proof.
1572        let mut file = File::create("proof-with-pis.bin").unwrap();
1573        file.write_all(bytes.as_slice()).unwrap();
1574
1575        // Load the proof.
1576        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        // Test program which proves the Keccak-256 hash of various inputs.
1644        let keccak_elf = test_artifacts::KECCAK256_ELF;
1645
1646        // Test program which verifies proofs of a vkey and a list of committed inputs.
1647        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        // Generate a second proof of keccak of various inputs.
1672        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        // Generate recursive proof of first subproof.
1683        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        // Generate recursive proof of second subproof.
1688        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        // Run verify program with keccak vkey, subproofs, and their committed values.
1693        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        // let public_values = verify_proof.public_values.clone();
1711
1712        // Generate recursive proof of verify program
1713        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    /// Tests an end-to-end workflow of proving a program across the entire proof generation
1744    /// pipeline.
1745    ///
1746    /// Add `FRI_QUERIES`=1 to your environment for faster execution. Should only take a few minutes
1747    /// on a Mac M2. Note: This test always re-builds the plonk bn254 artifacts, so setting SP1_DEV
1748    /// is not needed.
1749    #[test]
1750    #[serial]
1751    fn test_e2e() -> Result<()> {
1752        let elf = test_artifacts::FIBONACCI_ELF;
1753        setup_logger();
1754        let opts = SP1ProverOpts::auto();
1755        // TODO(mattstam): We should Test::Plonk here, but this uses the existing
1756        // docker image which has a different API than the current. So we need to wait until the
1757        // next release (v1.2.0+), and then switch it back.
1758        let prover = SP1Prover::<CpuProverComponents>::new();
1759        test_e2e_prover::<CpuProverComponents>(&prover, elf, SP1Stdin::default(), opts, Test::All)
1760    }
1761
1762    /// Tests an end-to-end workflow of proving a program across the entire proof generation
1763    /// pipeline in addition to verifying deferred proofs.
1764    #[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}