risc0_zkvm/host/recursion/prove/
mod.rs

1// Copyright 2024 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15pub mod zkr;
16
17use std::{
18    collections::{BTreeMap, VecDeque},
19    fmt::Debug,
20    sync::Mutex,
21};
22
23use anyhow::{anyhow, bail, ensure, Context, Result};
24use risc0_binfmt::read_sha_halfs;
25use risc0_circuit_recursion::{
26    control_id::BN254_IDENTITY_CONTROL_ID,
27    prove::{DigestKind, RecursionReceipt},
28    CircuitImpl,
29};
30use risc0_circuit_rv32im::control_id::POSEIDON2_CONTROL_IDS;
31use risc0_zkp::{
32    adapter::{CircuitInfo, PROOF_SYSTEM_INFO},
33    core::{
34        digest::{Digest, DIGEST_SHORTS},
35        hash::hash_suite_from_name,
36    },
37    field::baby_bear::{BabyBear, BabyBearElem, BabyBearExtElem},
38    hal::{CircuitHal, Hal},
39    verify::ReadIOP,
40    MIN_CYCLES_PO2,
41};
42use serde::Serialize;
43
44use crate::{
45    receipt::{
46        merkle::{MerkleGroup, MerkleProof},
47        SegmentReceipt, SuccinctReceipt, SuccinctReceiptVerifierParameters,
48    },
49    receipt_claim::{Assumption, MaybePruned, Merge},
50    sha::Digestible,
51    ProverOpts, ReceiptClaim, Unknown,
52};
53
54use risc0_circuit_recursion::prove::Program;
55
56/// Number of rows to use for the recursion circuit witness as a power of 2.
57pub const RECURSION_PO2: usize = 18;
58
59pub(crate) type ZkrRegistryEntry = Box<dyn Fn() -> Result<Program> + Send + 'static>;
60
61pub(crate) type ZkrRegistry = BTreeMap<Digest, ZkrRegistryEntry>;
62
63/// A registry to look up programs by control ID.
64pub(crate) static ZKR_REGISTRY: Mutex<ZkrRegistry> = Mutex::new(BTreeMap::new());
65
66/// Run the lift program to transform an rv32im segment receipt into a recursion receipt.
67///
68/// The lift program verifies the rv32im circuit STARK proof inside the recursion circuit,
69/// resulting in a recursion circuit STARK proof. This recursion proof has a single
70/// constant-time verification procedure, with respect to the original segment length, and is then
71/// used as the input to all other recursion programs (e.g. join, resolve, and identity_p254).
72pub fn lift(segment_receipt: &SegmentReceipt) -> Result<SuccinctReceipt<ReceiptClaim>> {
73    tracing::debug!("Proving lift: claim = {:#?}", segment_receipt.claim);
74    let opts = ProverOpts::succinct();
75    let mut prover = Prover::new_lift(segment_receipt, opts.clone())?;
76
77    let receipt = prover.prover.run()?;
78    let mut out_stream = VecDeque::<u32>::new();
79    out_stream.extend(receipt.output.iter());
80    let claim_decoded = ReceiptClaim::decode(&mut out_stream)?;
81    tracing::debug!("Proving lift finished: decoded claim = {claim_decoded:#?}");
82
83    // Include an inclusion proof for control_id to allow verification against a root.
84    let control_inclusion_proof = MerkleGroup::new(opts.control_ids.clone())?
85        .get_proof(&prover.control_id, opts.hash_suite()?.hashfn.as_ref())?;
86    Ok(SuccinctReceipt {
87        seal: receipt.seal,
88        hashfn: opts.hashfn,
89        control_id: prover.control_id,
90        control_inclusion_proof,
91        claim: claim_decoded.merge(&segment_receipt.claim)?.into(),
92        verifier_parameters: SuccinctReceiptVerifierParameters::default().digest(),
93    })
94}
95
96/// Run the join program to compress two receipts of the same session into one.
97///
98/// By repeated application of the join program, any number of receipts for execution spans within
99/// the same session can be compressed into a single receipt for the entire session.
100pub fn join(
101    a: &SuccinctReceipt<ReceiptClaim>,
102    b: &SuccinctReceipt<ReceiptClaim>,
103) -> Result<SuccinctReceipt<ReceiptClaim>> {
104    tracing::debug!("Proving join: a.claim = {:#?}", a.claim);
105    tracing::debug!("Proving join: b.claim = {:#?}", b.claim);
106
107    let opts = ProverOpts::succinct();
108    let mut prover = Prover::new_join(a, b, opts.clone())?;
109    let receipt = prover.prover.run()?;
110    let mut out_stream = VecDeque::<u32>::new();
111    out_stream.extend(receipt.output.iter());
112
113    // Construct the expected claim that should have result from the join.
114    let ab_claim = ReceiptClaim {
115        pre: a.claim.as_value()?.pre.clone(),
116        post: b.claim.as_value()?.post.clone(),
117        exit_code: b.claim.as_value()?.exit_code,
118        input: a.claim.as_value()?.input.clone(),
119        output: b.claim.as_value()?.output.clone(),
120    };
121
122    let claim_decoded = ReceiptClaim::decode(&mut out_stream)?;
123    tracing::debug!("Proving join finished: decoded claim = {claim_decoded:#?}");
124
125    // Include an inclusion proof for control_id to allow verification against a root.
126    let control_inclusion_proof = MerkleGroup::new(opts.control_ids.clone())?
127        .get_proof(&prover.control_id, opts.hash_suite()?.hashfn.as_ref())?;
128    Ok(SuccinctReceipt {
129        seal: receipt.seal,
130        hashfn: opts.hashfn,
131        control_id: prover.control_id,
132        control_inclusion_proof,
133        claim: claim_decoded.merge(&ab_claim)?.into(),
134        verifier_parameters: SuccinctReceiptVerifierParameters::default().digest(),
135    })
136}
137
138/// Run the resolve program to remove an assumption from a conditional receipt upon verifying a
139/// receipt proving the validity of the assumption.
140///
141/// By applying the resolve program, a conditional receipt (i.e. a receipt for an execution using
142/// the `env::verify` API to logically verify a receipt) can be made into an unconditional receipt.
143pub fn resolve<Claim>(
144    conditional: &SuccinctReceipt<ReceiptClaim>,
145    assumption: &SuccinctReceipt<Claim>,
146) -> Result<SuccinctReceipt<ReceiptClaim>>
147where
148    Claim: risc0_binfmt::Digestible + Debug + Clone + Serialize,
149{
150    tracing::debug!(
151        "Proving resolve: conditional.claim = {:#?}",
152        conditional.claim,
153    );
154    tracing::debug!(
155        "Proving resolve: assumption.claim = {:#?}",
156        assumption.claim,
157    );
158
159    // Construct the resolved claim by copying the conditional receipt claim and resolving
160    // the head assumption. If this fails, then so would the resolve program.
161    let mut resolved_claim = conditional
162        .claim
163        .as_value()
164        .context("conditional receipt claim is pruned")?
165        .clone();
166    // Open the assumptions on the output of the claim and remove the first assumption.
167    // NOTE: Prover::new_resolve will check that the assumption can actually be resolved with the
168    // given receipts.
169    resolved_claim
170        .output
171        .as_value_mut()
172        .context("conditional receipt output is pruned")?
173        .as_mut()
174        .ok_or(anyhow!(
175            "conditional receipt has empty output and no assumptions"
176        ))?
177        .assumptions
178        .as_value_mut()
179        .context("conditional receipt assumptions are pruned")?
180        .0
181        .drain(..1)
182        .next()
183        .ok_or(anyhow!(
184            "cannot resolve assumption from receipt with no assumptions"
185        ))?;
186
187    let opts = ProverOpts::succinct();
188    let mut prover = Prover::new_resolve(conditional, assumption, opts.clone())?;
189    let receipt = prover.prover.run()?;
190    let mut out_stream = VecDeque::<u32>::new();
191    out_stream.extend(receipt.output.iter());
192
193    let claim_decoded = ReceiptClaim::decode(&mut out_stream)?;
194    tracing::debug!("Proving resolve finished: decoded claim = {claim_decoded:#?}");
195
196    // Include an inclusion proof for control_id to allow verification against a root.
197    let control_inclusion_proof = MerkleGroup::new(opts.control_ids.clone())?
198        .get_proof(&prover.control_id, opts.hash_suite()?.hashfn.as_ref())?;
199    Ok(SuccinctReceipt {
200        seal: receipt.seal,
201        hashfn: opts.hashfn,
202        control_id: prover.control_id,
203        control_inclusion_proof,
204        claim: claim_decoded.merge(&resolved_claim)?.into(),
205        verifier_parameters: SuccinctReceiptVerifierParameters::default().digest(),
206    })
207}
208
209/// Prove the verification of a recursion receipt using the Poseidon254 hash function for FRI.
210///
211/// The identity_p254 program is used as the last step in the prover pipeline before running the
212/// Groth16 prover. In Groth16 over BN254, it is much more efficient to verify a STARK that was
213/// produced with Poseidon over the BN254 base field compared to using Poseidon over BabyBear.
214pub fn identity_p254(a: &SuccinctReceipt<ReceiptClaim>) -> Result<SuccinctReceipt<ReceiptClaim>> {
215    let opts = ProverOpts::succinct()
216        .with_hashfn("poseidon_254".to_string())
217        .with_control_ids(vec![BN254_IDENTITY_CONTROL_ID]);
218
219    let mut prover = Prover::new_identity(a, opts.clone())?;
220    let receipt = prover.prover.run()?;
221    let mut out_stream = VecDeque::<u32>::new();
222    out_stream.extend(receipt.output.iter());
223    let claim = MaybePruned::Value(ReceiptClaim::decode(&mut out_stream)?).merge(&a.claim)?;
224
225    // Include an inclusion proof for control_id to allow verification against a root.
226    let hashfn = opts.hash_suite()?.hashfn;
227    let control_inclusion_proof = MerkleGroup::new(opts.control_ids.clone())?
228        .get_proof(&prover.control_id, hashfn.as_ref())?;
229    let control_root = control_inclusion_proof.root(&prover.control_id, hashfn.as_ref());
230    let params = SuccinctReceiptVerifierParameters {
231        control_root,
232        inner_control_root: Some(a.control_root()?),
233        proof_system_info: PROOF_SYSTEM_INFO,
234        circuit_info: CircuitImpl::CIRCUIT_INFO,
235    };
236    Ok(SuccinctReceipt {
237        seal: receipt.seal,
238        hashfn: opts.hashfn,
239        control_id: prover.control_id,
240        control_inclusion_proof,
241        claim,
242        verifier_parameters: params.digest(),
243    })
244}
245
246/// Prove the specified program identified by the `control_id` using the specified `input`.
247pub fn prove_zkr(
248    program: Program,
249    control_id: &Digest,
250    allowed_control_ids: Vec<Digest>,
251    input: &[u8],
252) -> Result<SuccinctReceipt<Unknown>> {
253    let opts = ProverOpts::succinct().with_control_ids(allowed_control_ids);
254    let mut prover = Prover::new(program, *control_id, opts.clone());
255    prover.add_input(bytemuck::cast_slice(input));
256
257    tracing::debug!("Running prover");
258    let receipt = prover.run()?;
259
260    tracing::trace!("zkr receipt: {receipt:?}");
261
262    // Read the claim digest from the second of the global output slots.
263    let claim_digest: Digest = read_sha_halfs(&mut VecDeque::from_iter(
264        bytemuck::checked::cast_slice::<_, BabyBearElem>(
265            &receipt.seal[DIGEST_SHORTS..2 * DIGEST_SHORTS],
266        )
267        .iter()
268        .copied()
269        .map(u32::from),
270    ))?;
271
272    let hashfn = opts.hash_suite()?.hashfn;
273    let control_inclusion_proof =
274        MerkleGroup::new(opts.control_ids.clone())?.get_proof(control_id, hashfn.as_ref())?;
275
276    Ok(SuccinctReceipt {
277        seal: receipt.seal,
278        hashfn: opts.hashfn,
279        control_id: *control_id,
280        control_inclusion_proof,
281        claim: MaybePruned::<Unknown>::Pruned(claim_digest),
282        verifier_parameters: SuccinctReceiptVerifierParameters::default().digest(),
283    })
284}
285
286/// Prove the specified program identified by the `control_id` using the specified `input`.
287pub fn prove_registered_zkr(
288    control_id: &Digest,
289    allowed_control_ids: Vec<Digest>,
290    input: &[u8],
291) -> Result<SuccinctReceipt<Unknown>> {
292    let zkr = get_registered_zkr(control_id)?;
293    prove_zkr(zkr, control_id, allowed_control_ids, input)
294}
295
296/// Registers a function to retrieve a recursion program (zkr) based on a control id.
297pub fn register_zkr(
298    control_id: &Digest,
299    get_program_fn: impl Fn() -> Result<Program> + Send + 'static,
300) -> Option<ZkrRegistryEntry> {
301    let mut registry = ZKR_REGISTRY.lock().unwrap();
302    registry.insert(*control_id, Box::new(get_program_fn))
303}
304
305/// Returns a registered ZKR program, or an error if not found.
306pub fn get_registered_zkr(control_id: &Digest) -> Result<Program> {
307    let registry = ZKR_REGISTRY.lock().unwrap();
308    registry
309        .get(control_id)
310        .map(|f| f())
311        .unwrap_or_else(|| bail!("Control id {control_id} unregistered"))
312}
313
314/// Prove the test_recursion_circuit. This is useful for testing purposes.
315///
316/// digest1 will be passed through to the first of the output globals, as the "inner control root".
317/// digest1 and digest2 will be used to calculate a "claim digest", placed in the second output.
318#[cfg(test)]
319pub fn test_zkr(
320    digest1: &Digest,
321    digest2: &Digest,
322    po2: usize,
323) -> Result<SuccinctReceipt<crate::receipt_claim::Unknown>> {
324    use risc0_circuit_recursion::prove::zkr::get_zkr;
325    use risc0_zkp::core::hash::poseidon2::Poseidon2HashSuite;
326
327    let program = get_zkr("test_recursion_circuit.zkr", po2)?;
328    let suite = Poseidon2HashSuite::new_suite();
329    let control_id = program.compute_control_id(suite.clone());
330    let opts = ProverOpts::succinct().with_control_ids(vec![control_id]);
331
332    let mut prover = Prover::new(program, control_id, opts.clone());
333    prover.add_input_digest(digest1, DigestKind::Poseidon2);
334    prover.add_input_digest(digest2, DigestKind::Poseidon2);
335
336    let receipt = prover.run()?;
337
338    // Read the claim digest from the second of the global output slots.
339    let claim_digest = risc0_binfmt::read_sha_halfs(&mut VecDeque::from_iter(
340        bytemuck::checked::cast_slice::<_, BabyBearElem>(
341            &receipt.seal[DIGEST_SHORTS..2 * DIGEST_SHORTS],
342        )
343        .iter()
344        .copied()
345        .map(u32::from),
346    ))?;
347
348    // Include an inclusion proof for control_id to allow verification against a root.
349    let hashfn = opts.hash_suite()?.hashfn;
350    let control_inclusion_proof = MerkleGroup::new(opts.control_ids.clone())?
351        .get_proof(&prover.control_id, hashfn.as_ref())?;
352    let control_root = control_inclusion_proof.root(&prover.control_id, hashfn.as_ref());
353    let params = SuccinctReceiptVerifierParameters {
354        control_root,
355        inner_control_root: Some(digest1.to_owned()),
356        proof_system_info: PROOF_SYSTEM_INFO,
357        circuit_info: CircuitImpl::CIRCUIT_INFO,
358    };
359    Ok(SuccinctReceipt {
360        seal: receipt.seal,
361        hashfn: suite.name,
362        control_id: prover.control_id,
363        control_inclusion_proof,
364        claim: MaybePruned::Pruned(claim_digest),
365        verifier_parameters: params.digest(),
366    })
367}
368
369/// Prover for zkVM use of the recursion circuit.
370pub struct Prover {
371    prover: risc0_circuit_recursion::prove::Prover,
372    control_id: Digest,
373}
374
375impl Prover {
376    pub(crate) fn new(program: Program, control_id: Digest, opts: ProverOpts) -> Self {
377        Self {
378            prover: risc0_circuit_recursion::prove::Prover::new(program, &opts.hashfn),
379            control_id,
380        }
381    }
382
383    /// Returns the control id of the recursion VM program being proven.
384    pub fn control_id(&self) -> &Digest {
385        &self.control_id
386    }
387
388    /// Initialize a recursion prover with the test recursion program. This program is used in
389    /// testing the basic correctness of the recursion circuit.
390    pub fn new_test_recursion_circuit(digests: [&Digest; 2], opts: ProverOpts) -> Result<Self> {
391        let (program, control_id) = zkr::test_recursion_circuit(&opts.hashfn)?;
392        let mut prover = Prover::new(program, control_id, opts);
393
394        for digest in digests {
395            prover.add_input_digest(digest, DigestKind::Poseidon2);
396        }
397
398        Ok(prover)
399    }
400
401    /// Initialize a recursion prover with the lift program to transform an rv32im segment receipt
402    /// into a recursion receipt.
403    ///
404    /// The lift program is verifies the rv32im circuit STARK proof inside the recursion circuit,
405    /// resulting in a recursion circuit STARK proof. This recursion proof has a single
406    /// constant-time verification procedure, with respect to the original segment length, and is
407    /// then used as the input to all other recursion programs (e.g. join, resolve, and
408    /// identity_p254).
409    pub fn new_lift(segment: &SegmentReceipt, opts: ProverOpts) -> Result<Self> {
410        ensure!(
411            segment.hashfn == "poseidon2",
412            "lift recursion program only supports poseidon2 hashfn; received {}",
413            segment.hashfn
414        );
415
416        let inner_hash_suite = hash_suite_from_name(&segment.hashfn)
417            .ok_or_else(|| anyhow!("unsupported hash function: {}", segment.hashfn))?;
418        let allowed_ids = MerkleGroup::new(opts.control_ids.clone())?;
419        let merkle_root = allowed_ids.calc_root(inner_hash_suite.hashfn.as_ref());
420
421        // Read the output fields in the rv32im seal to get the po2. We need this po2 to chose
422        // which lift program we are going to run.
423        let mut iop = ReadIOP::new(&segment.seal, inner_hash_suite.rng.as_ref());
424        iop.read_field_elem_slice::<BabyBearElem>(risc0_circuit_rv32im::CircuitImpl::OUTPUT_SIZE);
425        let po2 = *iop.read_u32s(1).first().unwrap() as usize;
426
427        // Instantiate the prover with the lift recursion program and its control ID.
428        let (program, control_id) = zkr::lift(po2, &opts.hashfn)?;
429        let mut prover = Prover::new(program, control_id, opts);
430
431        prover.add_input_digest(&merkle_root, DigestKind::Poseidon2);
432
433        // Get the control ID for the rv32im with the given po2. It must also be in the allowed IDs.
434        let which = po2 - MIN_CYCLES_PO2;
435        let inner_control_id = POSEIDON2_CONTROL_IDS[which];
436        prover.add_seal(
437            &segment.seal,
438            &inner_control_id,
439            &allowed_ids.get_proof(&inner_control_id, inner_hash_suite.hashfn.as_ref())?,
440        )?;
441
442        Ok(prover)
443    }
444
445    /// Initialize a recursion prover with the join program to compress two receipts of the same
446    /// session into one.
447    ///
448    /// By repeated application of the join program, any number of receipts for execution spans
449    /// within the same session can be compressed into a single receipt for the entire session.
450    pub fn new_join(
451        a: &SuccinctReceipt<ReceiptClaim>,
452        b: &SuccinctReceipt<ReceiptClaim>,
453        opts: ProverOpts,
454    ) -> Result<Self> {
455        ensure!(
456            a.hashfn == "poseidon2",
457            "join recursion program only supports poseidon2 hashfn; received {}",
458            a.hashfn
459        );
460        ensure!(
461            b.hashfn == "poseidon2",
462            "join recursion program only supports poseidon2 hashfn; received {}",
463            b.hashfn
464        );
465
466        let (program, control_id) = zkr::join(&opts.hashfn)?;
467        let mut prover = Prover::new(program, control_id, opts);
468
469        // Determine the control root from the receipts themselves, and ensure they are equal. If
470        // the determined control root does not match what the downstream verifier expects, they
471        // will reject.
472        let merkle_root = a.control_root()?;
473        ensure!(
474            merkle_root == b.control_root()?,
475            "merkle roots for a and b do not match: {} != {}",
476            merkle_root,
477            b.control_root()?
478        );
479
480        prover.add_input_digest(&merkle_root, DigestKind::Poseidon2);
481        prover.add_segment_receipt(a)?;
482        prover.add_segment_receipt(b)?;
483        Ok(prover)
484    }
485
486    /// Initialize a recursion prover with the resolve program to remove an assumption from a
487    /// conditional receipt upon verifying a receipt proving the validity of the assumption.
488    ///
489    /// By applying the resolve program, a conditional receipt (i.e. a receipt for an execution
490    /// using the `env::verify` API to logically verify a receipt) can be made into an
491    /// unconditional receipt.
492    pub fn new_resolve<Claim>(
493        cond: &SuccinctReceipt<ReceiptClaim>,
494        assum: &SuccinctReceipt<Claim>,
495        opts: ProverOpts,
496    ) -> Result<Self>
497    where
498        Claim: risc0_binfmt::Digestible + Debug + Clone + Serialize,
499    {
500        ensure!(
501            cond.hashfn == "poseidon2",
502            "resolve recursion program only supports poseidon2 hashfn; received {}",
503            cond.hashfn
504        );
505        ensure!(
506            assum.hashfn == "poseidon2",
507            "resolve recursion program only supports poseidon2 hashfn; received {}",
508            assum.hashfn
509        );
510
511        // Load the resolve predicate as a Program and construct the prover.
512        let (program, control_id) = zkr::resolve(&opts.hashfn)?;
513        let mut prover = Prover::new(program, control_id, opts);
514
515        // Load the input values needed by the predicate.
516        // Resolve predicate needs both seals as input, and the journal and assumptions tail digest
517        // to compute the opening of the conditional receipt claim to the first assumption.
518        prover.add_input_digest(&cond.control_root()?, DigestKind::Poseidon2);
519        prover.add_segment_receipt(cond)?;
520
521        let output = cond
522            .claim
523            .as_value()
524            .context("cannot resolve conditional receipt with pruned claim")?
525            .output
526            .as_value()
527            .context("cannot resolve conditional receipt with pruned output")?
528            .as_ref()
529            .ok_or(anyhow!("cannot resolve conditional receipt with no output"))?
530            .clone();
531
532        // Unwrap the MaybePruned assumptions list and resolve the corroborated assumption,
533        // removing the head and leaving the tail of the list.
534        let assumptions = output
535            .assumptions
536            .value()
537            .context("cannot resolve conditional receipt with pruned assumptions")?;
538        let head: Assumption = assumptions
539            .0
540            .first()
541            .ok_or(anyhow!(
542                "cannot resolve conditional receipt with no assumptions"
543            ))?
544            .as_value()
545            .context("cannot resolve conditional receipt with pruned head assumption")?
546            .clone();
547
548        // Ensure that the assumption receipt can resolve the assumption.
549        ensure!(
550            head.claim == assum.claim.digest(),
551            "assumption receipt claim does not match head of assumptions list"
552        );
553        let expected_root = match head.control_root == Digest::ZERO {
554            true => cond.control_root()?,
555            false => head.control_root,
556        };
557        ensure!(
558            expected_root == assum.control_root()?,
559            "assumption receipt control root does not match head of assumptions list"
560        );
561
562        let mut assumptions_tail = assumptions;
563        assumptions_tail.resolve(&head.digest())?;
564
565        prover.add_assumption_receipt(head, assum)?;
566        prover.add_input_digest(&assumptions_tail.digest(), DigestKind::Sha256);
567        prover.add_input_digest(&output.journal.digest(), DigestKind::Sha256);
568        Ok(prover)
569    }
570
571    /// Prove the verification of a recursion receipt, applying no changes to [ReceiptClaim].
572    ///
573    /// The primary use for this program is to transform the receipt itself, e.g. using a different
574    /// hash function for FRI. See [identity_p254] for more information.
575    pub fn new_identity(a: &SuccinctReceipt<ReceiptClaim>, opts: ProverOpts) -> Result<Self> {
576        ensure!(
577            a.hashfn == "poseidon2",
578            "identity recursion program only supports poseidon2 hashfn; received {}",
579            a.hashfn
580        );
581
582        let (program, control_id) = zkr::identity(&opts.hashfn)?;
583        let mut prover = Prover::new(program, control_id, opts);
584
585        prover.add_input_digest(&a.control_root()?, DigestKind::Poseidon2);
586        prover.add_segment_receipt(a)?;
587        Ok(prover)
588    }
589
590    pub(crate) fn add_input(&mut self, input: &[u32]) {
591        self.prover.add_input(input)
592    }
593
594    /// Add a digest to the input for the recursion program.
595    fn add_input_digest(&mut self, digest: &Digest, kind: DigestKind) {
596        self.prover.add_input_digest(digest, kind)
597    }
598
599    /// Add a recursion seal (i.e. STARK proof) to input tape of the recursion program.
600    pub fn add_seal(
601        &mut self,
602        seal: &[u32],
603        control_id: &Digest,
604        control_inclusion_proof: &MerkleProof,
605    ) -> Result<()> {
606        tracing::debug!("Control ID = {:?}", control_id);
607        self.add_input(seal);
608        tracing::debug!("index = {:?}", control_inclusion_proof.index);
609        self.add_input(bytemuck::cast_slice(&[BabyBearElem::new(
610            control_inclusion_proof.index,
611        )]));
612        for digest in &control_inclusion_proof.digests {
613            tracing::debug!("path = {:?}", digest);
614            self.add_input_digest(digest, DigestKind::Poseidon2);
615        }
616        Ok(())
617    }
618
619    /// Add a receipt covering some generic claim. Do not include any claim information.
620    fn add_assumption_receipt<Claim>(
621        &mut self,
622        assumption: Assumption,
623        receipt: &SuccinctReceipt<Claim>,
624    ) -> Result<()>
625    where
626        Claim: risc0_binfmt::Digestible + Debug + Clone + Serialize,
627    {
628        self.add_seal(
629            &receipt.seal,
630            &receipt.control_id,
631            &receipt.control_inclusion_proof,
632        )?;
633        // Resolve program expects an additional boolean to tell it when the control root is zero.
634        let zero_root = BabyBearElem::new((assumption.control_root == Digest::ZERO) as u32);
635        self.add_input(bytemuck::cast_slice(&[zero_root]));
636        Ok(())
637    }
638
639    /// Add a receipt covering rv32im execution, and include the first level of ReceiptClaim.
640    fn add_segment_receipt(&mut self, a: &SuccinctReceipt<ReceiptClaim>) -> Result<()> {
641        self.add_seal(&a.seal, &a.control_id, &a.control_inclusion_proof)?;
642        let mut data = Vec::<u32>::new();
643        a.claim.as_value()?.encode(&mut data)?;
644        let data_fp: Vec<BabyBearElem> = data.iter().map(|x| BabyBearElem::new(*x)).collect();
645        self.add_input(bytemuck::cast_slice(&data_fp));
646        Ok(())
647    }
648
649    /// Run the prover, producing a receipt of execution for the recursion circuit over the loaded
650    /// program and input.
651    pub fn run(&mut self) -> Result<RecursionReceipt> {
652        self.prover.run()
653    }
654
655    /// Run the prover, producing a receipt of execution for the recursion circuit over the loaded
656    /// program and input, using the specified HAL.
657    pub fn run_with_hal<H, C>(&mut self, hal: &H, circuit_hal: &C) -> Result<RecursionReceipt>
658    where
659        H: Hal<Field = BabyBear, Elem = BabyBearElem, ExtElem = BabyBearExtElem>,
660        C: CircuitHal<H>,
661    {
662        self.prover.run_with_hal(hal, circuit_hal)
663    }
664}