openvm_sdk/
lib.rs

1#![cfg_attr(feature = "tco", allow(incomplete_features))]
2#![cfg_attr(feature = "tco", feature(explicit_tail_calls))]
3
4use std::{
5    fs::read,
6    marker::PhantomData,
7    path::Path,
8    sync::{Arc, OnceLock},
9};
10
11use config::AppConfig;
12use getset::Getters;
13use keygen::{AppProvingKey, AppVerifyingKey};
14use openvm_build::{
15    build_guest_package, find_unique_executable, get_package, GuestOptions, TargetFilter,
16};
17// Re-exports
18pub use openvm_build::{cargo_command, get_rustup_toolchain_name};
19pub use openvm_circuit;
20use openvm_circuit::{
21    arch::{
22        execution_mode::Segment, instructions::exe::VmExe, Executor, InitFileGenerator,
23        MeteredExecutor, PreflightExecutor, VirtualMachineError, VmBuilder, VmExecutionConfig,
24        VmExecutor,
25    },
26    system::memory::merkle::public_values::extract_public_values,
27};
28use openvm_continuations::CommitBytes;
29use openvm_sdk_config::{SdkVmConfig, SdkVmCpuBuilder, TranspilerConfig};
30use openvm_stark_backend::{keygen::types::MultiStarkVerifyingKey, StarkEngine, SystemParams};
31use openvm_stark_sdk::config::baby_bear_poseidon2::{
32    BabyBearPoseidon2CpuEngine as BabyBearPoseidon2Engine, Digest,
33};
34#[cfg(feature = "evm-prove")]
35use openvm_static_verifier::StaticVerifierShape;
36use openvm_transpiler::{
37    elf::Elf, openvm_platform::memory::MEM_SIZE, transpiler::Transpiler, FromElf,
38};
39use openvm_verify_stark_host::{
40    verify_vm_stark_proof_decoded,
41    vk::{VerificationBaseline, VmStarkVerifyingKey},
42    VmStarkProof,
43};
44
45use crate::{
46    config::{AggregationConfig, AggregationSystemParams, AggregationTreeConfig},
47    keygen::{AggPrefixProvingKey, AggProvingKey, SdkCachedProvingKey},
48    prover::{AggProver, AppProver, DeferralAggProver, DeferralHookCommits, StarkProver},
49    types::{AppExecutionCommit, ExecutableFormat},
50};
51#[cfg(feature = "evm-prove")]
52use crate::{halo2_params::CacheHalo2ParamsReader, keygen::Halo2ProvingKey, prover::Halo2Prover};
53#[cfg(feature = "root-prover")]
54use crate::{
55    keygen::{dummy::compute_root_proof_heights, RootProvingKey},
56    prover::{EvmProver, RootProver},
57};
58
59cfg_if::cfg_if! {
60    if #[cfg(feature = "cuda")] {
61        use openvm_sdk_config::SdkVmGpuBuilder;
62        use openvm_cuda_backend::BabyBearPoseidon2GpuEngine as GpuBabyBearPoseidon2Engine;
63        pub use GpuSdk as Sdk;
64        pub type DefaultStarkEngine = GpuBabyBearPoseidon2Engine;
65    } else {
66        pub use CpuSdk as Sdk;
67        pub type DefaultStarkEngine = BabyBearPoseidon2Engine;
68    }
69}
70
71pub use openvm_stark_sdk::config::baby_bear_poseidon2::{BabyBearPoseidon2Config as SC, F};
72
73pub mod builder;
74pub mod config;
75pub mod fs;
76#[cfg(feature = "evm-prove")]
77pub mod halo2_params;
78pub mod keygen;
79pub mod prover;
80#[cfg(feature = "evm-verify")]
81mod solidity;
82pub mod types;
83pub mod util;
84
85#[cfg(test)]
86mod tests;
87
88mod error;
89mod stdin;
90pub use error::SdkError;
91pub use stdin::*;
92
93pub const OPENVM_VERSION: &str = concat!(
94    env!("CARGO_PKG_VERSION_MAJOR"),
95    ".",
96    env!("CARGO_PKG_VERSION_MINOR")
97);
98
99// The SDK is only generic in the engine for the non-root SC. The root SC is fixed to
100// BabyBearPoseidon2RootEngine right now.
101/// The SDK provides convenience methods and constructors for provers.
102///
103/// A built SDK is an immutable proving environment: user-supplied config, params, and pre-generated
104/// keys are fixed after construction. Use [`builder`](Self::builder) for advanced initialization,
105/// or [`new`](Self::new) / [`new_without_transpiler`](Self::new_without_transpiler) for the
106/// common config-driven paths.
107///
108/// Internally, the SDK lazily caches proving state that depends only on the app VM config,
109/// aggregation config, root params, and optional pre-generated keys. It does not cache any state
110/// that depends on the program executable.
111///
112/// Some commonly used methods are:
113/// - [`execute`](Self::execute)
114/// - [`prove`](Self::prove)
115/// - [`verify_proof`](Self::verify_proof)
116#[derive(Getters)]
117pub struct GenericSdk<E, VB>
118where
119    E: StarkEngine<SC = SC>,
120    VB: VmBuilder<E>,
121    VB::VmConfig: VmExecutionConfig<F>,
122{
123    #[getset(get = "pub")]
124    app_config: AppConfig<VB::VmConfig>,
125    #[getset(get = "pub")]
126    agg_config: AggregationConfig,
127    #[getset(get = "pub")]
128    agg_tree_config: AggregationTreeConfig,
129    #[cfg(feature = "root-prover")]
130    #[getset(get = "pub")]
131    root_params: SystemParams,
132    #[cfg(feature = "evm-prove")]
133    #[getset(get = "pub")]
134    halo2_shape: StaticVerifierShape,
135    #[cfg(feature = "evm-prove")]
136    #[getset(get = "pub")]
137    halo2_config: config::Halo2Config,
138
139    #[getset(get = "pub")]
140    app_vm_builder: VB,
141
142    transpiler: Option<Transpiler<F>>,
143
144    /// The `executor` may be used to construct different types of interpreters, given the program,
145    /// for more specific execution purposes. By default, it is recommended to use the
146    /// [`execute`](GenericSdk::execute) method.
147    #[getset(get = "pub")]
148    executor: VmExecutor<F, VB::VmConfig>,
149
150    app_pk: OnceLock<AppProvingKey<VB::VmConfig>>,
151    agg_prover: OnceLock<Arc<AggProver>>,
152    #[cfg(feature = "root-prover")]
153    root_prover: OnceLock<Arc<RootProver>>,
154
155    deferral_setup: DeferralSetup,
156
157    #[cfg(feature = "evm-prove")]
158    #[getset(get = "pub")]
159    halo2_params_reader: CacheHalo2ParamsReader,
160    #[cfg(feature = "evm-prove")]
161    halo2_prover: OnceLock<Halo2Prover>,
162
163    _phantom: PhantomData<E>,
164}
165
166#[derive(Clone, Default)]
167pub enum DeferralSetup {
168    #[default]
169    /// Deferrals are disabled for this GenericSdk. The STARK, root, and halo2 vks are deferral-
170    /// unaware, and trying to prove with def_inputs set causes an error.
171    Disabled,
172    /// The STARK, root, and halo2 vks are deferral-aware (i.e. are equivalent to Active), but
173    /// trying to prove with def_inputs set still causes an error. Use this when a program that
174    /// doesn't use deferrals must be verified by a deferral-aware vk.
175    Aware(DeferralHookCommits),
176    /// The STARK, root, and halo2 vks are deferral-aware (i.e. are equivalent to Aware), and
177    /// deferral inputs can be used and proved.
178    Active(Arc<DeferralAggProver>),
179}
180
181impl DeferralSetup {
182    pub fn hook_cached_commit(&self) -> Option<Digest> {
183        match self {
184            Self::Disabled => None,
185            Self::Aware(commits) => Some(commits.hook_cached_commit),
186            Self::Active(prover) => Some(prover.def_hook_cached_commit()),
187        }
188    }
189
190    pub fn hook_commit(&self) -> Option<Digest> {
191        match self {
192            Self::Disabled => None,
193            Self::Aware(commits) => Some(commits.hook_commit),
194            Self::Active(prover) => Some(prover.def_hook_commit()),
195        }
196    }
197
198    pub fn prover(&self) -> Option<Arc<DeferralAggProver>> {
199        match self {
200            Self::Active(prover) => Some(prover.clone()),
201            Self::Disabled | Self::Aware(_) => None,
202        }
203    }
204}
205
206pub type CpuSdk = GenericSdk<BabyBearPoseidon2Engine, SdkVmCpuBuilder>;
207
208#[cfg(feature = "cuda")]
209pub type GpuSdk = GenericSdk<GpuBabyBearPoseidon2Engine, SdkVmGpuBuilder>;
210
211impl<E, VB> GenericSdk<E, VB>
212where
213    E: StarkEngine<SC = SC>,
214    VB: VmBuilder<E, VmConfig = SdkVmConfig> + Clone + Default,
215{
216    /// Creates SDK with a standard configuration that includes a set of default VM extensions
217    /// loaded.
218    ///
219    /// **Note**: To use this configuration, your `openvm.toml` must match
220    /// [`SdkVmConfig::standard`], including the order of the moduli and elliptic curve parameters
221    /// of the respective extensions. See the `openvm-sdk-config` crate documentation for the
222    /// corresponding TOML.
223    pub fn standard(app_params: SystemParams, agg_params: AggregationSystemParams) -> Self {
224        GenericSdk::new(AppConfig::standard(app_params), agg_params).unwrap()
225    }
226
227    /// Creates SDK with a configuration with RISC-V RV32IM and IO VM extensions loaded.
228    ///
229    /// **Note**: To use this configuration, your `openvm.toml` must match
230    /// [`SdkVmConfig::riscv32`]. See the `openvm-sdk-config` crate documentation for the
231    /// corresponding TOML.
232    pub fn riscv32(app_params: SystemParams, agg_params: AggregationSystemParams) -> Self {
233        GenericSdk::new(AppConfig::riscv32(app_params), agg_params).unwrap()
234    }
235}
236
237impl<E, VB> GenericSdk<E, VB>
238where
239    E: StarkEngine<SC = SC>,
240    VB: VmBuilder<E>,
241{
242    /// Creates SDK custom to the given [AppConfig], with a RISC-V transpiler.
243    pub fn new(
244        app_config: AppConfig<VB::VmConfig>,
245        agg_params: AggregationSystemParams,
246    ) -> Result<Self, SdkError>
247    where
248        VB: Default,
249        VB::VmConfig: TranspilerConfig<F>,
250    {
251        Self::builder()
252            .app_config(app_config)
253            .agg_params(agg_params)
254            .build()
255    }
256
257    /// Creates an SDK custom to the given [AppConfig] without configuring a transpiler.
258    ///
259    /// **Note**: This function does not set the transpiler, which must be done separately to
260    /// support RISC-V ELFs.
261    pub fn new_without_transpiler(
262        app_config: AppConfig<VB::VmConfig>,
263        agg_params: AggregationSystemParams,
264    ) -> Result<Self, SdkError>
265    where
266        VB: Default,
267    {
268        Self::builder()
269            .app_config(app_config)
270            .agg_params(agg_params)
271            .build_without_transpiler()
272    }
273
274    /// Returns the def_hook_prover cached commit.
275    pub fn def_hook_cached_commit(&self) -> Option<Digest> {
276        self.deferral_setup.hook_cached_commit()
277    }
278
279    /// Returns the deferral hook commit derived from the deferral aggregation path.
280    pub fn def_hook_commit(&self) -> Option<Digest> {
281        self.deferral_setup.hook_commit()
282    }
283
284    /// Returns the deferral aggregation prover when this SDK can prove non-empty deferral inputs.
285    pub fn deferral_agg_prover(&self) -> Option<Arc<DeferralAggProver>> {
286        self.deferral_setup.prover()
287    }
288
289    /// Derives the cached commits that the deferral circuit at `def_idx` expects callers to fold
290    /// into its input commit.
291    pub fn deferral_circuit_cached_commits(
292        &self,
293        def_idx: usize,
294    ) -> Result<Vec<CommitBytes>, SdkError> {
295        let deferral_prover = self.deferral_setup.prover().ok_or_else(|| {
296            SdkError::Other(eyre::eyre!(
297                "deferral circuit cached commits require an active deferral prover"
298            ))
299        })?;
300        deferral_prover
301            .multi_deferral_circuit_prover
302            .single_circuit_provers
303            .get(def_idx)
304            .map(|prover| prover.def_circuit_prover.cached_commits())
305            .ok_or_else(|| {
306                SdkError::Other(eyre::eyre!(
307                    "deferral circuit index {def_idx} is not configured"
308                ))
309            })
310    }
311
312    /// Returns serde-serializable proving keys for this SDK.
313    ///
314    /// This errors if `app_pk` or `agg_pk` have not already been generated or seeded into the SDK.
315    /// Optional keys are returned only if they are already cached or were seeded into the SDK.
316    ///
317    /// Halo2 proving keys are intentionally excluded; use `write_halo2_pk_to_file` and
318    /// `read_halo2_pk_from_file` for those.
319    pub fn cached_proving_key(&self) -> Result<SdkCachedProvingKey<VB::VmConfig>, SdkError> {
320        let app_pk = self
321            .app_pk
322            .get()
323            .ok_or_else(|| SdkError::Other(eyre::eyre!("app_pk is not generated")))?
324            .clone();
325        let agg_prover = self
326            .agg_prover
327            .get()
328            .ok_or_else(|| SdkError::Other(eyre::eyre!("agg_pk is not generated")))?;
329        let deferral_prover = self.deferral_setup.prover();
330        Ok(SdkCachedProvingKey {
331            app_pk,
332            agg_pk: AggProvingKey {
333                prefix: AggPrefixProvingKey {
334                    leaf: agg_prover.leaf_prover.get_pk(),
335                    internal_for_leaf: agg_prover.internal_for_leaf_prover.get_pk(),
336                },
337                internal_recursive: agg_prover.internal_recursive_prover.get_pk(),
338            },
339            deferral_pk: deferral_prover
340                .as_ref()
341                .map(|def_agg_prover| def_agg_prover.multi_deferral_circuit_prover.get_pk()),
342            deferral_agg_pk: deferral_prover
343                .as_ref()
344                .map(|def_agg_prover| def_agg_prover.get_pk()),
345            #[cfg(feature = "root-prover")]
346            root_pk: self.root_prover.get().map(|root_prover| RootProvingKey {
347                root_pk: root_prover.0.get_pk(),
348                trace_heights: root_prover.0.get_trace_heights().unwrap_or_default(),
349            }),
350        })
351    }
352
353    /// Builds the guest package located at `pkg_dir`. This function requires that the build target
354    /// is unique and errors otherwise. Returns the built ELF file decoded in the [Elf] type.
355    pub fn build<P: AsRef<Path>>(
356        &self,
357        guest_opts: GuestOptions,
358        pkg_dir: P,
359        target_filter: &Option<TargetFilter>,
360        init_file_name: Option<&str>, // If None, we use "openvm-init.rs"
361    ) -> Result<Elf, SdkError> {
362        self.app_config
363            .app_vm_config
364            .write_to_init_file(pkg_dir.as_ref(), init_file_name)?;
365        let pkg = get_package(pkg_dir.as_ref());
366        let target_dir = match build_guest_package(&pkg, &guest_opts, None, target_filter) {
367            Ok(target_dir) => target_dir,
368            Err(Some(code)) => {
369                return Err(SdkError::BuildFailedWithCode(code));
370            }
371            Err(None) => {
372                return Err(SdkError::BuildFailed);
373            }
374        };
375
376        let elf_path =
377            find_unique_executable(pkg_dir, target_dir, target_filter).map_err(SdkError::Other)?;
378        let data = read(&elf_path)?;
379        Elf::decode(&data, MEM_SIZE as u32).map_err(SdkError::Other)
380    }
381
382    /// Transpiler for transpiling RISC-V ELF to OpenVM executable.
383    pub fn transpiler(&self) -> Result<&Transpiler<F>, SdkError> {
384        self.transpiler
385            .as_ref()
386            .ok_or(SdkError::TranspilerNotAvailable)
387    }
388
389    /// Normalizes an ELF or executable handle into a shared [`VmExe`].
390    pub fn convert_to_exe(
391        &self,
392        executable: impl Into<ExecutableFormat>,
393    ) -> Result<Arc<VmExe<F>>, SdkError> {
394        let executable = executable.into();
395        let exe = match executable {
396            ExecutableFormat::Elf(elf) => {
397                let transpiler = self.transpiler()?.clone();
398                Arc::new(VmExe::from_elf(elf, transpiler)?)
399            }
400            ExecutableFormat::VmExe(exe) => Arc::new(exe),
401            ExecutableFormat::SharedVmExe(exe) => exe,
402        };
403        Ok(exe)
404    }
405}
406
407// The SDK is only functional for SC = BabyBearPoseidon2Config because that is what recursive
408// aggregation supports.
409impl<E, VB> GenericSdk<E, VB>
410where
411    E: StarkEngine<SC = SC>,
412    VB: VmBuilder<E> + Clone,
413    <VB::VmConfig as VmExecutionConfig<F>>::Executor:
414        Executor<F> + MeteredExecutor<F> + PreflightExecutor<F, VB::RecordArena>,
415{
416    /// Returns the user public values as field elements.
417    pub fn execute(
418        &self,
419        app_exe: impl Into<ExecutableFormat>,
420        inputs: StdIn,
421    ) -> Result<Vec<u8>, SdkError> {
422        let exe = self.convert_to_exe(app_exe)?;
423        let instance = self
424            .executor
425            .instance(&exe)
426            .map_err(VirtualMachineError::from)?;
427        let final_memory = instance
428            .execute(inputs, None)
429            .map_err(VirtualMachineError::from)?
430            .memory;
431        let public_values = extract_public_values(
432            self.executor.config.as_ref().num_public_values,
433            &final_memory.memory,
434        );
435        Ok(public_values)
436    }
437
438    /// Executes with segmentation for proof generation.
439    /// Returns both user public values and segments with instruction counts and trace heights.
440    pub fn execute_metered(
441        &self,
442        app_exe: impl Into<ExecutableFormat>,
443        inputs: StdIn,
444    ) -> Result<(Vec<u8>, Vec<Segment>), SdkError> {
445        let app_prover = self.app_prover(app_exe)?;
446
447        let vm = app_prover.vm();
448        let exe = app_prover.exe();
449
450        let ctx = vm.build_metered_ctx(&exe);
451        let interpreter = vm
452            .metered_interpreter(&exe)
453            .map_err(VirtualMachineError::from)?;
454
455        let (segments, final_state) = interpreter
456            .execute_metered(inputs, ctx)
457            .map_err(VirtualMachineError::from)?;
458        let public_values = extract_public_values(
459            self.executor.config.as_ref().num_public_values,
460            &final_state.memory.memory,
461        );
462
463        Ok((public_values, segments))
464    }
465
466    /// Executes with cost metering to measure computational cost in trace cells.
467    /// Returns both user public values, and cost along with instruction count.
468    pub fn execute_metered_cost(
469        &self,
470        app_exe: impl Into<ExecutableFormat>,
471        inputs: StdIn,
472    ) -> Result<(Vec<u8>, (u64, u64)), SdkError> {
473        let app_prover = self.app_prover(app_exe)?;
474
475        let vm = app_prover.vm();
476        let exe = app_prover.exe();
477
478        let ctx = vm.build_metered_cost_ctx();
479        let interpreter = vm
480            .metered_cost_interpreter(&exe)
481            .map_err(VirtualMachineError::from)?;
482
483        let (ctx, final_state) = interpreter
484            .execute_metered_cost(inputs, ctx)
485            .map_err(VirtualMachineError::from)?;
486        let instret = ctx.instret;
487        let cost = ctx.cost;
488
489        let public_values = extract_public_values(
490            self.executor.config.as_ref().num_public_values,
491            &final_state.memory.memory,
492        );
493
494        Ok((public_values, (cost, instret)))
495    }
496
497    // ======================== Proving Methods ============================
498
499    /// Generates a single aggregate STARK proof of the full program execution of the given
500    /// `app_exe` with program inputs `inputs`.\
501    ///
502    /// For convenience, this function also returns the [VerificationBaseline], which is a full
503    /// commitment to the App [VmExe] and aggregation verifiers. It does **not** depend on the
504    /// `inputs`. It can be generated separately from the proof by creating a
505    /// [`prover`](Self::prover) and calling
506    /// [`app_vm_commit`](StarkProver::app_vm_commit).
507    ///
508    /// If STARK aggregation is not needed and a proof whose size may grow linearly with the length
509    /// of the program runtime is desired, create an [`app_prover`](Self::app_prover) and call
510    /// [`app_prover.prove(inputs)`](AppProver::prove).
511    pub fn prove(
512        &self,
513        app_exe: impl Into<ExecutableFormat>,
514        inputs: StdIn,
515        def_inputs: &[DeferralInput],
516    ) -> Result<(VmStarkProof, VerificationBaseline), SdkError> {
517        let mut prover = self.prover(app_exe)?;
518        let proof = prover.prove(inputs, def_inputs)?.0;
519        let baseline = prover.generate_baseline();
520        Ok((proof, baseline))
521    }
522
523    #[cfg(feature = "evm-prove")]
524    /// Generates an EVM-verifiable proof for the given executable and inputs.
525    pub fn prove_evm(
526        &self,
527        app_exe: impl Into<ExecutableFormat>,
528        inputs: StdIn,
529        def_inputs: &[DeferralInput],
530    ) -> Result<types::EvmProof, SdkError> {
531        let app_exe = self.convert_to_exe(app_exe)?;
532        let mut evm_prover = self.evm_prover(app_exe)?;
533        let evm_proof = evm_prover.prove_evm(inputs, def_inputs)?;
534        Ok(evm_proof)
535    }
536
537    // ========================= Prover Constructors =========================
538
539    /// This constructor is for generating app proofs that do not require a single aggregate STARK
540    /// proof of the full program execution. For a single STARK proof, use the
541    /// [`prove`](Self::prove) method instead.
542    ///
543    /// Creates an app prover instance specific to the provided exe.
544    /// This function will generate the [AppProvingKey] if it doesn't already exist and use it to
545    /// construct the [AppProver].
546    pub fn app_prover(
547        &self,
548        exe: impl Into<ExecutableFormat>,
549    ) -> Result<AppProver<E, VB>, SdkError> {
550        let exe = self.convert_to_exe(exe)?;
551        let app_pk = self.app_pk();
552        let prover = AppProver::<E, VB>::new(self.app_vm_builder.clone(), &app_pk.app_vm_pk, exe)?;
553        Ok(prover)
554    }
555
556    /// Constructs a new [StarkProver] instance for the given executable.
557    /// This function will generate the [AppProvingKey] if it does not already
558    /// exist.
559    pub fn prover(
560        &self,
561        app_exe: impl Into<ExecutableFormat>,
562    ) -> Result<StarkProver<E, VB>, SdkError> {
563        let app_exe = self.convert_to_exe(app_exe)?;
564        let app_pk = self.app_pk();
565        let stark_prover = StarkProver::<E, _>::new(
566            self.app_vm_builder.clone(),
567            &app_pk.app_vm_pk,
568            app_exe,
569            self.agg_prover(),
570            self.deferral_setup.clone(),
571        )?;
572        Ok(stark_prover)
573    }
574
575    #[cfg(feature = "root-prover")]
576    /// Constructs an [`EvmProver`] for the given executable with only the root prover, generating
577    /// prerequisite keys, lazily
578    pub fn evm_prover_without_halo2(
579        &self,
580        app_exe: impl Into<ExecutableFormat>,
581    ) -> Result<EvmProver<E, VB>, SdkError> {
582        let app_exe = self.convert_to_exe(app_exe)?;
583        let app_pk = self.app_pk();
584        let evm_prover = EvmProver::<E, _>::new(
585            self.app_vm_builder.clone(),
586            &app_pk.app_vm_pk,
587            app_exe,
588            self.agg_prover(),
589            self.deferral_setup.clone(),
590            self.root_prover(),
591            #[cfg(feature = "evm-prove")]
592            None,
593        )?;
594        Ok(evm_prover)
595    }
596
597    #[cfg(feature = "root-prover")]
598    /// Constructs an [`EvmProver`] for the given executable, generating prerequisite keys lazily.
599    pub fn evm_prover(
600        &self,
601        app_exe: impl Into<ExecutableFormat>,
602    ) -> Result<EvmProver<E, VB>, SdkError> {
603        #[allow(unused_mut)]
604        let mut evm_prover = self.evm_prover_without_halo2(app_exe)?;
605        #[cfg(feature = "evm-prove")]
606        {
607            evm_prover.halo2_prover = Some(self.halo2_prover());
608        }
609        Ok(evm_prover)
610    }
611
612    // ===================== Component Prover Constructors =====================
613
614    /// Returns the cached aggregation prover, generating it on first use if needed.
615    pub fn agg_prover(&self) -> Arc<AggProver> {
616        let app_pk = self.app_pk();
617        self.agg_prover
618            .get_or_init(|| {
619                Arc::new(AggProver::new(
620                    Arc::new(app_pk.app_vm_pk.vm_pk.get_vk()),
621                    self.agg_config.clone(),
622                    self.agg_tree_config,
623                    self.def_hook_cached_commit(),
624                ))
625            })
626            .clone()
627    }
628
629    #[cfg(feature = "root-prover")]
630    /// Returns the cached root prover, generating it on first use if needed.
631    pub fn root_prover(&self) -> Arc<RootProver> {
632        self.root_prover
633            .get_or_init(|| {
634                let system_config = self.app_config.app_vm_config.as_ref();
635                let root_params = self.root_params.clone();
636                let agg_prover = self.agg_prover();
637
638                let (trace_heights, root_pk) = compute_root_proof_heights(
639                    system_config.clone(),
640                    self.agg_config.params.clone(),
641                    self.agg_tree_config,
642                    root_params.clone(),
643                    self.deferral_setup.clone(),
644                )
645                .expect("Trace heights did not generate properly");
646
647                let memory_dimensions = system_config.memory_config.memory_dimensions();
648                let num_user_pvs = system_config.num_public_values;
649
650                Arc::new(RootProver::from_pk(
651                    agg_prover.internal_recursive_prover.get_vk(),
652                    agg_prover
653                        .internal_recursive_prover
654                        .get_self_vk_pcs_data()
655                        .unwrap()
656                        .commitment
657                        .into(),
658                    root_pk,
659                    memory_dimensions,
660                    num_user_pvs,
661                    self.def_hook_commit(),
662                    Some(trace_heights),
663                ))
664            })
665            .clone()
666    }
667
668    #[cfg(feature = "evm-prove")]
669    /// Returns the cached Halo2 prover, generating it on first use if needed.
670    pub fn halo2_prover(&self) -> Halo2Prover {
671        self.halo2_prover
672            .get_or_init(|| {
673                use crate::keygen::static_verifier::keygen_halo2;
674
675                let root_prover = self.root_prover();
676                let root_vk = root_prover.0.get_vk().as_ref().clone();
677                let agg_prover = self.agg_prover();
678
679                // Generate a dummy root proof by running a trivial program through the pipeline
680                let dummy_root_proof = keygen::dummy::generate_dummy_root_proof::<E, _>(
681                    self.app_vm_builder.clone(),
682                    &self.app_pk().app_vm_pk,
683                    agg_prover.clone(),
684                    self.deferral_setup.clone(),
685                    root_prover,
686                );
687
688                let halo2_pk = keygen_halo2(
689                    &self.halo2_config,
690                    &self.halo2_params_reader,
691                    self.halo2_shape,
692                    &agg_prover.internal_recursive_prover.get_vk(),
693                    &root_vk,
694                    &dummy_root_proof,
695                );
696
697                Halo2Prover::new(self.halo2_params_reader(), halo2_pk)
698            })
699            .clone()
700    }
701
702    // ======================== Keygen Related Methods ========================
703
704    /// Generates the app proving key once and caches it. Future calls will return the cached key.
705    ///
706    /// # Panics
707    /// This function will panic if the app keygen fails.
708    pub fn app_keygen(&self) -> (AppProvingKey<VB::VmConfig>, AppVerifyingKey) {
709        let pk = self.app_pk().clone();
710        let vk = pk.get_app_vk();
711        (pk, vk)
712    }
713
714    /// Generates the app proving key once and caches it. Future calls will return the cached key.
715    ///
716    /// # Panics
717    /// This function will panic if the app keygen fails.
718    pub fn app_pk(&self) -> &AppProvingKey<VB::VmConfig> {
719        // TODO[jpw]: use `get_or_try_init` once it is stable
720        self.app_pk.get_or_init(|| {
721            AppProvingKey::keygen(self.app_config.clone()).expect("app_keygen failed")
722        })
723    }
724
725    /// Returns the app verifying key derived from the cached app proving key.
726    pub fn app_vk(&self) -> AppVerifyingKey {
727        self.app_pk().get_app_vk()
728    }
729
730    /// Generates or retrieves the aggregation proving and verifying keys as a pair.
731    pub fn agg_keygen(&self) -> (AggProvingKey, MultiStarkVerifyingKey<SC>) {
732        let pk = self.agg_pk();
733        let vk = self.agg_vk().as_ref().clone();
734        (pk, vk)
735    }
736
737    /// Generates or retrieves the aggregation prefix proving key without the internal-recursive
738    /// key.
739    pub fn agg_prefix_pk(&self) -> AggPrefixProvingKey {
740        if let Some(agg_prover) = self.agg_prover.get() {
741            return AggPrefixProvingKey {
742                leaf: agg_prover.leaf_prover.get_pk(),
743                internal_for_leaf: agg_prover.internal_for_leaf_prover.get_pk(),
744            };
745        }
746
747        let app_pk = self.app_pk();
748        AggProver::keygen_prefix(
749            Arc::new(app_pk.app_vm_pk.vm_pk.get_vk()),
750            self.agg_config.clone(),
751            self.def_hook_cached_commit(),
752        )
753    }
754
755    /// Generates or retrieves the full aggregation proving key.
756    pub fn agg_pk(&self) -> AggProvingKey {
757        let agg_prover = self.agg_prover();
758        AggProvingKey {
759            prefix: AggPrefixProvingKey {
760                leaf: agg_prover.leaf_prover.get_pk(),
761                internal_for_leaf: agg_prover.internal_for_leaf_prover.get_pk(),
762            },
763            internal_recursive: agg_prover.internal_recursive_prover.get_pk(),
764        }
765    }
766
767    /// Returns the aggregation verifying key for the recursive aggregation layer.
768    pub fn agg_vk(&self) -> Arc<MultiStarkVerifyingKey<SC>> {
769        self.agg_prover().internal_recursive_prover.get_vk()
770    }
771
772    #[cfg(feature = "root-prover")]
773    /// Generates or retrieves the root proving key and recorded trace heights.
774    pub fn root_pk(&self) -> RootProvingKey {
775        let root_prover = self.root_prover();
776        RootProvingKey {
777            root_pk: root_prover.0.get_pk(),
778            trace_heights: root_prover.0.get_trace_heights().unwrap_or_default(),
779        }
780    }
781
782    /// Generates the Halo2 (static verifier + wrapper) proving key once and caches it.
783    ///
784    /// The flow:
785    /// 1. Get the root VK and internal recursive VK cached commit
786    /// 2. Generate a dummy root proof via the EVM prover pipeline
787    /// 3. Keygen the static verifier circuit
788    /// 4. Generate a dummy snark from the verifier
789    /// 5. Keygen the wrapper circuit (auto-tuned or fixed k)
790    #[cfg(feature = "evm-prove")]
791    pub fn halo2_pk(&self) -> Halo2ProvingKey {
792        self.halo2_prover().pk()
793    }
794
795    /// Generates the [`AppExecutionCommit`] for the given executable.
796    ///
797    /// This function will generate the app_pk if it does not already exist.
798    pub fn app_commit(
799        &self,
800        app_exe: impl Into<ExecutableFormat>,
801    ) -> Result<AppExecutionCommit, SdkError> {
802        let prover = self.prover(app_exe)?;
803        Ok(AppExecutionCommit {
804            app_exe_commit: CommitBytes::from(prover.generate_baseline().app_exe_commit),
805            app_vm_commit: CommitBytes::from(prover.app_vm_commit()),
806        })
807    }
808
809    // ======================== Verification Methods ========================
810
811    /// Verifies aggregate STARK proof of VM execution.
812    ///
813    /// **Note**: This function does not have any reliance on `self` and does not depend on the app
814    /// config set in the [Sdk].
815    pub fn verify_proof(
816        agg_vk: MultiStarkVerifyingKey<SC>,
817        baseline: VerificationBaseline,
818        proof: &VmStarkProof,
819    ) -> Result<(), SdkError> {
820        let vk = VmStarkVerifyingKey {
821            mvk: agg_vk,
822            baseline,
823        };
824        verify_vm_stark_proof_decoded(&vk, proof)?;
825        Ok(())
826    }
827
828    #[cfg(feature = "evm-verify")]
829    /// Generates Solidity verifier artifacts for the cached Halo2 proving key.
830    pub fn generate_halo2_verifier_solidity(&self) -> Result<types::EvmHalo2Verifier, SdkError> {
831        solidity::generate_halo2_verifier_solidity(&self.halo2_pk(), &self.halo2_params_reader)
832    }
833
834    #[cfg(feature = "evm-verify")]
835    /// Generates Solidity verifier artifacts under `src/{version_name}` in the solc source map.
836    ///
837    /// Solidity embeds source metadata in bytecode, so `version_name` should match the directory
838    /// where the generated verifier contracts will be written.
839    pub fn generate_halo2_verifier_solidity_with_version_name(
840        &self,
841        version_name: &str,
842    ) -> Result<types::EvmHalo2Verifier, SdkError> {
843        solidity::generate_halo2_verifier_solidity_with_version_name(
844            &self.halo2_pk(),
845            &self.halo2_params_reader,
846            version_name,
847        )
848    }
849
850    #[cfg(feature = "evm-verify")]
851    /// Uses the `verify(..)` interface of the `OpenVmHalo2Verifier` contract.
852    ///
853    /// Requires the `evm-verify` feature. Internally deploys the verifier bytecode in a local EVM
854    /// and executes the verification call. If expected_app_commit is provided, it will check the
855    /// proof's app_commit against it.
856    pub fn verify_evm_halo2_proof(
857        openvm_verifier: &types::EvmHalo2Verifier,
858        evm_proof: types::EvmProof,
859        expected_app_commit: Option<AppExecutionCommit>,
860    ) -> Result<u64, SdkError> {
861        if let Some(expected_app_commit) = expected_app_commit {
862            if expected_app_commit != evm_proof.app_commit {
863                return Err(
864                    eyre::eyre!("EVM proof verification failed: mismatching app commits").into(),
865                );
866            }
867        }
868        solidity::verify_evm_halo2_proof(openvm_verifier, evm_proof)
869    }
870}