risc0_zkvm/host/server/prove/
mod.rs

1// Copyright 2025 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
15//! Run the zkVM guest and prove its results.
16
17pub(crate) mod dev_mode;
18pub(crate) mod keccak;
19mod prover_impl;
20#[cfg(test)]
21mod tests;
22pub(crate) mod union_peak;
23
24use std::rc::Rc;
25
26use anyhow::{anyhow, bail, ensure, Result};
27use risc0_core::field::baby_bear::{BabyBear, Elem, ExtElem};
28use risc0_groth16::prove::shrink_wrap;
29use risc0_zkp::hal::{CircuitHal, Hal};
30
31use self::{dev_mode::DevModeProver, prover_impl::ProverImpl};
32use crate::{
33    claim::{receipt::UnionClaim, Unknown},
34    host::prove_info::ProveInfo,
35    receipt::{
36        CompositeReceipt, Groth16Receipt, Groth16ReceiptVerifierParameters, InnerAssumptionReceipt,
37        InnerReceipt, SegmentReceipt, SuccinctReceipt,
38    },
39    sha::Digestible,
40    ExecutorEnv, PreflightResults, ProverOpts, Receipt, ReceiptClaim, ReceiptKind, Segment,
41    Session, VerifierContext, WorkClaim,
42};
43
44mod private {
45    use super::{dev_mode::DevModeProver, prover_impl::ProverImpl};
46
47    pub trait Sealed {}
48    impl Sealed for ProverImpl {}
49    impl Sealed for DevModeProver {}
50}
51
52/// A ProverServer can execute a given ELF binary and produce a [ProveInfo] which contains a
53/// [Receipt][crate::Receipt] that can be used to verify correct computation.
54pub trait ProverServer: private::Sealed {
55    /// Prove the specified ELF binary.
56    fn prove(&self, env: ExecutorEnv<'_>, elf: &[u8]) -> Result<ProveInfo> {
57        self.prove_with_ctx(env, &VerifierContext::default(), elf)
58    }
59
60    /// Prove the specified ELF binary using the specified [VerifierContext].
61    fn prove_with_ctx(
62        &self,
63        env: ExecutorEnv<'_>,
64        ctx: &VerifierContext,
65        elf: &[u8],
66    ) -> Result<ProveInfo>;
67
68    /// Prove the specified [Session].
69    fn prove_session(&self, ctx: &VerifierContext, session: &Session) -> Result<ProveInfo>;
70
71    /// Prove the specified [Segment].
72    fn prove_segment(&self, ctx: &VerifierContext, segment: &Segment) -> Result<SegmentReceipt> {
73        tracing::debug!("prove_segment");
74        let results = self.segment_preflight(segment)?;
75        self.prove_segment_core(ctx, results)
76    }
77
78    /// Run preflight on the specified [Segment].
79    fn segment_preflight(&self, segment: &Segment) -> Result<PreflightResults>;
80
81    /// Prove the specified [Segment] which has had preflight run on it.
82    fn prove_segment_core(
83        &self,
84        ctx: &VerifierContext,
85        preflight_results: PreflightResults,
86    ) -> Result<SegmentReceipt>;
87
88    /// Prove the specified keccak request
89    fn prove_keccak(&self, request: &crate::ProveKeccakRequest)
90        -> Result<SuccinctReceipt<Unknown>>;
91
92    /// Lift a [SegmentReceipt] into a [SuccinctReceipt]
93    fn lift(&self, receipt: &SegmentReceipt) -> Result<SuccinctReceipt<ReceiptClaim>>;
94
95    /// Join two [SuccinctReceipt] into a [SuccinctReceipt]
96    fn join(
97        &self,
98        a: &SuccinctReceipt<ReceiptClaim>,
99        b: &SuccinctReceipt<ReceiptClaim>,
100    ) -> Result<SuccinctReceipt<ReceiptClaim>>;
101
102    /// Unite two [SuccinctReceipt] into a [SuccinctReceipt]
103    fn union(
104        &self,
105        a: &SuccinctReceipt<Unknown>,
106        b: &SuccinctReceipt<Unknown>,
107    ) -> Result<SuccinctReceipt<UnionClaim>>;
108
109    /// Resolve an assumption from a conditional [SuccinctReceipt] by providing a [SuccinctReceipt]
110    /// proving the validity of the assumption.
111    fn resolve(
112        &self,
113        conditional: &SuccinctReceipt<ReceiptClaim>,
114        assumption: &SuccinctReceipt<Unknown>,
115    ) -> Result<SuccinctReceipt<ReceiptClaim>>;
116
117    /// Lift a [SegmentReceipt] into a [SuccinctReceipt] with a proof of verifiable work (PoVW) claim.
118    fn lift_povw(
119        &self,
120        receipt: &SegmentReceipt,
121    ) -> Result<SuccinctReceipt<WorkClaim<ReceiptClaim>>>;
122
123    /// Join two [SuccinctReceipt] with proof of verifiable work (PoVW) claims into a
124    /// [SuccinctReceipt] a PoVW claim.
125    fn join_povw(
126        &self,
127        a: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
128        b: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
129    ) -> Result<SuccinctReceipt<WorkClaim<ReceiptClaim>>>;
130
131    /// Join two [SuccinctReceipt] with proof of verifiable work (PoVW) claims into a
132    /// [SuccinctReceipt], and unwrap the result (see [ProverServer::unwrap_povw]).
133    fn join_unwrap_povw(
134        &self,
135        a: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
136        b: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
137    ) -> Result<SuccinctReceipt<ReceiptClaim>>;
138
139    /// Resolve an assumption from a conditional [SuccinctReceipt] with a proof of verifiable work
140    /// (PoVW) claim by providing a [SuccinctReceipt] proving the validity of the assumption.
141    fn resolve_povw(
142        &self,
143        conditional: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
144        assumption: &SuccinctReceipt<Unknown>,
145    ) -> Result<SuccinctReceipt<WorkClaim<ReceiptClaim>>>;
146
147    /// Resolve an assumption from a conditional [SuccinctReceipt] with a proof of verifiable work
148    /// (PoVW) claim by providing a [SuccinctReceipt] proving the validity of the assumption, and
149    /// unwrap the result (see [ProverServer::unwrap_povw]).
150    fn resolve_unwrap_povw(
151        &self,
152        conditional: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
153        assumption: &SuccinctReceipt<Unknown>,
154    ) -> Result<SuccinctReceipt<ReceiptClaim>>;
155
156    /// Remove the proof of verifiable work (PoVW) information from a [SuccinctReceipt] to produce
157    /// a [SuccinctReceipt] over the underlying claim.
158    fn unwrap_povw(
159        &self,
160        a: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
161    ) -> Result<SuccinctReceipt<ReceiptClaim>>;
162
163    /// Convert a [SuccinctReceipt] with a Poseidon hash function that uses a 254-bit field
164    fn identity_p254(
165        &self,
166        a: &SuccinctReceipt<ReceiptClaim>,
167    ) -> Result<SuccinctReceipt<ReceiptClaim>>;
168
169    /// Compress a [CompositeReceipt] into a single [SuccinctReceipt].
170    ///
171    /// A [CompositeReceipt] may contain an arbitrary number of receipts assembled into
172    /// segments and assumptions. Together, these receipts collectively prove a top-level
173    /// [ReceiptClaim](crate::ReceiptClaim). This function compresses all of the constituent receipts of a
174    /// [CompositeReceipt] into a single [SuccinctReceipt] that proves the same top-level claim. It
175    /// accomplishes this by iterative application of the recursion programs including lift, join,
176    /// and resolve.
177    fn composite_to_succinct(
178        &self,
179        receipt: &CompositeReceipt,
180    ) -> Result<SuccinctReceipt<ReceiptClaim>> {
181        <Self as Compress<_>>::composite_to_succinct(self, receipt)
182    }
183
184    /// Convert a composite receipt to a succinct work claim receipt.
185    fn composite_to_succinct_povw(
186        &self,
187        receipt: &CompositeReceipt,
188    ) -> Result<SuccinctReceipt<WorkClaim<ReceiptClaim>>> {
189        <Self as Compress<_>>::composite_to_succinct(self, receipt)
190    }
191
192    /// Compress a [SuccinctReceipt] into a [Groth16Receipt].
193    fn succinct_to_groth16(
194        &self,
195        receipt: &SuccinctReceipt<ReceiptClaim>,
196    ) -> Result<Groth16Receipt<ReceiptClaim>> {
197        let ident_receipt = self.identity_p254(receipt).unwrap();
198        let seal_bytes = ident_receipt.get_seal_bytes();
199        let seal = shrink_wrap(&seal_bytes)?.to_vec();
200        Ok(Groth16Receipt {
201            seal,
202            claim: receipt.claim.clone(),
203            verifier_parameters: Groth16ReceiptVerifierParameters::default().digest(),
204        })
205    }
206
207    /// Compress a receipt into one with a smaller representation.
208    ///
209    /// The requested target representation is determined by the [ReceiptKind] specified on the
210    /// provided [ProverOpts]. If the receipt is already at least as compressed as the requested
211    /// kind, this is a no-op.
212    fn compress(&self, opts: &ProverOpts, receipt: &Receipt) -> Result<Receipt> {
213        match &receipt.inner {
214            InnerReceipt::Composite(inner) => match opts.receipt_kind {
215                ReceiptKind::Composite => Ok(receipt.clone()),
216                ReceiptKind::Succinct => {
217                    let succinct_receipt = self.composite_to_succinct(inner)?;
218                    Ok(Receipt::new(
219                        InnerReceipt::Succinct(succinct_receipt),
220                        receipt.journal.bytes.clone(),
221                    ))
222                }
223                ReceiptKind::Groth16 => {
224                    let succinct_receipt = self.composite_to_succinct(inner)?;
225                    let groth16_receipt = self.succinct_to_groth16(&succinct_receipt)?;
226                    Ok(Receipt::new(
227                        InnerReceipt::Groth16(groth16_receipt),
228                        receipt.journal.bytes.clone(),
229                    ))
230                }
231            },
232            InnerReceipt::Succinct(inner) => match opts.receipt_kind {
233                ReceiptKind::Composite | ReceiptKind::Succinct => Ok(receipt.clone()),
234                ReceiptKind::Groth16 => {
235                    let groth16_receipt = self.succinct_to_groth16(inner)?;
236                    Ok(Receipt::new(
237                        InnerReceipt::Groth16(groth16_receipt),
238                        receipt.journal.bytes.clone(),
239                    ))
240                }
241            },
242            InnerReceipt::Groth16(_) => match opts.receipt_kind {
243                ReceiptKind::Composite | ReceiptKind::Succinct | ReceiptKind::Groth16 => {
244                    Ok(receipt.clone())
245                }
246            },
247            InnerReceipt::Fake(_) => {
248                ensure!(
249                    opts.dev_mode(),
250                    "dev mode must be enabled to compress fake receipts"
251                );
252                Ok(receipt.clone())
253            }
254        }
255    }
256}
257
258trait Lift<Claim> {
259    fn lift(&self, segment_receipt: &SegmentReceipt) -> anyhow::Result<SuccinctReceipt<Claim>>;
260}
261
262impl<P: ProverServer + ?Sized> Lift<ReceiptClaim> for P {
263    fn lift(
264        &self,
265        segment_receipt: &SegmentReceipt,
266    ) -> anyhow::Result<SuccinctReceipt<ReceiptClaim>> {
267        <Self as ProverServer>::lift(self, segment_receipt)
268    }
269}
270
271impl<P: ProverServer + ?Sized> Lift<WorkClaim<ReceiptClaim>> for P {
272    fn lift(
273        &self,
274        segment_receipt: &SegmentReceipt,
275    ) -> anyhow::Result<SuccinctReceipt<WorkClaim<ReceiptClaim>>> {
276        self.lift_povw(segment_receipt)
277    }
278}
279
280trait Join<Claim> {
281    fn join(
282        &self,
283        a: &SuccinctReceipt<Claim>,
284        b: &SuccinctReceipt<Claim>,
285    ) -> anyhow::Result<SuccinctReceipt<Claim>>;
286}
287
288impl<P: ProverServer + ?Sized> Join<ReceiptClaim> for P {
289    fn join(
290        &self,
291        a: &SuccinctReceipt<ReceiptClaim>,
292        b: &SuccinctReceipt<ReceiptClaim>,
293    ) -> anyhow::Result<SuccinctReceipt<ReceiptClaim>> {
294        <Self as ProverServer>::join(self, a, b)
295    }
296}
297
298impl<P: ProverServer + ?Sized> Join<WorkClaim<ReceiptClaim>> for P {
299    fn join(
300        &self,
301        a: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
302        b: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
303    ) -> anyhow::Result<SuccinctReceipt<WorkClaim<ReceiptClaim>>> {
304        self.join_povw(a, b)
305    }
306}
307
308trait Resolve<Claim> {
309    fn resolve(
310        &self,
311        cond: &SuccinctReceipt<Claim>,
312        assum: &SuccinctReceipt<Unknown>,
313    ) -> anyhow::Result<SuccinctReceipt<Claim>>;
314}
315
316impl<P: ProverServer + ?Sized> Resolve<ReceiptClaim> for P {
317    fn resolve(
318        &self,
319        cond: &SuccinctReceipt<ReceiptClaim>,
320        assum: &SuccinctReceipt<Unknown>,
321    ) -> anyhow::Result<SuccinctReceipt<ReceiptClaim>> {
322        <Self as ProverServer>::resolve(self, cond, assum)
323    }
324}
325
326impl<P: ProverServer + ?Sized> Resolve<WorkClaim<ReceiptClaim>> for P {
327    fn resolve(
328        &self,
329        cond: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
330        assum: &SuccinctReceipt<Unknown>,
331    ) -> anyhow::Result<SuccinctReceipt<WorkClaim<ReceiptClaim>>> {
332        self.resolve_povw(cond, assum)
333    }
334}
335
336trait Compress<Claim> {
337    fn composite_to_succinct(
338        &self,
339        composite_receipt: &CompositeReceipt,
340    ) -> anyhow::Result<SuccinctReceipt<Claim>>;
341}
342
343impl<P, Claim> Compress<Claim> for P
344where
345    P: Lift<Claim>
346        + Join<Claim>
347        + Resolve<Claim>
348        + Lift<ReceiptClaim>
349        + Join<ReceiptClaim>
350        + Resolve<ReceiptClaim>
351        + ?Sized,
352{
353    fn composite_to_succinct(
354        &self,
355        composite_receipt: &CompositeReceipt,
356    ) -> anyhow::Result<SuccinctReceipt<Claim>> {
357        // Compress all receipts in the top-level session into one succinct receipt for the session.
358        let continuation_receipt = composite_receipt
359            .segments
360            .iter()
361            .try_fold(
362                None,
363                |left: Option<SuccinctReceipt<Claim>>, right: &SegmentReceipt| -> Result<_> {
364                    Ok(Some(match left {
365                        Some(left) => self.join(&left, &self.lift(right)?)?,
366                        None => self.lift(right)?,
367                    }))
368                },
369            )?
370            .ok_or_else(|| {
371                anyhow!("malformed composite receipt has no continuation segment receipts")
372            })?;
373
374        // Compress assumptions and resolve them to get the final succinct receipt.
375        composite_receipt.assumption_receipts.iter().try_fold(
376            continuation_receipt,
377            |conditional: SuccinctReceipt<Claim>, assumption: &InnerAssumptionReceipt| match assumption {
378                InnerAssumptionReceipt::Succinct(assumption) => self.resolve(&conditional, assumption),
379                InnerAssumptionReceipt::Composite(assumption) => {
380                    self.resolve(&conditional, &SuccinctReceipt::<ReceiptClaim>::into_unknown(self.composite_to_succinct(assumption)?))
381                }
382                InnerAssumptionReceipt::Fake(_) => bail!(
383                    "compressing composite receipts with fake receipt assumptions is not supported"
384                ),
385                InnerAssumptionReceipt::Groth16(_) => bail!(
386                    "compressing composite receipts with Groth16 receipt assumptions is not supported"
387                )
388            },
389        )
390    }
391}
392
393/// A pair of [Hal] and [CircuitHal].
394#[derive(Clone)]
395pub struct HalPair<H, C>
396where
397    H: Hal<Field = BabyBear, Elem = Elem, ExtElem = ExtElem>,
398    C: CircuitHal<H>,
399{
400    /// A [Hal] implementation.
401    pub hal: Rc<H>,
402
403    /// An [CircuitHal] implementation.
404    pub circuit_hal: Rc<C>,
405}
406
407impl Session {
408    /// For each segment, call [ProverServer::prove_session] and collect the
409    /// receipts.
410    pub fn prove(&self) -> Result<ProveInfo> {
411        let prover = get_prover_server(&ProverOpts::default())?;
412        prover.prove_session(&VerifierContext::default(), self)
413    }
414}
415
416/// Select a [ProverServer] based on the specified [ProverOpts].
417pub fn get_prover_server(opts: &ProverOpts) -> Result<Rc<dyn ProverServer>> {
418    if opts.dev_mode() {
419        eprintln!("WARNING: proving in dev mode. This will not generate valid, secure proofs.");
420        return Ok(Rc::new(DevModeProver::new()));
421    }
422
423    Ok(Rc::new(ProverImpl::new(opts.clone())))
424}