openvm_sdk_config/
lib.rs

1use bon::Builder;
2use openvm_algebra_circuit::*;
3use openvm_algebra_transpiler::{Fp2TranspilerExtension, ModularTranspilerExtension};
4use openvm_bigint_circuit::*;
5use openvm_bigint_transpiler::*;
6use openvm_circuit::{
7    arch::{instructions::DEFERRAL_AS, *},
8    derive::VmConfig,
9    system::{SystemChipInventory, SystemCpuBuilder, SystemExecutor},
10};
11use openvm_cpu_backend::{CpuBackend, CpuDevice};
12use openvm_deferral_circuit::*;
13use openvm_deferral_transpiler::*;
14use openvm_ecc_circuit::*;
15use openvm_ecc_transpiler::*;
16use openvm_keccak256_circuit::*;
17use openvm_keccak256_transpiler::*;
18use openvm_pairing_circuit::*;
19use openvm_pairing_transpiler::*;
20use openvm_rv32im_circuit::*;
21use openvm_rv32im_transpiler::*;
22use openvm_sha2_circuit::*;
23use openvm_sha2_transpiler::*;
24use openvm_stark_backend::{p3_field::Field, StarkEngine, StarkProtocolConfig, Val};
25use openvm_stark_sdk::config::baby_bear_poseidon2::F;
26use openvm_transpiler::transpiler::Transpiler;
27use serde::{Deserialize, Serialize};
28
29pub mod deferral;
30use deferral::DeferralConfig;
31
32cfg_if::cfg_if! {
33    if #[cfg(feature = "cuda")] {
34        use openvm_algebra_circuit::AlgebraProverExt;
35        use openvm_bigint_circuit::Int256GpuProverExt;
36        use openvm_circuit::system::cuda::{extensions::SystemGpuBuilder, SystemChipInventoryGPU};
37        use openvm_cuda_backend::{
38            BabyBearPoseidon2GpuEngine, GpuBackend
39        };
40        use openvm_ecc_circuit::EccProverExt;
41        use openvm_keccak256_circuit::Keccak256GpuProverExt;
42        use openvm_rv32im_circuit::Rv32ImGpuProverExt;
43        use openvm_sha2_circuit::Sha2GpuProverExt;
44        pub use SdkVmGpuBuilder as SdkVmBuilder;
45    } else {
46        pub use SdkVmCpuBuilder as SdkVmBuilder;
47    }
48}
49
50#[derive(Clone, Debug, Serialize, Deserialize)]
51struct SdkVmConfigWrapper {
52    app_vm_config: SdkVmConfig,
53}
54
55/// The recommended way to construct [SdkVmConfig] is using [SdkVmConfig::from_toml].
56///
57/// For construction without reliance on deserialization, you can use [SdkVmConfigBuilder], which
58/// follows a builder pattern. After calling [SdkVmConfigBuilder::build], call
59/// [SdkVmConfig::optimize] to apply some default optimizations to built configuration for best
60/// performance.
61#[derive(Builder, Clone, Debug, Serialize, Deserialize)]
62#[serde(from = "SdkVmConfigWithDefaultDeser")]
63pub struct SdkVmConfig {
64    pub system: SdkSystemConfig,
65    pub rv32i: Option<UnitStruct>,
66    pub io: Option<UnitStruct>,
67    pub keccak: Option<UnitStruct>,
68    pub sha2: Option<UnitStruct>,
69
70    /// NOTE: if enabling this together with the [Int256] extension, you should set the `rv32m`
71    /// field to have the same `range_tuple_checker_sizes` as the `bigint` field for best
72    /// performance.
73    pub rv32m: Option<Rv32M>,
74    /// NOTE: if enabling this together with the [Rv32M] extension, you should set the `rv32m`
75    /// field to have the same `range_tuple_checker_sizes` as the `bigint` field for best
76    /// performance.
77    pub bigint: Option<Int256>,
78    pub modular: Option<ModularExtension>,
79    pub fp2: Option<Fp2Extension>,
80    pub pairing: Option<PairingExtension>,
81    pub ecc: Option<WeierstrassExtension>,
82
83    /// NOTE: Only deferral configurations enumerated by SupportedDeferral are fully serializable
84    /// and deserializable. For custom deferral circuits stored as SupportedDeferral::Other, the
85    /// circuit's DeferralFn must be manually replaced in the extension after deserialization.
86    pub deferral: Option<DeferralConfig>,
87}
88
89impl SdkVmConfig {
90    /// Standard configuration with a set of default VM extensions loaded.
91    ///
92    /// **Note**: To use this configuration, your `openvm.toml` must match, including the order of
93    /// the moduli and elliptic curve parameters of the respective extensions:
94    /// The `app_vm_config` field of your `openvm.toml` must exactly match the following:
95    ///
96    /// ```toml
97    #[doc = include_str!("openvm_standard.toml")]
98    /// ```
99    pub fn standard() -> SdkVmConfig {
100        let bn_config = PairingCurve::Bn254.curve_config();
101        let bls_config = PairingCurve::Bls12_381.curve_config();
102        SdkVmConfig::builder()
103            .system(Default::default())
104            .rv32i(Default::default())
105            .rv32m(Default::default())
106            .io(Default::default())
107            .keccak(Default::default())
108            .sha2(Default::default())
109            .bigint(Default::default())
110            .modular(ModularExtension::new(vec![
111                bn_config.modulus.clone(),
112                bn_config.scalar.clone(),
113                SECP256K1_CONFIG.modulus.clone(),
114                SECP256K1_CONFIG.scalar.clone(),
115                P256_CONFIG.modulus.clone(),
116                P256_CONFIG.scalar.clone(),
117                bls_config.modulus.clone(),
118                bls_config.scalar.clone(),
119            ]))
120            .fp2(Fp2Extension::new(vec![
121                (
122                    BN254_COMPLEX_STRUCT_NAME.to_string(),
123                    bn_config.modulus.clone(),
124                ),
125                (
126                    BLS12_381_COMPLEX_STRUCT_NAME.to_string(),
127                    bls_config.modulus.clone(),
128                ),
129            ]))
130            .ecc(WeierstrassExtension::new(vec![
131                bn_config.clone(),
132                SECP256K1_CONFIG.clone(),
133                P256_CONFIG.clone(),
134                bls_config.clone(),
135            ]))
136            .pairing(PairingExtension::new(vec![
137                PairingCurve::Bn254,
138                PairingCurve::Bls12_381,
139            ]))
140            .build()
141            .optimize()
142    }
143
144    /// Configuration with RISC-V RV32IM and IO VM extensions loaded.
145    ///
146    /// **Note**: To use this configuration, your `openvm.toml` must exactly match the following:
147    ///
148    /// ```toml
149    #[doc = include_str!("openvm_riscv32.toml")]
150    /// ```
151    pub fn riscv32() -> Self {
152        SdkVmConfig::builder()
153            .system(Default::default())
154            .rv32i(Default::default())
155            .rv32m(Default::default())
156            .io(Default::default())
157            .build()
158            .optimize()
159    }
160
161    /// `openvm_toml` should be the TOML string read from an openvm.toml file.
162    pub fn from_toml(openvm_toml: &str) -> Result<Self, toml::de::Error> {
163        let wrapper: SdkVmConfigWrapper = toml::from_str(openvm_toml)?;
164        Ok(wrapper.app_vm_config)
165    }
166
167    pub fn to_toml(&self) -> Result<String, toml::ser::Error> {
168        toml::to_string_pretty(&SdkVmConfigWrapper {
169            app_vm_config: self.clone(),
170        })
171    }
172}
173
174pub trait TranspilerConfig<F> {
175    fn transpiler(&self) -> Transpiler<F>;
176}
177
178impl TranspilerConfig<F> for SdkVmConfig {
179    fn transpiler(&self) -> Transpiler<F> {
180        let mut transpiler = Transpiler::default();
181        if self.rv32i.is_some() {
182            transpiler = transpiler.with_extension(Rv32ITranspilerExtension);
183        }
184        if self.io.is_some() {
185            transpiler = transpiler.with_extension(Rv32IoTranspilerExtension);
186        }
187        if self.keccak.is_some() {
188            transpiler = transpiler.with_extension(Keccak256TranspilerExtension);
189        }
190        if self.sha2.is_some() {
191            transpiler = transpiler.with_extension(Sha2TranspilerExtension);
192        }
193        if self.rv32m.is_some() {
194            transpiler = transpiler.with_extension(Rv32MTranspilerExtension);
195        }
196        if self.bigint.is_some() {
197            transpiler = transpiler.with_extension(Int256TranspilerExtension);
198        }
199        if self.modular.is_some() {
200            transpiler = transpiler.with_extension(ModularTranspilerExtension);
201        }
202        if self.fp2.is_some() {
203            transpiler = transpiler.with_extension(Fp2TranspilerExtension);
204        }
205        if self.pairing.is_some() {
206            transpiler = transpiler.with_extension(PairingTranspilerExtension);
207        }
208        if self.ecc.is_some() {
209            transpiler = transpiler.with_extension(EccTranspilerExtension);
210        }
211        if let Some(deferral_config) = &self.deferral {
212            transpiler = transpiler.with_extension(DeferralTranspilerExtension::new(
213                deferral_config.to_extension().def_circuit_commits,
214            ));
215        }
216        transpiler
217    }
218}
219
220impl AsRef<SystemConfig> for SdkVmConfig {
221    fn as_ref(&self) -> &SystemConfig {
222        &self.system.config
223    }
224}
225
226impl AsMut<SystemConfig> for SdkVmConfig {
227    fn as_mut(&mut self) -> &mut SystemConfig {
228        &mut self.system.config
229    }
230}
231
232impl SdkVmConfig {
233    pub fn optimize(mut self) -> Self {
234        self.apply_optimizations();
235        self
236    }
237
238    /// Apply small optimizations to the configuration.
239    pub fn apply_optimizations(&mut self) {
240        let rv32m = self.rv32m.as_mut();
241        let bigint = self.bigint.as_mut();
242        if let (Some(bigint), Some(rv32m)) = (bigint, rv32m) {
243            rv32m.range_tuple_checker_sizes[0] =
244                rv32m.range_tuple_checker_sizes[0].max(bigint.range_tuple_checker_sizes[0]);
245            rv32m.range_tuple_checker_sizes[1] =
246                rv32m.range_tuple_checker_sizes[1].max(bigint.range_tuple_checker_sizes[1]);
247            bigint.range_tuple_checker_sizes = rv32m.range_tuple_checker_sizes;
248        }
249
250        const DEFERRAL_AS_USIZE: usize = DEFERRAL_AS as usize;
251        let addr_spaces = &mut self.system.config.memory_config.addr_spaces;
252        let deferral_as_exists = addr_spaces.len() > DEFERRAL_AS_USIZE;
253        if self.deferral.is_some() {
254            assert!(
255                deferral_as_exists,
256                "deferral is enabled but address space DEFERRAL_AS ({DEFERRAL_AS_USIZE}) is missing \
257                 from memory_config.addr_spaces; the VM config must allocate it"
258            );
259        } else if deferral_as_exists {
260            addr_spaces[DEFERRAL_AS_USIZE].num_cells = 0;
261        }
262    }
263
264    pub fn to_inner(&self) -> SdkVmConfigInner {
265        let config = self.clone().optimize();
266        let system = config.system.config.clone();
267        let rv32i = config.rv32i.map(|_| Rv32I);
268        let io = config.io.map(|_| Rv32Io);
269        let keccak = config.keccak.map(|_| Keccak256);
270        let sha2 = config.sha2.map(|_| Sha2);
271        let rv32m = config.rv32m;
272        let bigint = config.bigint;
273        let modular = config.modular.clone();
274        let fp2 = config.fp2.clone();
275        let pairing = config.pairing.clone();
276        let ecc = config.ecc.clone();
277        let deferral = config.deferral.as_ref().map(DeferralConfig::to_extension);
278
279        SdkVmConfigInner {
280            system,
281            rv32i,
282            io,
283            keccak,
284            sha2,
285            rv32m,
286            bigint,
287            modular,
288            fp2,
289            pairing,
290            ecc,
291            deferral,
292        }
293    }
294}
295
296// ======================= Implementation of VmConfig and VmBuilder ====================
297
298/// SDK CPU VmBuilder
299#[derive(Copy, Clone, Default)]
300pub struct SdkVmCpuBuilder;
301
302/// Internal struct to use for the VmConfig derive macro.
303/// Can be obtained via [`SdkVmConfig::to_inner`].
304#[derive(Clone, Debug, VmConfig, Serialize, Deserialize)]
305pub struct SdkVmConfigInner {
306    #[config(executor = "SystemExecutor<F>")]
307    pub system: SystemConfig,
308    #[extension(executor = "Rv32IExecutor")]
309    pub rv32i: Option<Rv32I>,
310    #[extension(executor = "Rv32IoExecutor")]
311    pub io: Option<Rv32Io>,
312    #[extension(executor = "Keccak256Executor")]
313    pub keccak: Option<Keccak256>,
314    #[extension(executor = "Sha2Executor")]
315    pub sha2: Option<Sha2>,
316
317    #[extension(executor = "Rv32MExecutor")]
318    pub rv32m: Option<Rv32M>,
319    #[extension(executor = "Int256Executor")]
320    pub bigint: Option<Int256>,
321    #[extension(executor = "ModularExtensionExecutor")]
322    pub modular: Option<ModularExtension>,
323    #[extension(executor = "Fp2ExtensionExecutor")]
324    pub fp2: Option<Fp2Extension>,
325    #[extension(executor = "PairingExtensionExecutor<F>")]
326    pub pairing: Option<PairingExtension>,
327    #[extension(executor = "WeierstrassExtensionExecutor")]
328    pub ecc: Option<WeierstrassExtension>,
329
330    #[extension(executor = "DeferralExecutor")]
331    pub deferral: Option<DeferralExtension>,
332}
333
334// Generated by macro
335pub type SdkVmConfigExecutor<F> = SdkVmConfigInnerExecutor<F>;
336
337impl<F: Field> VmExecutionConfig<F> for SdkVmConfig
338where
339    SdkVmConfigInner: VmExecutionConfig<F>,
340{
341    type Executor = <SdkVmConfigInner as VmExecutionConfig<F>>::Executor;
342
343    fn create_executors(
344        &self,
345    ) -> Result<ExecutorInventory<Self::Executor>, ExecutorInventoryError> {
346        self.to_inner().create_executors()
347    }
348}
349
350impl<SC: StarkProtocolConfig> VmCircuitConfig<SC> for SdkVmConfig
351where
352    SdkVmConfigInner: VmCircuitConfig<SC>,
353{
354    fn create_airs(&self) -> Result<AirInventory<SC>, AirInventoryError> {
355        self.to_inner().create_airs()
356    }
357}
358
359use openvm_stark_sdk::config::baby_bear_poseidon2::BabyBearPoseidon2Config;
360type SC = BabyBearPoseidon2Config;
361impl<E> VmBuilder<E> for SdkVmCpuBuilder
362where
363    E: StarkEngine<SC = SC, PB = CpuBackend<SC>, PD = CpuDevice<SC>>,
364{
365    type VmConfig = SdkVmConfig;
366    type SystemChipInventory = SystemChipInventory<SC>;
367    type RecordArena = MatrixRecordArena<Val<SC>>;
368
369    fn create_chip_complex(
370        &self,
371        config: &SdkVmConfig,
372        circuit: AirInventory<SC>,
373        device_ctx: &openvm_stark_backend::EngineDeviceCtx<E>,
374    ) -> Result<
375        VmChipComplex<SC, Self::RecordArena, E::PB, Self::SystemChipInventory>,
376        ChipInventoryError,
377    > {
378        let config = config.to_inner();
379        let mut chip_complex = VmBuilder::<E>::create_chip_complex(
380            &SystemCpuBuilder,
381            &config.system,
382            circuit,
383            device_ctx,
384        )?;
385        let inventory = &mut chip_complex.inventory;
386        if let Some(rv32i) = &config.rv32i {
387            VmProverExtension::<E, _, _>::extend_prover(&Rv32ImCpuProverExt, rv32i, inventory)?;
388        }
389        if let Some(io) = &config.io {
390            VmProverExtension::<E, _, _>::extend_prover(&Rv32ImCpuProverExt, io, inventory)?;
391        }
392        if let Some(keccak) = &config.keccak {
393            VmProverExtension::<E, _, _>::extend_prover(&Keccak256CpuProverExt, keccak, inventory)?;
394        }
395        if let Some(sha2) = &config.sha2 {
396            VmProverExtension::<E, _, _>::extend_prover(&Sha2CpuProverExt, sha2, inventory)?;
397        }
398        if let Some(rv32m) = &config.rv32m {
399            VmProverExtension::<E, _, _>::extend_prover(&Rv32ImCpuProverExt, rv32m, inventory)?;
400        }
401        if let Some(bigint) = &config.bigint {
402            VmProverExtension::<E, _, _>::extend_prover(&Int256CpuProverExt, bigint, inventory)?;
403        }
404        if let Some(modular) = &config.modular {
405            VmProverExtension::<E, _, _>::extend_prover(&AlgebraCpuProverExt, modular, inventory)?;
406        }
407        if let Some(fp2) = &config.fp2 {
408            VmProverExtension::<E, _, _>::extend_prover(&AlgebraCpuProverExt, fp2, inventory)?;
409        }
410        if let Some(pairing) = &config.pairing {
411            VmProverExtension::<E, _, _>::extend_prover(&PairingProverExt, pairing, inventory)?;
412        }
413        if let Some(ecc) = &config.ecc {
414            VmProverExtension::<E, _, _>::extend_prover(&EccCpuProverExt, ecc, inventory)?;
415        }
416        if let Some(deferral) = &config.deferral {
417            VmProverExtension::<E, _, _>::extend_prover(
418                &DeferralCpuProverExt,
419                deferral,
420                inventory,
421            )?;
422        }
423        Ok(chip_complex)
424    }
425}
426
427#[cfg(feature = "cuda")]
428#[derive(Copy, Clone, Default)]
429pub struct SdkVmGpuBuilder;
430
431#[cfg(feature = "cuda")]
432impl VmBuilder<BabyBearPoseidon2GpuEngine> for SdkVmGpuBuilder {
433    type VmConfig = SdkVmConfig;
434    type SystemChipInventory = SystemChipInventoryGPU;
435    type RecordArena = DenseRecordArena;
436
437    fn create_chip_complex(
438        &self,
439        config: &SdkVmConfig,
440        circuit: AirInventory<SC>,
441        device_ctx: &openvm_stark_backend::EngineDeviceCtx<BabyBearPoseidon2GpuEngine>,
442    ) -> Result<
443        VmChipComplex<SC, Self::RecordArena, GpuBackend, Self::SystemChipInventory>,
444        ChipInventoryError,
445    > {
446        type E = BabyBearPoseidon2GpuEngine;
447
448        let config = config.to_inner();
449        let mut chip_complex = VmBuilder::<E>::create_chip_complex(
450            &SystemGpuBuilder,
451            &config.system,
452            circuit,
453            device_ctx,
454        )?;
455        let inventory = &mut chip_complex.inventory;
456        if let Some(rv32i) = &config.rv32i {
457            VmProverExtension::<E, _, _>::extend_prover(&Rv32ImGpuProverExt, rv32i, inventory)?;
458        }
459        if let Some(io) = &config.io {
460            VmProverExtension::<E, _, _>::extend_prover(&Rv32ImGpuProverExt, io, inventory)?;
461        }
462        if let Some(keccak) = &config.keccak {
463            VmProverExtension::<E, _, _>::extend_prover(&Keccak256GpuProverExt, keccak, inventory)?;
464        }
465        if let Some(sha2) = &config.sha2 {
466            VmProverExtension::<E, _, _>::extend_prover(&Sha2GpuProverExt, sha2, inventory)?;
467        }
468        if let Some(rv32m) = &config.rv32m {
469            VmProverExtension::<E, _, _>::extend_prover(&Rv32ImGpuProverExt, rv32m, inventory)?;
470        }
471        if let Some(bigint) = &config.bigint {
472            VmProverExtension::<E, _, _>::extend_prover(&Int256GpuProverExt, bigint, inventory)?;
473        }
474        if let Some(modular) = &config.modular {
475            VmProverExtension::<E, _, _>::extend_prover(&AlgebraProverExt, modular, inventory)?;
476        }
477        if let Some(fp2) = &config.fp2 {
478            VmProverExtension::<E, _, _>::extend_prover(&AlgebraProverExt, fp2, inventory)?;
479        }
480        if let Some(pairing) = &config.pairing {
481            VmProverExtension::<E, _, _>::extend_prover(&PairingProverExt, pairing, inventory)?;
482        }
483        if let Some(ecc) = &config.ecc {
484            VmProverExtension::<E, _, _>::extend_prover(&EccProverExt, ecc, inventory)?;
485        }
486        if let Some(deferral) = &config.deferral {
487            VmProverExtension::<E, _, _>::extend_prover(&DeferralProverExt, deferral, inventory)?;
488        }
489        Ok(chip_complex)
490    }
491}
492
493// ======================= Boilerplate ====================
494
495impl InitFileGenerator for SdkVmConfig {
496    fn generate_init_file_contents(&self) -> Option<String> {
497        self.to_inner().generate_init_file_contents()
498    }
499}
500impl InitFileGenerator for SdkVmConfigInner {
501    fn generate_init_file_contents(&self) -> Option<String> {
502        if self.modular.is_some() || self.fp2.is_some() || self.ecc.is_some() {
503            let mut contents = String::new();
504            contents.push_str(
505                "// This file is automatically generated by cargo openvm. Do not rename or edit.\n",
506            );
507
508            if let Some(modular_config) = &self.modular {
509                contents.push_str(&modular_config.generate_moduli_init());
510                contents.push('\n');
511            }
512
513            if let Some(fp2_config) = &self.fp2 {
514                assert!(
515                    self.modular.is_some(),
516                    "ModularExtension is required for Fp2Extension"
517                );
518                let modular_config = self.modular.as_ref().unwrap();
519                contents.push_str(&fp2_config.generate_complex_init(modular_config));
520                contents.push('\n');
521            }
522
523            if let Some(ecc_config) = &self.ecc {
524                contents.push_str(&ecc_config.generate_sw_init());
525                contents.push('\n');
526            }
527
528            Some(contents)
529        } else {
530            None
531        }
532    }
533}
534
535#[derive(Clone, Debug, Default, Serialize, Deserialize)]
536pub struct SdkSystemConfig {
537    pub config: SystemConfig,
538}
539
540// Default implementation uses no init file
541impl InitFileGenerator for SdkSystemConfig {}
542
543impl From<SystemConfig> for SdkSystemConfig {
544    fn from(config: SystemConfig) -> Self {
545        Self { config }
546    }
547}
548
549/// A struct that is used to represent a unit struct in the config, used for
550/// serialization and deserialization.
551#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
552pub struct UnitStruct {}
553
554impl From<Rv32I> for UnitStruct {
555    fn from(_: Rv32I) -> Self {
556        UnitStruct {}
557    }
558}
559
560impl From<Rv32Io> for UnitStruct {
561    fn from(_: Rv32Io) -> Self {
562        UnitStruct {}
563    }
564}
565
566impl From<Keccak256> for UnitStruct {
567    fn from(_: Keccak256) -> Self {
568        UnitStruct {}
569    }
570}
571
572impl From<Sha2> for UnitStruct {
573    fn from(_: Sha2) -> Self {
574        UnitStruct {}
575    }
576}
577
578#[derive(Deserialize)]
579struct SdkVmConfigWithDefaultDeser {
580    #[serde(default)]
581    pub system: SdkSystemConfig,
582
583    pub rv32i: Option<UnitStruct>,
584    pub io: Option<UnitStruct>,
585    pub keccak: Option<UnitStruct>,
586    pub sha2: Option<UnitStruct>,
587
588    pub rv32m: Option<Rv32M>,
589    pub bigint: Option<Int256>,
590    pub modular: Option<ModularExtension>,
591    pub fp2: Option<Fp2Extension>,
592    pub pairing: Option<PairingExtension>,
593    pub ecc: Option<WeierstrassExtension>,
594
595    pub deferral: Option<DeferralConfig>,
596}
597
598impl From<SdkVmConfigWithDefaultDeser> for SdkVmConfig {
599    fn from(config: SdkVmConfigWithDefaultDeser) -> Self {
600        let ret = Self {
601            system: config.system,
602            rv32i: config.rv32i,
603            io: config.io,
604            keccak: config.keccak,
605            sha2: config.sha2,
606            rv32m: config.rv32m,
607            bigint: config.bigint,
608            modular: config.modular,
609            fp2: config.fp2,
610            pairing: config.pairing,
611            ecc: config.ecc,
612            deferral: config.deferral,
613        };
614        ret.optimize()
615    }
616}