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