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_zkp::hal::{CircuitHal, Hal};
29
30use self::{dev_mode::DevModeProver, prover_impl::ProverImpl};
31use crate::{
32    host::prove_info::ProveInfo,
33    receipt::{
34        CompositeReceipt, Groth16Receipt, Groth16ReceiptVerifierParameters, InnerAssumptionReceipt,
35        InnerReceipt, SegmentReceipt, SuccinctReceipt,
36    },
37    receipt_claim::{UnionClaim, Unknown},
38    sha::Digestible,
39    stark_to_snark, ExecutorEnv, PreflightResults, ProverOpts, Receipt, ReceiptClaim, ReceiptKind,
40    Segment, Session, VerifierContext,
41};
42
43mod private {
44    use super::{dev_mode::DevModeProver, prover_impl::ProverImpl};
45
46    pub trait Sealed {}
47    impl Sealed for ProverImpl {}
48    impl Sealed for DevModeProver {}
49}
50
51/// A ProverServer can execute a given ELF binary and produce a [ProveInfo] which contains a
52/// [Receipt][crate::Receipt] that can be used to verify correct computation.
53pub trait ProverServer: private::Sealed {
54    /// Prove the specified ELF binary.
55    fn prove(&self, env: ExecutorEnv<'_>, elf: &[u8]) -> Result<ProveInfo> {
56        self.prove_with_ctx(env, &VerifierContext::default(), elf)
57    }
58
59    /// Prove the specified ELF binary using the specified [VerifierContext].
60    fn prove_with_ctx(
61        &self,
62        env: ExecutorEnv<'_>,
63        ctx: &VerifierContext,
64        elf: &[u8],
65    ) -> Result<ProveInfo>;
66
67    /// Prove the specified [Session].
68    fn prove_session(&self, ctx: &VerifierContext, session: &Session) -> Result<ProveInfo>;
69
70    /// Prove the specified [Segment].
71    fn prove_segment(&self, ctx: &VerifierContext, segment: &Segment) -> Result<SegmentReceipt> {
72        let results = self.segment_preflight(segment)?;
73        self.prove_segment_core(ctx, results)
74    }
75
76    /// Run preflight on the specified [Segment].
77    fn segment_preflight(&self, segment: &Segment) -> Result<PreflightResults>;
78
79    /// Prove the specified [Segment] which has had preflight run on it.
80    fn prove_segment_core(
81        &self,
82        ctx: &VerifierContext,
83        preflight_results: PreflightResults,
84    ) -> Result<SegmentReceipt>;
85
86    /// Prove the specified keccak request
87    fn prove_keccak(&self, request: &crate::ProveKeccakRequest)
88        -> Result<SuccinctReceipt<Unknown>>;
89
90    /// Lift a [SegmentReceipt] into a [SuccinctReceipt]
91    fn lift(&self, receipt: &SegmentReceipt) -> Result<SuccinctReceipt<ReceiptClaim>>;
92
93    /// Join two [SuccinctReceipt] into a [SuccinctReceipt]
94    fn join(
95        &self,
96        a: &SuccinctReceipt<ReceiptClaim>,
97        b: &SuccinctReceipt<ReceiptClaim>,
98    ) -> Result<SuccinctReceipt<ReceiptClaim>>;
99
100    /// Unite two [SuccinctReceipt] into a [SuccinctReceipt]
101    fn union(
102        &self,
103        a: &SuccinctReceipt<Unknown>,
104        b: &SuccinctReceipt<Unknown>,
105    ) -> Result<SuccinctReceipt<UnionClaim>>;
106
107    /// Resolve an assumption from a conditional [SuccinctReceipt] by providing a [SuccinctReceipt]
108    /// proving the validity of the assumption.
109    fn resolve(
110        &self,
111        conditional: &SuccinctReceipt<ReceiptClaim>,
112        assumption: &SuccinctReceipt<Unknown>,
113    ) -> Result<SuccinctReceipt<ReceiptClaim>>;
114
115    /// Convert a [SuccinctReceipt] with a Poseidon hash function that uses a 254-bit field
116    fn identity_p254(
117        &self,
118        a: &SuccinctReceipt<ReceiptClaim>,
119    ) -> Result<SuccinctReceipt<ReceiptClaim>>;
120
121    /// Compress a [CompositeReceipt] into a single [SuccinctReceipt].
122    ///
123    /// A [CompositeReceipt] may contain an arbitrary number of receipts assembled into
124    /// segments and assumptions. Together, these receipts collectively prove a top-level
125    /// [ReceiptClaim](crate::ReceiptClaim). This function compresses all of the constituent receipts of a
126    /// [CompositeReceipt] into a single [SuccinctReceipt] that proves the same top-level claim. It
127    /// accomplishes this by iterative application of the recursion programs including lift, join,
128    /// and resolve.
129    fn composite_to_succinct(
130        &self,
131        receipt: &CompositeReceipt,
132    ) -> Result<SuccinctReceipt<ReceiptClaim>> {
133        // Compress all receipts in the top-level session into one succinct receipt for the session.
134        let continuation_receipt = receipt
135            .segments
136            .iter()
137            .try_fold(
138                None,
139                |left: Option<SuccinctReceipt<ReceiptClaim>>,
140                 right: &SegmentReceipt|
141                 -> Result<_> {
142                    Ok(Some(match left {
143                        Some(left) => self.join(&left, &self.lift(right)?)?,
144                        None => self.lift(right)?,
145                    }))
146                },
147            )?
148            .ok_or_else(|| {
149                anyhow!("malformed composite receipt has no continuation segment receipts")
150            })?;
151
152        // Compress assumptions and resolve them to get the final succinct receipt.
153        receipt.assumption_receipts.iter().try_fold(
154            continuation_receipt,
155            |conditional: SuccinctReceipt<ReceiptClaim>, assumption: &InnerAssumptionReceipt| match assumption {
156                InnerAssumptionReceipt::Succinct(assumption) => self.resolve(&conditional, assumption),
157                InnerAssumptionReceipt::Composite(assumption) => {
158                    self.resolve(&conditional, &self.composite_to_succinct(assumption)?.into_unknown())
159                }
160                InnerAssumptionReceipt::Fake(_) => bail!(
161                    "compressing composite receipts with fake receipt assumptions is not supported"
162                ),
163                InnerAssumptionReceipt::Groth16(_) => bail!(
164                    "compressing composite receipts with Groth16 receipt assumptions is not supported"
165                )
166            },
167        )
168    }
169
170    /// Compress a [SuccinctReceipt] into a [Groth16Receipt].
171    fn succinct_to_groth16(
172        &self,
173        receipt: &SuccinctReceipt<ReceiptClaim>,
174    ) -> Result<Groth16Receipt<ReceiptClaim>> {
175        let ident_receipt = self.identity_p254(receipt).unwrap();
176        let seal_bytes = ident_receipt.get_seal_bytes();
177
178        let seal = stark_to_snark(&seal_bytes)?.to_vec();
179        Ok(Groth16Receipt {
180            seal,
181            claim: receipt.claim.clone(),
182            verifier_parameters: Groth16ReceiptVerifierParameters::default().digest(),
183        })
184    }
185
186    /// Compress a receipt into one with a smaller representation.
187    ///
188    /// The requested target representation is determined by the [ReceiptKind] specified on the
189    /// provided [ProverOpts]. If the receipt is already at least as compressed as the requested
190    /// kind, this is a no-op.
191    fn compress(&self, opts: &ProverOpts, receipt: &Receipt) -> Result<Receipt> {
192        match &receipt.inner {
193            InnerReceipt::Composite(inner) => match opts.receipt_kind {
194                ReceiptKind::Composite => Ok(receipt.clone()),
195                ReceiptKind::Succinct => {
196                    let succinct_receipt = self.composite_to_succinct(inner)?;
197                    Ok(Receipt::new(
198                        InnerReceipt::Succinct(succinct_receipt),
199                        receipt.journal.bytes.clone(),
200                    ))
201                }
202                ReceiptKind::Groth16 => {
203                    let succinct_receipt = self.composite_to_succinct(inner)?;
204                    let groth16_receipt = self.succinct_to_groth16(&succinct_receipt)?;
205                    Ok(Receipt::new(
206                        InnerReceipt::Groth16(groth16_receipt),
207                        receipt.journal.bytes.clone(),
208                    ))
209                }
210            },
211            InnerReceipt::Succinct(inner) => match opts.receipt_kind {
212                ReceiptKind::Composite | ReceiptKind::Succinct => Ok(receipt.clone()),
213                ReceiptKind::Groth16 => {
214                    let groth16_receipt = self.succinct_to_groth16(inner)?;
215                    Ok(Receipt::new(
216                        InnerReceipt::Groth16(groth16_receipt),
217                        receipt.journal.bytes.clone(),
218                    ))
219                }
220            },
221            InnerReceipt::Groth16(_) => match opts.receipt_kind {
222                ReceiptKind::Composite | ReceiptKind::Succinct | ReceiptKind::Groth16 => {
223                    Ok(receipt.clone())
224                }
225            },
226            InnerReceipt::Fake(_) => {
227                ensure!(
228                    opts.dev_mode(),
229                    "dev mode must be enabled to compress fake receipts"
230                );
231                Ok(receipt.clone())
232            }
233        }
234    }
235}
236
237/// A pair of [Hal] and [CircuitHal].
238#[derive(Clone)]
239pub struct HalPair<H, C>
240where
241    H: Hal<Field = BabyBear, Elem = Elem, ExtElem = ExtElem>,
242    C: CircuitHal<H>,
243{
244    /// A [Hal] implementation.
245    pub hal: Rc<H>,
246
247    /// An [CircuitHal] implementation.
248    pub circuit_hal: Rc<C>,
249}
250
251impl Session {
252    /// For each segment, call [ProverServer::prove_session] and collect the
253    /// receipts.
254    pub fn prove(&self) -> Result<ProveInfo> {
255        let prover = get_prover_server(&ProverOpts::default())?;
256        prover.prove_session(&VerifierContext::default(), self)
257    }
258}
259
260/// Select a [ProverServer] based on the specified [ProverOpts].
261pub fn get_prover_server(opts: &ProverOpts) -> Result<Rc<dyn ProverServer>> {
262    if opts.dev_mode() {
263        eprintln!("WARNING: proving in dev mode. This will not generate valid, secure proofs.");
264        return Ok(Rc::new(DevModeProver::new()));
265    }
266
267    Ok(Rc::new(ProverImpl::new(opts.clone())))
268}