risc0_zkvm/host/client/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#[cfg(feature = "bonsai")]
16pub(crate) mod bonsai;
17pub(crate) mod default;
18pub(crate) mod external;
19#[cfg(feature = "prove")]
20pub(crate) mod local;
21pub(crate) mod opts;
22
23use core::ops::Deref;
24use std::{path::PathBuf, rc::Rc};
25
26use anyhow::{anyhow, Result};
27
28#[cfg(feature = "bonsai")]
29use self::bonsai::BonsaiProver;
30
31use self::{default::DefaultProver, external::ExternalProver, opts::ProverOpts};
32
33use crate::{
34    get_version, host::prove_info::ProveInfo, ExecutorEnv, Receipt, SessionInfo, VerifierContext,
35};
36
37/// A Prover can execute a given ELF binary and produce a
38/// [Receipt] that can be used to verify correct computation.
39///
40/// # Usage
41/// To produce a proof, you must minimally provide an [ExecutorEnv] and an ELF
42/// binary. See the [risc0_build](https://docs.rs/risc0-build/*/risc0_build)
43/// crate for more information on producing ELF binaries from Rust source code.
44///
45/// ```rust
46/// use risc0_zkvm::{
47///     default_prover,
48///     ExecutorEnv,
49///     ProverOpts,
50///     VerifierContext,
51/// };
52/// use risc0_zkvm_methods::FIB_ELF;
53///
54/// # #[cfg(not(feature = "cuda"))]
55/// # {
56/// // A straightforward case with an ELF binary
57/// let env = ExecutorEnv::builder().write(&20u32).unwrap().build().unwrap();
58/// let receipt = default_prover().prove(env, FIB_ELF).unwrap();
59///
60/// // Or you can specify a context and options
61/// // Here we are using ProverOpts::succinct() to get a constant size proof through recursion.
62/// let env = ExecutorEnv::builder().write(&20u32).unwrap().build().unwrap();
63/// let opts = ProverOpts::succinct();
64/// let receipt = default_prover().prove_with_opts(env, FIB_ELF, &opts).unwrap();
65/// # }
66/// ```
67pub trait Prover {
68    /// Return a name for this [Prover].
69    fn get_name(&self) -> String;
70
71    /// Prove zkVM execution of the specified ELF binary.
72    ///
73    /// Use this method unless you have need to configure the prover options or verifier context.
74    /// Default [VerifierContext] and [ProverOpts] will be used.
75    fn prove(&self, env: ExecutorEnv<'_>, elf: &[u8]) -> Result<ProveInfo> {
76        let opts = ProverOpts::default();
77        let ctx = VerifierContext::default();
78        self.prove_with_ctx(env, &ctx, elf, &opts)
79    }
80
81    /// Prove zkVM execution of the specified ELF binary and using the specified [ProverOpts].
82    ///
83    /// Use this method when you want to specify the receipt type you would like (e.g. groth16 or
84    /// succinct), or if you need to tweak other parameter in [ProverOpts].
85    ///
86    /// Default [VerifierContext] will be used.
87    fn prove_with_opts(
88        &self,
89        env: ExecutorEnv<'_>,
90        elf: &[u8],
91        opts: &ProverOpts,
92    ) -> Result<ProveInfo> {
93        let ctx = VerifierContext::default().with_dev_mode(opts.dev_mode());
94        self.prove_with_ctx(env, &ctx, elf, opts)
95    }
96
97    /// Prove zkVM execution of the specified ELF binary and using the specified [VerifierContext]
98    /// and [ProverOpts].
99    ///
100    /// Use this method if you are using non-standard verification parameters. The
101    /// [VerifierContext] specified here should match what you expect the verifier to use in your
102    /// application.
103    fn prove_with_ctx(
104        &self,
105        env: ExecutorEnv<'_>,
106        ctx: &VerifierContext,
107        elf: &[u8],
108        opts: &ProverOpts,
109    ) -> Result<ProveInfo>;
110
111    /// Compress a [Receipt], proving the same computation using a smaller representation.
112    ///
113    /// Proving will, by default, produce a [CompositeReceipt](crate::CompositeReceipt), which
114    /// may contain an arbitrary number of receipts assembled into segments and assumptions.
115    /// Together, these receipts collectively prove a top-level
116    /// [ReceiptClaim](crate::ReceiptClaim). This function can be used to compress all of the constituent
117    /// receipts of a [CompositeReceipt](crate::CompositeReceipt) into a single
118    /// [SuccinctReceipt](crate::SuccinctReceipt) or [Groth16Receipt](crate::Groth16Receipt) that proves the same top-level claim.
119    ///
120    /// Compression from [Groth16Receipt](crate::CompositeReceipt) to
121    /// [SuccinctReceipt](crate::SuccinctReceipt) is accomplished by iterative application of the
122    /// recursion programs including lift, join, and resolve.
123    ///
124    /// Compression from [SuccinctReceipt](crate::SuccinctReceipt) to
125    /// [Groth16Receipt](crate::Groth16Receipt) is accomplished by running a Groth16 recursive
126    /// verifier, referred to as the "STARK-to-SNARK" operation.
127    ///
128    /// NOTE: Compression to [Groth16Receipt](crate::Groth16Receipt) requires
129    /// Docker to be installed. See issue
130    /// [#1749](https://github.com/risc0/risc0/issues/1749) for more information.
131    ///
132    /// If the receipt is already at least as compressed as the requested compression level (e.g.
133    /// it is already succinct or Groth16 and a succinct receipt is required) this function is a
134    /// no-op. As a result, it is idempotent.
135    fn compress(&self, opts: &ProverOpts, receipt: &Receipt) -> Result<Receipt>;
136}
137
138// Implementation of Prover for `Rc<Prover>` which allows it to satisfy trait bounds for Prover.
139impl<P: Prover + ?Sized> Prover for Rc<P> {
140    fn get_name(&self) -> String {
141        self.deref().get_name()
142    }
143
144    fn prove_with_ctx(
145        &self,
146        env: ExecutorEnv<'_>,
147        ctx: &VerifierContext,
148        elf: &[u8],
149        opts: &ProverOpts,
150    ) -> Result<ProveInfo> {
151        self.deref().prove_with_ctx(env, ctx, elf, opts)
152    }
153
154    fn compress(&self, opts: &ProverOpts, receipt: &Receipt) -> Result<Receipt> {
155        self.deref().compress(opts, receipt)
156    }
157}
158
159/// An Executor can execute a given ELF binary.
160pub trait Executor {
161    /// Execute the specified ELF binary.
162    ///
163    /// This only executes the program and does not generate a receipt.
164    fn execute(&self, env: ExecutorEnv<'_>, elf: &[u8]) -> Result<SessionInfo>;
165}
166
167/// Return a default [Prover] based on environment variables and feature flags.
168///
169/// The `RISC0_PROVER` environment variable, if specified, will select the
170/// following [Prover] implementation:
171/// * `bonsai`: [BonsaiProver] to prove on Bonsai.
172/// * `local`: LocalProver to prove locally in-process. Note: this
173///   requires the `prove` feature flag.
174/// * `ipc`: [ExternalProver] to prove using an `r0vm` sub-process. Note: `r0vm`
175///   must be installed. To specify the path to `r0vm`, use `RISC0_SERVER_PATH`.
176///
177/// If `RISC0_PROVER` is not specified, the following rules are used to select a
178/// [Prover]:
179/// * [BonsaiProver] if the `BONSAI_API_URL` and `BONSAI_API_KEY` environment
180///   variables are set unless `RISC0_DEV_MODE` is enabled.
181/// * LocalProver if the `prove` feature flag is enabled.
182/// * [ExternalProver] otherwise.
183pub fn default_prover() -> Rc<dyn Prover> {
184    let explicit = std::env::var("RISC0_PROVER").unwrap_or_default();
185    if !explicit.is_empty() {
186        return match explicit.to_lowercase().as_str() {
187            "actor" => Rc::new(DefaultProver::new(get_r0vm_path().unwrap()).unwrap()),
188            #[cfg(feature = "bonsai")]
189            "bonsai" => Rc::new(BonsaiProver::new("bonsai")),
190            "ipc" => Rc::new(ExternalProver::new("ipc", get_r0vm_path().unwrap())),
191            #[cfg(feature = "prove")]
192            "local" => Rc::new(self::local::LocalProver::new("local")),
193            _ => unimplemented!("Unsupported prover: {explicit}"),
194        };
195    }
196
197    #[cfg(feature = "bonsai")]
198    {
199        if !crate::is_dev_mode_enabled_via_environment()
200            && std::env::var("BONSAI_API_URL").is_ok()
201            && std::env::var("BONSAI_API_KEY").is_ok()
202        {
203            return Rc::new(BonsaiProver::new("bonsai"));
204        }
205    }
206
207    if cfg!(feature = "prove") {
208        #[cfg(feature = "prove")]
209        return Rc::new(self::local::LocalProver::new("local"));
210    }
211
212    Rc::new(ExternalProver::new("ipc", get_r0vm_path().unwrap()))
213}
214
215/// Return a default [Executor] based on environment variables and feature
216/// flags.
217///
218/// The `RISC0_EXECUTOR` environment variable, if specified, will select the
219/// following [Executor] implementation:
220/// * `local`: LocalProver to execute locally in-process. Note: this is
221///   only available when the `prove` feature is enabled.
222/// * `ipc`: [ExternalProver] to execute using an `r0vm` sub-process. Note:
223///   `r0vm` must be installed. To specify the path to `r0vm`, use
224///   `RISC0_SERVER_PATH`.
225///
226/// If `RISC0_EXECUTOR` is not specified, the following rules are used to select
227/// an [Executor]:
228/// * LocalProver if the `prove` feature flag is enabled.
229/// * [ExternalProver] otherwise.
230pub fn default_executor() -> Rc<dyn Executor> {
231    let explicit = std::env::var("RISC0_EXECUTOR").unwrap_or_default();
232    if !explicit.is_empty() {
233        return match explicit.to_lowercase().as_str() {
234            "ipc" => Rc::new(ExternalProver::new("ipc", get_r0vm_path().unwrap())),
235            #[cfg(feature = "prove")]
236            "local" => Rc::new(self::local::LocalProver::new("local")),
237            _ => unimplemented!("Unsupported executor: {explicit}"),
238        };
239    }
240
241    if cfg!(feature = "prove") {
242        #[cfg(feature = "prove")]
243        return Rc::new(self::local::LocalProver::new("local"));
244    }
245
246    Rc::new(ExternalProver::new("ipc", get_r0vm_path().unwrap()))
247}
248
249/// Return a local [Executor].
250#[cfg(feature = "prove")]
251pub fn local_executor() -> Rc<dyn Executor> {
252    Rc::new(self::local::LocalProver::new("local"))
253}
254
255pub(crate) fn get_r0vm_path() -> Result<PathBuf> {
256    if let Ok(path) = std::env::var("RISC0_SERVER_PATH") {
257        let path = PathBuf::from(path);
258        if path.is_file() {
259            return Ok(path);
260        }
261    }
262
263    let mut version = get_version().map_err(|err| anyhow!(err))?;
264    tracing::debug!("version: {version}");
265
266    if let Ok(rzup) = rzup::Rzup::new() {
267        if let Ok(dir) = rzup.get_version_dir(&rzup::Component::R0Vm, &version) {
268            return Ok(dir.join("r0vm"));
269        }
270
271        // Try again, but with these fields stripped
272        version.patch = 0;
273        version.build = Default::default();
274
275        if let Ok(dir) = rzup.get_version_dir(&rzup::Component::R0Vm, &version) {
276            return Ok(dir.join("r0vm"));
277        }
278    }
279
280    Ok("r0vm".into())
281}