1pub 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::RV32IM_SEAL_VERSION;
31use risc0_zkp::{
32 adapter::{CircuitInfo, PROOF_SYSTEM_INFO},
33 core::{
34 digest::{Digest, DIGEST_SHORTS},
35 hash::{hash_suite_from_name, poseidon2::Poseidon2HashSuite},
36 },
37 field::baby_bear::{BabyBear, BabyBearElem, BabyBearExtElem},
38 hal::{CircuitHal, Hal},
39 verify::ReadIOP,
40};
41use serde::Serialize;
42
43use crate::{
44 receipt::{
45 merkle::{MerkleGroup, MerkleProof},
46 SegmentReceipt, SuccinctReceipt, SuccinctReceiptVerifierParameters,
47 },
48 receipt_claim::{Assumption, MaybePruned, Merge, UnionClaim},
49 sha::Digestible,
50 ProverOpts, ReceiptClaim, Unknown,
51};
52
53use risc0_circuit_recursion::prove::Program;
54
55pub const RECURSION_PO2: usize = 18;
57
58pub(crate) type ZkrRegistryEntry = Box<dyn Fn() -> Result<Program> + Send + 'static>;
59
60pub(crate) type ZkrRegistry = BTreeMap<Digest, ZkrRegistryEntry>;
61
62pub(crate) static ZKR_REGISTRY: Mutex<ZkrRegistry> = Mutex::new(BTreeMap::new());
64
65pub fn lift(segment_receipt: &SegmentReceipt) -> Result<SuccinctReceipt<ReceiptClaim>> {
72 tracing::debug!("Proving lift: claim = {:#?}", segment_receipt.claim);
73 let opts = ProverOpts::succinct();
74 let mut prover = Prover::new_lift(segment_receipt, opts.clone())?;
75
76 let receipt = prover.prover.run()?;
77 let mut out_stream: VecDeque<u32> = VecDeque::new();
78 out_stream.extend(receipt.output.iter());
79 let claim_decoded = ReceiptClaim::decode(&mut out_stream)?;
80 tracing::debug!("Proving lift finished: decoded claim = {claim_decoded:#?}");
81
82 let control_inclusion_proof = MerkleGroup::new(opts.control_ids.clone())?
84 .get_proof(&prover.control_id, opts.hash_suite()?.hashfn.as_ref())?;
85 Ok(SuccinctReceipt {
86 seal: receipt.seal,
87 hashfn: opts.hashfn,
88 control_id: prover.control_id,
89 control_inclusion_proof,
90 claim: claim_decoded.merge(&segment_receipt.claim)?.into(),
91 verifier_parameters: SuccinctReceiptVerifierParameters::default().digest(),
92 })
93}
94
95pub fn join(
100 a: &SuccinctReceipt<ReceiptClaim>,
101 b: &SuccinctReceipt<ReceiptClaim>,
102) -> Result<SuccinctReceipt<ReceiptClaim>> {
103 tracing::debug!("Proving join: a.claim = {:#?}", a.claim);
104 tracing::debug!("Proving join: b.claim = {:#?}", b.claim);
105
106 let opts = ProverOpts::succinct();
107 let mut prover = Prover::new_join(a, b, opts.clone())?;
108 let receipt = prover.prover.run()?;
109 let mut out_stream: VecDeque<u32> = VecDeque::new();
110 out_stream.extend(receipt.output.iter());
111
112 let ab_claim = ReceiptClaim {
114 pre: a.claim.as_value()?.pre.clone(),
115 post: b.claim.as_value()?.post.clone(),
116 exit_code: b.claim.as_value()?.exit_code,
117 input: a.claim.as_value()?.input.clone(),
118 output: b.claim.as_value()?.output.clone(),
119 };
120
121 let claim_decoded = ReceiptClaim::decode(&mut out_stream)?;
122 tracing::debug!("Proving join finished: decoded claim = {claim_decoded:#?}");
123
124 let control_inclusion_proof = MerkleGroup::new(opts.control_ids.clone())?
126 .get_proof(&prover.control_id, opts.hash_suite()?.hashfn.as_ref())?;
127 Ok(SuccinctReceipt {
128 seal: receipt.seal,
129 hashfn: opts.hashfn,
130 control_id: prover.control_id,
131 control_inclusion_proof,
132 claim: claim_decoded.merge(&ab_claim)?.into(),
133 verifier_parameters: SuccinctReceiptVerifierParameters::default().digest(),
134 })
135}
136
137pub fn union(
142 a: &SuccinctReceipt<Unknown>,
143 b: &SuccinctReceipt<Unknown>,
144) -> Result<SuccinctReceipt<UnionClaim>> {
145 let a_assumption = Assumption {
146 claim: a.claim.digest(),
147 control_root: a.control_root()?,
148 }
149 .digest();
150 let b_assumption = Assumption {
151 claim: b.claim.digest(),
152 control_root: b.control_root()?,
153 }
154 .digest();
155
156 let ((left_assumption, left_receipt), (right_assumption, right_receipt)) =
157 if a_assumption <= b_assumption {
158 ((a_assumption, a), (b_assumption, b))
159 } else {
160 ((b_assumption, b), (a_assumption, a))
161 };
162 tracing::debug!("Proving union: left assumption = {:#?}", left_assumption);
163 tracing::debug!("Proving union: right assumption = {:#?}", right_assumption);
164
165 let opts = ProverOpts::succinct();
166 let mut prover = Prover::new_union(left_receipt, right_receipt, opts.clone())?;
167 let receipt = prover.prover.run()?;
168
169 let claim = UnionClaim {
170 left: left_assumption,
171 right: right_assumption,
172 };
173
174 let control_inclusion_proof = MerkleGroup::new(opts.control_ids.clone())?
176 .get_proof(&prover.control_id, opts.hash_suite()?.hashfn.as_ref())?;
177 Ok(SuccinctReceipt {
178 seal: receipt.seal,
179 hashfn: opts.hashfn,
180 control_id: prover.control_id,
181 control_inclusion_proof,
182 claim: MaybePruned::Value(claim),
183 verifier_parameters: SuccinctReceiptVerifierParameters::default().digest(),
184 })
185}
186
187pub fn resolve<Claim>(
193 conditional: &SuccinctReceipt<ReceiptClaim>,
194 assumption: &SuccinctReceipt<Claim>,
195) -> Result<SuccinctReceipt<ReceiptClaim>>
196where
197 Claim: risc0_binfmt::Digestible + Debug + Clone + Serialize,
198{
199 tracing::debug!(
200 "Proving resolve: conditional.claim = {:#?}",
201 conditional.claim,
202 );
203 tracing::debug!(
204 "Proving resolve: assumption.claim = {:#?}",
205 assumption.claim,
206 );
207
208 let mut resolved_claim = conditional
211 .claim
212 .as_value()
213 .context("conditional receipt claim is pruned")?
214 .clone();
215 resolved_claim
219 .output
220 .as_value_mut()
221 .context("conditional receipt output is pruned")?
222 .as_mut()
223 .ok_or_else(|| anyhow!("conditional receipt has empty output and no assumptions"))?
224 .assumptions
225 .as_value_mut()
226 .context("conditional receipt assumptions are pruned")?
227 .0
228 .drain(..1)
229 .next()
230 .ok_or_else(|| anyhow!("cannot resolve assumption from receipt with no assumptions"))?;
231
232 let opts = ProverOpts::succinct();
233 let mut prover = Prover::new_resolve(conditional, assumption, opts.clone())?;
234 let receipt = prover.prover.run()?;
235 let mut out_stream: VecDeque<u32> = VecDeque::new();
236 out_stream.extend(receipt.output.iter());
237
238 let claim_decoded = ReceiptClaim::decode(&mut out_stream)?;
239 tracing::debug!("Proving resolve finished: decoded claim = {claim_decoded:#?}");
240
241 let control_inclusion_proof = MerkleGroup::new(opts.control_ids.clone())?
243 .get_proof(&prover.control_id, opts.hash_suite()?.hashfn.as_ref())?;
244 Ok(SuccinctReceipt {
245 seal: receipt.seal,
246 hashfn: opts.hashfn,
247 control_id: prover.control_id,
248 control_inclusion_proof,
249 claim: claim_decoded.merge(&resolved_claim)?.into(),
250 verifier_parameters: SuccinctReceiptVerifierParameters::default().digest(),
251 })
252}
253
254pub fn identity_p254(a: &SuccinctReceipt<ReceiptClaim>) -> Result<SuccinctReceipt<ReceiptClaim>> {
260 let opts = ProverOpts::succinct()
261 .with_hashfn("poseidon_254".to_string())
262 .with_control_ids(vec![BN254_IDENTITY_CONTROL_ID]);
263
264 let mut prover = Prover::new_identity(a, opts.clone())?;
265 let receipt = prover.prover.run()?;
266 let mut out_stream: VecDeque<u32> = VecDeque::new();
267 out_stream.extend(receipt.output.iter());
268 let claim = MaybePruned::Value(ReceiptClaim::decode(&mut out_stream)?).merge(&a.claim)?;
269
270 let hashfn = opts.hash_suite()?.hashfn;
272 let control_inclusion_proof = MerkleGroup::new(opts.control_ids.clone())?
273 .get_proof(&prover.control_id, hashfn.as_ref())?;
274 let control_root = control_inclusion_proof.root(&prover.control_id, hashfn.as_ref());
275 let params = SuccinctReceiptVerifierParameters {
276 control_root,
277 inner_control_root: Some(a.control_root()?),
278 proof_system_info: PROOF_SYSTEM_INFO,
279 circuit_info: CircuitImpl::CIRCUIT_INFO,
280 };
281 Ok(SuccinctReceipt {
282 seal: receipt.seal,
283 hashfn: opts.hashfn,
284 control_id: prover.control_id,
285 control_inclusion_proof,
286 claim,
287 verifier_parameters: params.digest(),
288 })
289}
290
291pub fn prove_zkr(
293 program: Program,
294 control_id: &Digest,
295 allowed_control_ids: Vec<Digest>,
296 input: &[u8],
297) -> Result<SuccinctReceipt<Unknown>> {
298 let opts = ProverOpts::succinct().with_control_ids(allowed_control_ids);
299 let mut prover = Prover::new(program, *control_id, opts.clone());
300 prover.add_input(bytemuck::cast_slice(input));
301
302 tracing::debug!("Running prover");
303 let receipt = prover.run()?;
304
305 tracing::trace!("zkr receipt: {receipt:?}");
306
307 let claim_digest: Digest = read_sha_halfs(&mut VecDeque::from_iter(
309 bytemuck::checked::cast_slice::<_, BabyBearElem>(
310 &receipt.seal[DIGEST_SHORTS..2 * DIGEST_SHORTS],
311 )
312 .iter()
313 .copied()
314 .map(u32::from),
315 ))?;
316
317 let hashfn = opts.hash_suite()?.hashfn;
318 let control_inclusion_proof =
319 MerkleGroup::new(opts.control_ids.clone())?.get_proof(control_id, hashfn.as_ref())?;
320
321 Ok(SuccinctReceipt {
322 seal: receipt.seal,
323 hashfn: opts.hashfn,
324 control_id: *control_id,
325 control_inclusion_proof,
326 claim: MaybePruned::<Unknown>::Pruned(claim_digest),
327 verifier_parameters: SuccinctReceiptVerifierParameters::default().digest(),
328 })
329}
330
331pub fn prove_registered_zkr(
333 control_id: &Digest,
334 allowed_control_ids: Vec<Digest>,
335 input: &[u8],
336) -> Result<SuccinctReceipt<Unknown>> {
337 let zkr = get_registered_zkr(control_id)?;
338 prove_zkr(zkr, control_id, allowed_control_ids, input)
339}
340
341pub fn register_zkr(
343 control_id: &Digest,
344 get_program_fn: impl Fn() -> Result<Program> + Send + 'static,
345) -> Option<ZkrRegistryEntry> {
346 let mut registry = ZKR_REGISTRY.lock().unwrap();
347 registry.insert(*control_id, Box::new(get_program_fn))
348}
349
350pub fn get_registered_zkr(control_id: &Digest) -> Result<Program> {
352 let registry = ZKR_REGISTRY.lock().unwrap();
353 registry
354 .get(control_id)
355 .map(|f| f())
356 .unwrap_or_else(|| bail!("Control id {control_id} unregistered"))
357}
358
359#[cfg(test)]
364pub fn test_zkr(
365 digest1: &Digest,
366 digest2: &Digest,
367 po2: usize,
368) -> Result<SuccinctReceipt<crate::receipt_claim::Unknown>> {
369 use risc0_circuit_recursion::prove::zkr::get_zkr;
370 use risc0_zkp::core::hash::poseidon2::Poseidon2HashSuite;
371
372 let program = get_zkr("test_recursion_circuit.zkr", po2)?;
373 let suite = Poseidon2HashSuite::new_suite();
374 let control_id = program.compute_control_id(suite.clone());
375 let opts = ProverOpts::succinct().with_control_ids(vec![control_id]);
376
377 let mut prover = Prover::new(program, control_id, opts.clone());
378 prover.add_input_digest(digest1, DigestKind::Poseidon2);
379 prover.add_input_digest(digest2, DigestKind::Poseidon2);
380
381 let receipt = prover.run()?;
382
383 let claim_digest = read_sha_halfs(&mut VecDeque::from_iter(
385 bytemuck::checked::cast_slice::<_, BabyBearElem>(
386 &receipt.seal[DIGEST_SHORTS..2 * DIGEST_SHORTS],
387 )
388 .iter()
389 .copied()
390 .map(u32::from),
391 ))?;
392
393 let hashfn = opts.hash_suite()?.hashfn;
395 let control_inclusion_proof = MerkleGroup::new(opts.control_ids.clone())?
396 .get_proof(&prover.control_id, hashfn.as_ref())?;
397 let control_root = control_inclusion_proof.root(&prover.control_id, hashfn.as_ref());
398 let params = SuccinctReceiptVerifierParameters {
399 control_root,
400 inner_control_root: Some(digest1.to_owned()),
401 proof_system_info: PROOF_SYSTEM_INFO,
402 circuit_info: CircuitImpl::CIRCUIT_INFO,
403 };
404 Ok(SuccinctReceipt {
405 seal: receipt.seal,
406 hashfn: suite.name,
407 control_id: prover.control_id,
408 control_inclusion_proof,
409 claim: MaybePruned::Pruned(claim_digest),
410 verifier_parameters: params.digest(),
411 })
412}
413
414pub struct Prover {
416 prover: risc0_circuit_recursion::prove::Prover,
417 control_id: Digest,
418}
419
420impl Prover {
421 pub(crate) fn new(program: Program, control_id: Digest, opts: ProverOpts) -> Self {
422 Self {
423 prover: risc0_circuit_recursion::prove::Prover::new(program, &opts.hashfn),
424 control_id,
425 }
426 }
427
428 pub fn control_id(&self) -> &Digest {
430 &self.control_id
431 }
432
433 pub fn new_test_recursion_circuit(digests: [&Digest; 2], opts: ProverOpts) -> Result<Self> {
436 let (program, control_id) = zkr::test_recursion_circuit(&opts.hashfn)?;
437 let mut prover = Prover::new(program, control_id, opts);
438
439 for digest in digests {
440 prover.add_input_digest(digest, DigestKind::Poseidon2);
441 }
442
443 Ok(prover)
444 }
445
446 pub fn new_lift(segment: &SegmentReceipt, opts: ProverOpts) -> Result<Self> {
455 ensure!(
456 segment.hashfn == "poseidon2",
457 "lift recursion program only supports poseidon2 hashfn; received {}",
458 segment.hashfn
459 );
460
461 let inner_hash_suite = hash_suite_from_name(&segment.hashfn)
462 .ok_or_else(|| anyhow!("unsupported hash function: {}", segment.hashfn))?;
463 let allowed_ids = MerkleGroup::new(opts.control_ids.clone())?;
464 let merkle_root = allowed_ids.calc_root(inner_hash_suite.hashfn.as_ref());
465
466 let out_size = risc0_circuit_rv32im::CircuitImpl::OUTPUT_SIZE;
467
468 ensure!(
469 segment.seal[0] == RV32IM_SEAL_VERSION,
470 "seal version mismatch"
471 );
472
473 let seal = &segment.seal[1..];
474
475 let mut iop = ReadIOP::new(seal, inner_hash_suite.rng.as_ref());
478 iop.read_field_elem_slice::<BabyBearElem>(out_size);
479 let po2 = *iop.read_u32s(1).first().unwrap() as usize;
480
481 let (program, control_id) = zkr::lift(po2, &opts.hashfn)?;
483 let mut prover = Prover::new(program, control_id, opts);
484
485 prover.add_input_digest(&merkle_root, DigestKind::Poseidon2);
486 prover.add_input(seal);
487
488 Ok(prover)
489 }
490
491 pub fn new_union(
497 a: &SuccinctReceipt<Unknown>,
498 b: &SuccinctReceipt<Unknown>,
499 opts: ProverOpts,
500 ) -> Result<Self> {
501 ensure!(
502 a.hashfn == "poseidon2",
503 "union recursion program only supports poseidon2 hashfn; received {}",
504 a.hashfn
505 );
506 ensure!(
507 b.hashfn == "poseidon2",
508 "union recursion program only supports poseidon2 hashfn; received {}",
509 b.hashfn
510 );
511 let hash_suite = Poseidon2HashSuite::new_suite();
512 let allowed_ids = MerkleGroup::new(opts.control_ids.clone())?;
513 let merkle_root = allowed_ids.calc_root(hash_suite.hashfn.as_ref());
514
515 let (program, control_id) = zkr::union(&opts.hashfn)?;
516 let mut prover = Prover::new(program, control_id, opts);
517
518 prover.add_input_digest(&merkle_root, DigestKind::Poseidon2);
519 prover.add_succinct_receipt(a)?;
520 prover.add_succinct_receipt(b)?;
521 Ok(prover)
522 }
523
524 pub fn new_join(
530 a: &SuccinctReceipt<ReceiptClaim>,
531 b: &SuccinctReceipt<ReceiptClaim>,
532 opts: ProverOpts,
533 ) -> Result<Self> {
534 ensure!(
535 a.hashfn == "poseidon2",
536 "join recursion program only supports poseidon2 hashfn; received {}",
537 a.hashfn
538 );
539 ensure!(
540 b.hashfn == "poseidon2",
541 "join recursion program only supports poseidon2 hashfn; received {}",
542 b.hashfn
543 );
544
545 let (program, control_id) = zkr::join(&opts.hashfn)?;
546 let mut prover = Prover::new(program, control_id, opts);
547
548 let merkle_root = a.control_root()?;
552 ensure!(
553 merkle_root == b.control_root()?,
554 "merkle roots for a and b do not match: {} != {}",
555 merkle_root,
556 b.control_root()?
557 );
558
559 prover.add_input_digest(&merkle_root, DigestKind::Poseidon2);
560 prover.add_segment_receipt(a)?;
561 prover.add_segment_receipt(b)?;
562 Ok(prover)
563 }
564
565 pub fn new_resolve<Claim>(
572 cond: &SuccinctReceipt<ReceiptClaim>,
573 assum: &SuccinctReceipt<Claim>,
574 opts: ProverOpts,
575 ) -> Result<Self>
576 where
577 Claim: risc0_binfmt::Digestible + Debug + Clone + Serialize,
578 {
579 ensure!(
580 cond.hashfn == "poseidon2",
581 "resolve recursion program only supports poseidon2 hashfn; received {}",
582 cond.hashfn
583 );
584 ensure!(
585 assum.hashfn == "poseidon2",
586 "resolve recursion program only supports poseidon2 hashfn; received {}",
587 assum.hashfn
588 );
589
590 let (program, control_id) = zkr::resolve(&opts.hashfn)?;
592 let mut prover = Prover::new(program, control_id, opts);
593
594 prover.add_input_digest(&cond.control_root()?, DigestKind::Poseidon2);
598 prover.add_segment_receipt(cond)?;
599
600 let output = cond
601 .claim
602 .as_value()
603 .context("cannot resolve conditional receipt with pruned claim")?
604 .output
605 .as_value()
606 .context("cannot resolve conditional receipt with pruned output")?
607 .as_ref()
608 .ok_or_else(|| anyhow!("cannot resolve conditional receipt with no output"))?
609 .clone();
610
611 let assumptions = output
614 .assumptions
615 .value()
616 .context("cannot resolve conditional receipt with pruned assumptions")?;
617 let head: Assumption = assumptions
618 .0
619 .first()
620 .ok_or_else(|| anyhow!("cannot resolve conditional receipt with no assumptions"))?
621 .as_value()
622 .context("cannot resolve conditional receipt with pruned head assumption")?
623 .clone();
624
625 ensure!(
627 head.claim == assum.claim.digest(),
628 "assumption receipt claim does not match head of assumptions list"
629 );
630 let expected_root = match head.control_root == Digest::ZERO {
631 true => cond.control_root()?,
632 false => head.control_root,
633 };
634 ensure!(
635 expected_root == assum.control_root()?,
636 "assumption receipt control root does not match head of assumptions list"
637 );
638
639 let mut assumptions_tail = assumptions;
640 assumptions_tail.resolve(&head.digest())?;
641
642 prover.add_assumption_receipt(head, assum)?;
643 prover.add_input_digest(&assumptions_tail.digest(), DigestKind::Sha256);
644 prover.add_input_digest(&output.journal.digest(), DigestKind::Sha256);
645 Ok(prover)
646 }
647
648 pub fn new_identity(a: &SuccinctReceipt<ReceiptClaim>, opts: ProverOpts) -> Result<Self> {
653 ensure!(
654 a.hashfn == "poseidon2",
655 "identity recursion program only supports poseidon2 hashfn; received {}",
656 a.hashfn
657 );
658
659 let (program, control_id) = zkr::identity(&opts.hashfn)?;
660 let mut prover = Prover::new(program, control_id, opts);
661
662 prover.add_input_digest(&a.control_root()?, DigestKind::Poseidon2);
663 prover.add_segment_receipt(a)?;
664 Ok(prover)
665 }
666
667 pub(crate) fn add_input(&mut self, input: &[u32]) {
668 self.prover.add_input(input)
669 }
670
671 fn add_input_digest(&mut self, digest: &Digest, kind: DigestKind) {
673 self.prover.add_input_digest(digest, kind)
674 }
675
676 pub fn add_seal(
678 &mut self,
679 seal: &[u32],
680 control_id: &Digest,
681 control_inclusion_proof: &MerkleProof,
682 ) -> Result<()> {
683 tracing::debug!("Control ID = {:?}", control_id);
684 self.add_input(seal);
685 tracing::debug!("index = {:?}", control_inclusion_proof.index);
686 self.add_input(bytemuck::cast_slice(&[BabyBearElem::new(
687 control_inclusion_proof.index,
688 )]));
689 for digest in &control_inclusion_proof.digests {
690 tracing::debug!("path = {:?}", digest);
691 self.add_input_digest(digest, DigestKind::Poseidon2);
692 }
693 Ok(())
694 }
695
696 fn add_assumption_receipt<Claim>(
698 &mut self,
699 assumption: Assumption,
700 receipt: &SuccinctReceipt<Claim>,
701 ) -> Result<()>
702 where
703 Claim: risc0_binfmt::Digestible + Debug + Clone + Serialize,
704 {
705 self.add_seal(
706 &receipt.seal,
707 &receipt.control_id,
708 &receipt.control_inclusion_proof,
709 )?;
710 let zero_root = BabyBearElem::new((assumption.control_root == Digest::ZERO) as u32);
712 self.add_input(bytemuck::cast_slice(&[zero_root]));
713 Ok(())
714 }
715
716 fn add_segment_receipt(&mut self, a: &SuccinctReceipt<ReceiptClaim>) -> Result<()> {
718 self.add_seal(&a.seal, &a.control_id, &a.control_inclusion_proof)?;
719 let mut data = Vec::<u32>::new();
720 a.claim.as_value()?.encode(&mut data)?;
721 let data_fp: Vec<BabyBearElem> = data.iter().map(|x| BabyBearElem::new(*x)).collect();
722 self.add_input(bytemuck::cast_slice(&data_fp));
723 Ok(())
724 }
725
726 fn add_succinct_receipt<Claim>(&mut self, a: &SuccinctReceipt<Claim>) -> Result<()>
728 where
729 Claim: risc0_binfmt::Digestible + Debug + Clone + Serialize,
730 {
731 self.add_seal(&a.seal, &a.control_id, &a.control_inclusion_proof)?;
732 let zero_root = BabyBearElem::new((a.control_root()? == Digest::ZERO) as u32);
734 self.add_input(bytemuck::cast_slice(&[zero_root]));
735 Ok(())
736 }
737
738 pub fn run(&mut self) -> Result<RecursionReceipt> {
741 self.prover.run()
742 }
743
744 pub fn run_with_hal<H, C>(&mut self, hal: &H, circuit_hal: &C) -> Result<RecursionReceipt>
747 where
748 H: Hal<Field = BabyBear, Elem = BabyBearElem, ExtElem = BabyBearExtElem>,
749 C: CircuitHal<H>,
750 {
751 self.prover.run_with_hal(hal, circuit_hal)
752 }
753}