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