1#![cfg_attr(feature = "tco", allow(incomplete_features))]
2#![cfg_attr(feature = "tco", feature(explicit_tail_calls))]
3use std::{
4 borrow::Borrow,
5 fs::read,
6 marker::PhantomData,
7 path::Path,
8 sync::{Arc, OnceLock},
9};
10
11#[cfg(feature = "evm-verify")]
12use alloy_sol_types::sol;
13use commit::AppExecutionCommit;
14use config::{AggregationTreeConfig, AppConfig};
15use getset::{Getters, MutGetters, WithSetters};
16use keygen::{AppProvingKey, AppVerifyingKey};
17use openvm_build::{
18 build_guest_package, find_unique_executable, get_package, GuestOptions, TargetFilter,
19};
20use openvm_circuit::{
21 arch::{
22 execution_mode::Segment,
23 hasher::{poseidon2::vm_poseidon2_hasher, Hasher},
24 instructions::exe::VmExe,
25 Executor, InitFileGenerator, MeteredExecutor, PreflightExecutor, VirtualMachineError,
26 VmBuilder, VmExecutionConfig, VmExecutor, VmVerificationError, CONNECTOR_AIR_ID,
27 PROGRAM_AIR_ID, PROGRAM_CACHED_TRACE_INDEX, PUBLIC_VALUES_AIR_ID,
28 },
29 system::{
30 memory::{
31 merkle::public_values::{extract_public_values, UserPublicValuesProofError},
32 CHUNK,
33 },
34 program::trace::compute_exe_commit,
35 },
36};
37#[cfg(feature = "evm-prove")]
38pub use openvm_continuations::static_verifier::DefaultStaticVerifierPvHandler;
39use openvm_continuations::verifier::{
40 common::types::VmVerifierPvs,
41 internal::types::{InternalVmVerifierPvs, VmStarkProof},
42 root::RootVmVerifierConfig,
43};
44pub use openvm_continuations::{RootSC, C, F, SC};
46use openvm_native_circuit::{NativeConfig, NativeCpuBuilder};
47use openvm_native_compiler::conversion::CompilerOptions;
48#[cfg(feature = "evm-prove")]
49use openvm_native_recursion::halo2::utils::{CacheHalo2ParamsReader, Halo2ParamsReader};
50use openvm_stark_backend::proof::Proof;
51use openvm_stark_sdk::{
52 config::baby_bear_poseidon2::BabyBearPoseidon2Engine,
53 engine::{StarkEngine, StarkFriEngine},
54};
55use openvm_transpiler::{
56 elf::Elf, openvm_platform::memory::MEM_SIZE, transpiler::Transpiler, FromElf,
57};
58#[cfg(feature = "evm-verify")]
59use snark_verifier_sdk::{evm::gen_evm_verifier_sol_code, halo2::aggregation::AggregationCircuit};
60
61#[cfg(feature = "evm-prove")]
62use crate::{
63 config::Halo2Config, keygen::Halo2ProvingKey, prover::EvmHalo2Prover, types::EvmProof,
64};
65use crate::{
66 config::{AggregationConfig, SdkVmConfig, SdkVmCpuBuilder, TranspilerConfig},
67 keygen::{asm::program_to_asm, AggProvingKey, AggVerifyingKey},
68 prover::{AppProver, StarkProver},
69 types::ExecutableFormat,
70};
71
72cfg_if::cfg_if! {
73 if #[cfg(feature = "cuda")] {
74 use config::SdkVmGpuBuilder;
75 use openvm_cuda_backend::engine::GpuBabyBearPoseidon2Engine;
76 use openvm_native_circuit::NativeGpuBuilder;
77 pub use GpuSdk as Sdk;
78 pub type DefaultStarkEngine = GpuBabyBearPoseidon2Engine;
79 } else {
80 pub use CpuSdk as Sdk;
81 pub type DefaultStarkEngine = BabyBearPoseidon2Engine;
82 }
83}
84
85pub mod codec;
86pub mod commit;
87pub mod config;
88pub mod fs;
89pub mod keygen;
90pub mod prover;
91pub mod types;
92pub mod util;
93
94mod error;
95mod stdin;
96pub use error::SdkError;
97pub use stdin::*;
98
99pub const EVM_HALO2_VERIFIER_INTERFACE: &str =
100 include_str!("../contracts/src/IOpenVmHalo2Verifier.sol");
101pub const EVM_HALO2_VERIFIER_TEMPLATE: &str =
102 include_str!("../contracts/template/OpenVmHalo2Verifier.sol");
103pub const OPENVM_VERSION: &str = concat!(
104 env!("CARGO_PKG_VERSION_MAJOR"),
105 ".",
106 env!("CARGO_PKG_VERSION_MINOR")
107);
108
109#[cfg(feature = "evm-verify")]
110sol! {
111 IOpenVmHalo2Verifier,
112 concat!(env!("CARGO_MANIFEST_DIR"), "/contracts/abi/IOpenVmHalo2Verifier.json"),
113}
114
115#[derive(Getters, MutGetters, WithSetters)]
127pub struct GenericSdk<E, VB, NativeBuilder>
128where
129 E: StarkEngine<SC = SC>,
130 VB: VmBuilder<E>,
131 VB::VmConfig: VmExecutionConfig<F>,
132{
133 #[getset(get = "pub", get_mut = "pub", set_with = "pub")]
134 app_config: AppConfig<VB::VmConfig>,
135 #[getset(get = "pub", get_mut = "pub", set_with = "pub")]
136 agg_config: AggregationConfig,
137 #[getset(get = "pub", get_mut = "pub", set_with = "pub")]
138 agg_tree_config: AggregationTreeConfig,
139 #[cfg(feature = "evm-prove")]
140 #[getset(get = "pub", get_mut = "pub", set_with = "pub")]
141 halo2_config: Halo2Config,
142
143 #[getset(get = "pub")]
147 executor: VmExecutor<F, VB::VmConfig>,
148
149 app_pk: OnceLock<AppProvingKey<VB::VmConfig>>,
150 agg_pk: OnceLock<AggProvingKey>,
153 dummy_internal_proof: OnceLock<Proof<SC>>,
154
155 #[cfg(feature = "evm-prove")]
156 #[getset(get = "pub", get_mut = "pub", set_with = "pub")]
157 halo2_params_reader: CacheHalo2ParamsReader,
158 #[cfg(feature = "evm-prove")]
159 halo2_pk: OnceLock<Halo2ProvingKey>,
160
161 #[getset(get = "pub")]
162 app_vm_builder: VB,
163 #[getset(get = "pub")]
164 native_builder: NativeBuilder,
165 transpiler: Option<Transpiler<F>>,
166
167 _phantom: PhantomData<E>,
168}
169
170pub type CpuSdk = GenericSdk<BabyBearPoseidon2Engine, SdkVmCpuBuilder, NativeCpuBuilder>;
171
172#[cfg(feature = "cuda")]
173pub type GpuSdk = GenericSdk<GpuBabyBearPoseidon2Engine, SdkVmGpuBuilder, NativeGpuBuilder>;
174
175impl<E, VB, NativeBuilder> GenericSdk<E, VB, NativeBuilder>
176where
177 E: StarkFriEngine<SC = SC>,
178 VB: VmBuilder<E, VmConfig = SdkVmConfig> + Clone + Default,
179 NativeBuilder: Clone + Default,
180{
181 #[doc = include_str!("./config/openvm_standard.toml")]
190 pub fn standard() -> Self {
192 GenericSdk::new(AppConfig::standard()).unwrap()
193 }
194
195 #[doc = include_str!("./config/openvm_riscv32.toml")]
201 pub fn riscv32() -> Self {
203 GenericSdk::new(AppConfig::riscv32()).unwrap()
204 }
205}
206
207impl<E, VB, NativeBuilder> GenericSdk<E, VB, NativeBuilder>
208where
209 E: StarkFriEngine<SC = SC>,
210 VB: VmBuilder<E>,
211{
212 pub fn new(app_config: AppConfig<VB::VmConfig>) -> Result<Self, SdkError>
214 where
215 VB: Default,
216 NativeBuilder: Default,
217 VB::VmConfig: TranspilerConfig<F>,
218 {
219 let transpiler = app_config.app_vm_config.transpiler();
220 let sdk = Self::new_without_transpiler(app_config)?.with_transpiler(transpiler);
221 Ok(sdk)
222 }
223
224 pub fn new_without_transpiler(app_config: AppConfig<VB::VmConfig>) -> Result<Self, SdkError>
227 where
228 VB: Default,
229 NativeBuilder: Default,
230 {
231 let system_config = app_config.app_vm_config.as_ref();
232 let profiling = system_config.profiling;
233 let compiler_options = CompilerOptions {
234 enable_cycle_tracker: profiling,
235 ..Default::default()
236 };
237 let executor = VmExecutor::new(app_config.app_vm_config.clone())
238 .map_err(|e| SdkError::Vm(e.into()))?;
239 let agg_config = AggregationConfig {
240 max_num_user_public_values: system_config.num_public_values,
241 leaf_fri_params: app_config.leaf_fri_params.fri_params,
242 profiling,
243 compiler_options,
244 ..Default::default()
245 };
246 #[cfg(feature = "evm-prove")]
247 let halo2_config = Halo2Config {
248 profiling,
249 ..Default::default()
250 };
251 Ok(Self {
252 app_config,
253 agg_config,
254 #[cfg(feature = "evm-prove")]
255 halo2_config,
256 agg_tree_config: Default::default(),
257 app_vm_builder: Default::default(),
258 native_builder: Default::default(),
259 transpiler: None,
260 executor,
261 app_pk: OnceLock::new(),
262 agg_pk: OnceLock::new(),
263 dummy_internal_proof: OnceLock::new(),
264 #[cfg(feature = "evm-prove")]
265 halo2_params_reader: CacheHalo2ParamsReader::new_with_default_params_dir(),
266 #[cfg(feature = "evm-prove")]
267 halo2_pk: OnceLock::new(),
268 _phantom: PhantomData,
269 })
270 }
271
272 pub fn build<P: AsRef<Path>>(
275 &self,
276 guest_opts: GuestOptions,
277 pkg_dir: P,
278 target_filter: &Option<TargetFilter>,
279 init_file_name: Option<&str>, ) -> Result<Elf, SdkError> {
281 self.app_config
282 .app_vm_config
283 .write_to_init_file(pkg_dir.as_ref(), init_file_name)?;
284 let pkg = get_package(pkg_dir.as_ref());
285 let target_dir = match build_guest_package(&pkg, &guest_opts, None, target_filter) {
286 Ok(target_dir) => target_dir,
287 Err(Some(code)) => {
288 return Err(SdkError::BuildFailedWithCode(code));
289 }
290 Err(None) => {
291 return Err(SdkError::BuildFailed);
292 }
293 };
294
295 let elf_path =
296 find_unique_executable(pkg_dir, target_dir, target_filter).map_err(SdkError::Other)?;
297 let data = read(&elf_path)?;
298 Elf::decode(&data, MEM_SIZE as u32).map_err(SdkError::Other)
299 }
300
301 pub fn transpiler(&self) -> Result<&Transpiler<F>, SdkError> {
303 self.transpiler
304 .as_ref()
305 .ok_or(SdkError::TranspilerNotAvailable)
306 }
307 pub fn set_transpiler(&mut self, transpiler: Transpiler<F>) {
308 self.transpiler = Some(transpiler);
309 }
310 pub fn with_transpiler(mut self, transpiler: Transpiler<F>) -> Self {
311 self.set_transpiler(transpiler);
312 self
313 }
314
315 pub fn convert_to_exe(
316 &self,
317 executable: impl Into<ExecutableFormat>,
318 ) -> Result<Arc<VmExe<F>>, SdkError> {
319 let executable = executable.into();
320 let exe = match executable {
321 ExecutableFormat::Elf(elf) => {
322 let transpiler = self.transpiler()?.clone();
323 Arc::new(VmExe::from_elf(elf, transpiler)?)
324 }
325 ExecutableFormat::VmExe(exe) => Arc::new(exe),
326 ExecutableFormat::SharedVmExe(exe) => exe,
327 };
328 Ok(exe)
329 }
330}
331
332impl<E, VB, NativeBuilder> GenericSdk<E, VB, NativeBuilder>
335where
336 E: StarkFriEngine<SC = SC>,
337 VB: VmBuilder<E> + Clone,
338 <VB::VmConfig as VmExecutionConfig<F>>::Executor:
339 Executor<F> + MeteredExecutor<F> + PreflightExecutor<F, VB::RecordArena>,
340 NativeBuilder: VmBuilder<E, VmConfig = NativeConfig> + Clone,
341 <NativeConfig as VmExecutionConfig<F>>::Executor:
342 PreflightExecutor<F, <NativeBuilder as VmBuilder<E>>::RecordArena>,
343{
344 pub fn execute(
346 &self,
347 app_exe: impl Into<ExecutableFormat>,
348 inputs: StdIn,
349 ) -> Result<Vec<u8>, SdkError> {
350 let exe = self.convert_to_exe(app_exe)?;
351 let instance = self
352 .executor
353 .instance(&exe)
354 .map_err(VirtualMachineError::from)?;
355 let final_memory = instance
356 .execute(inputs, None)
357 .map_err(VirtualMachineError::from)?
358 .memory;
359 let public_values = extract_public_values(
360 self.executor.config.as_ref().num_public_values,
361 &final_memory.memory,
362 );
363 Ok(public_values)
364 }
365
366 pub fn execute_metered(
369 &self,
370 app_exe: impl Into<ExecutableFormat>,
371 inputs: StdIn,
372 ) -> Result<(Vec<u8>, Vec<Segment>), SdkError> {
373 let app_prover = self.app_prover(app_exe)?;
374
375 let vm = app_prover.vm();
376 let exe = app_prover.exe();
377
378 let ctx = vm.build_metered_ctx();
379 let interpreter = vm
380 .metered_interpreter(&exe)
381 .map_err(VirtualMachineError::from)?;
382
383 let (segments, final_state) = interpreter
384 .execute_metered(inputs, ctx)
385 .map_err(VirtualMachineError::from)?;
386 let public_values = extract_public_values(
387 self.executor.config.as_ref().num_public_values,
388 &final_state.memory.memory,
389 );
390
391 Ok((public_values, segments))
392 }
393
394 pub fn execute_metered_cost(
397 &self,
398 app_exe: impl Into<ExecutableFormat>,
399 inputs: StdIn,
400 ) -> Result<(Vec<u8>, (u64, u64)), SdkError> {
401 let app_prover = self.app_prover(app_exe)?;
402
403 let vm = app_prover.vm();
404 let exe = app_prover.exe();
405
406 let ctx = vm.build_metered_cost_ctx();
407 let interpreter = vm
408 .metered_cost_interpreter(&exe)
409 .map_err(VirtualMachineError::from)?;
410
411 let (cost, final_state) = interpreter
412 .execute_metered_cost(inputs, ctx)
413 .map_err(VirtualMachineError::from)?;
414 let instret = final_state.instret;
415
416 let public_values = extract_public_values(
417 self.executor.config.as_ref().num_public_values,
418 &final_state.memory.memory,
419 );
420
421 Ok((public_values, (cost, instret)))
422 }
423
424 pub fn prove(
443 &self,
444 app_exe: impl Into<ExecutableFormat>,
445 inputs: StdIn,
446 ) -> Result<(VmStarkProof<SC>, AppExecutionCommit), SdkError> {
447 let mut prover = self.prover(app_exe)?;
448 let app_commit = prover.app_prover.app_commit();
449 let proof = prover.prove(inputs)?;
450 Ok((proof, app_commit))
451 }
452
453 #[cfg(feature = "evm-prove")]
454 pub fn prove_evm(
455 &self,
456 app_exe: impl Into<ExecutableFormat>,
457 inputs: StdIn,
458 ) -> Result<EvmProof, SdkError> {
459 let app_exe = self.convert_to_exe(app_exe)?;
460 let mut evm_prover = self.evm_prover(app_exe)?;
461 let proof = evm_prover.prove_evm(inputs)?;
462 Ok(proof)
463 }
464
465 pub fn prover(
471 &self,
472 app_exe: impl Into<ExecutableFormat>,
473 ) -> Result<StarkProver<E, VB, NativeBuilder>, SdkError> {
474 let app_exe = self.convert_to_exe(app_exe)?;
475 let app_pk = self.app_pk();
476 let agg_pk = self.agg_pk();
477 let stark_prover = StarkProver::<E, _, _>::new(
478 self.app_vm_builder.clone(),
479 self.native_builder.clone(),
480 app_pk,
481 app_exe,
482 agg_pk,
483 self.agg_tree_config,
484 )?;
485 Ok(stark_prover)
486 }
487
488 #[cfg(feature = "evm-prove")]
489 pub fn evm_prover(
490 &self,
491 app_exe: impl Into<ExecutableFormat>,
492 ) -> Result<EvmHalo2Prover<E, VB, NativeBuilder>, SdkError> {
493 let app_exe = self.convert_to_exe(app_exe)?;
494 let evm_prover = EvmHalo2Prover::<E, _, _>::new(
495 self.halo2_params_reader(),
496 self.app_vm_builder.clone(),
497 self.native_builder.clone(),
498 self.app_pk(),
499 app_exe,
500 self.agg_pk(),
501 self.halo2_pk().clone(),
502 self.agg_tree_config,
503 )?;
504 Ok(evm_prover)
505 }
506
507 pub fn app_prover(
515 &self,
516 exe: impl Into<ExecutableFormat>,
517 ) -> Result<AppProver<E, VB>, SdkError> {
518 let exe = self.convert_to_exe(exe)?;
519 let app_pk = self.app_pk();
520 let prover = AppProver::<E, VB>::new(
521 self.app_vm_builder.clone(),
522 &app_pk.app_vm_pk,
523 exe,
524 app_pk.leaf_verifier_program_commit(),
525 )?;
526 Ok(prover)
527 }
528
529 pub fn app_keygen(&self) -> (AppProvingKey<VB::VmConfig>, AppVerifyingKey) {
536 let pk = self.app_pk().clone();
537 let vk = pk.get_app_vk();
538 (pk, vk)
539 }
540
541 pub fn app_pk(&self) -> &AppProvingKey<VB::VmConfig> {
546 self.app_pk.get_or_init(|| {
548 AppProvingKey::keygen(self.app_config.clone()).expect("app_keygen failed")
549 })
550 }
551 pub fn set_app_pk(
554 &self,
555 app_pk: AppProvingKey<VB::VmConfig>,
556 ) -> Result<(), AppProvingKey<VB::VmConfig>> {
557 self.app_pk.set(app_pk)
558 }
559 pub fn with_app_pk(self, app_pk: AppProvingKey<VB::VmConfig>) -> Self {
562 let _ = self
563 .set_app_pk(app_pk)
564 .map_err(|_| panic!("app_pk already set"));
565 self
566 }
567
568 pub fn agg_keygen(&self) -> Result<(AggProvingKey, AggVerifyingKey), SdkError> {
576 let agg_pk = self.agg_pk().clone();
577 let agg_vk = agg_pk.get_agg_vk();
578 Ok((agg_pk, agg_vk))
579 }
580
581 pub fn agg_pk(&self) -> &AggProvingKey {
582 self.agg_pk.get_or_init(|| {
584 let (agg_pk, dummy_proof) =
585 AggProvingKey::dummy_proof_and_keygen(self.agg_config).expect("agg_keygen failed");
586 let prev = self.dummy_internal_proof.set(dummy_proof);
587 if prev.is_err() {
588 tracing::debug!("dummy proof already exists, did not overwrite");
589 }
590 agg_pk
591 })
592 }
593 pub fn set_agg_pk(&self, agg_pk: AggProvingKey) -> Result<(), AggProvingKey> {
596 self.agg_pk.set(agg_pk)
597 }
598 pub fn with_agg_pk(self, agg_pk: AggProvingKey) -> Self {
601 let _ = self
602 .set_agg_pk(agg_pk)
603 .map_err(|_| panic!("agg_pk already set"));
604 self
605 }
606 fn dummy_internal_proof(&self) -> &Proof<SC> {
608 self.dummy_internal_proof.get_or_init(|| {
609 let (agg_pk, dummy_proof) =
610 AggProvingKey::dummy_proof_and_keygen(self.agg_config).expect("agg_keygen failed");
611 let prev = self.agg_pk.set(agg_pk);
612 if prev.is_err() {
613 tracing::debug!("agg_pk already exists, did not overwrite");
614 }
615 dummy_proof
616 })
617 }
618 pub fn agg_pk_and_dummy_internal_proof(&self) -> (&AggProvingKey, &Proof<SC>) {
619 (self.agg_pk(), self.dummy_internal_proof())
620 }
621
622 pub fn generate_root_verifier_asm(&self) -> String {
623 let agg_pk = self.agg_pk();
624 let kernel_asm = RootVmVerifierConfig {
625 leaf_fri_params: agg_pk.leaf_vm_pk.fri_params,
626 internal_fri_params: agg_pk.internal_vm_pk.fri_params,
627 num_user_public_values: agg_pk.num_user_public_values(),
628 internal_vm_verifier_commit: agg_pk.internal_committed_exe.get_program_commit().into(),
629 compiler_options: Default::default(),
630 }
631 .build_kernel_asm(
632 &agg_pk.leaf_vm_pk.vm_pk.get_vk(),
633 &agg_pk.internal_vm_pk.vm_pk.get_vk(),
634 );
635 program_to_asm(kernel_asm)
636 }
637
638 #[cfg(feature = "evm-prove")]
639 pub fn halo2_keygen(&self) -> Halo2ProvingKey {
640 self.halo2_pk().clone()
641 }
642
643 #[cfg(feature = "evm-prove")]
644 pub fn halo2_pk(&self) -> &Halo2ProvingKey {
645 let (agg_pk, dummy_internal_proof) = self.agg_pk_and_dummy_internal_proof();
646 self.halo2_pk.get_or_init(|| {
648 Halo2ProvingKey::keygen(
649 self.halo2_config,
650 self.halo2_params_reader(),
651 &DefaultStaticVerifierPvHandler,
652 agg_pk,
653 dummy_internal_proof.clone(),
654 )
655 .expect("halo2_keygen failed")
656 })
657 }
658 #[cfg(feature = "evm-prove")]
661 pub fn set_halo2_pk(&self, halo2_pk: Halo2ProvingKey) -> Result<(), Halo2ProvingKey> {
662 self.halo2_pk.set(halo2_pk)
663 }
664 #[cfg(feature = "evm-prove")]
667 pub fn with_halo2_pk(self, halo2_pk: Halo2ProvingKey) -> Self {
668 let _ = self
669 .set_halo2_pk(halo2_pk)
670 .map_err(|_| "halo2_pk already set");
671 self
672 }
673
674 #[cfg(feature = "evm-prove")]
675 pub fn with_halo2_params_dir(mut self, params_dir: impl AsRef<Path>) -> Self {
676 self.set_halo2_params_dir(params_dir);
677 self
678 }
679 #[cfg(feature = "evm-prove")]
680 pub fn set_halo2_params_dir(&mut self, params_dir: impl AsRef<Path>) {
681 self.halo2_params_reader = CacheHalo2ParamsReader::new(params_dir);
682 }
683
684 pub fn verify_proof(
691 agg_vk: &AggVerifyingKey,
692 expected_app_commit: AppExecutionCommit,
693 proof: &VmStarkProof<SC>,
694 ) -> Result<(), SdkError> {
695 if proof.inner.per_air.len() < 3 {
696 return Err(VmVerificationError::NotEnoughAirs(proof.inner.per_air.len()).into());
697 } else if proof.inner.per_air[0].air_id != PROGRAM_AIR_ID {
698 return Err(VmVerificationError::SystemAirMissing {
699 air_id: PROGRAM_AIR_ID,
700 }
701 .into());
702 } else if proof.inner.per_air[1].air_id != CONNECTOR_AIR_ID {
703 return Err(VmVerificationError::SystemAirMissing {
704 air_id: CONNECTOR_AIR_ID,
705 }
706 .into());
707 } else if proof.inner.per_air[2].air_id != PUBLIC_VALUES_AIR_ID {
708 return Err(VmVerificationError::SystemAirMissing {
709 air_id: PUBLIC_VALUES_AIR_ID,
710 }
711 .into());
712 }
713 let public_values_air_proof_data = &proof.inner.per_air[2];
714
715 let program_commit =
716 proof.inner.commitments.main_trace[PROGRAM_CACHED_TRACE_INDEX].as_ref();
717 let internal_commit: &[_; CHUNK] = &agg_vk.internal_verifier_program_commit.into();
718
719 let (fri_params_final, vk_final, claimed_app_vm_commit) =
720 if program_commit == internal_commit {
721 let internal_pvs: &InternalVmVerifierPvs<_> = public_values_air_proof_data
722 .public_values
723 .as_slice()
724 .borrow();
725 if internal_commit != &internal_pvs.extra_pvs.internal_program_commit {
726 tracing::debug!(
727 "Invalid internal program commit: expected {:?}, got {:?}",
728 internal_commit,
729 internal_pvs.extra_pvs.internal_program_commit
730 );
731 return Err(VmVerificationError::ProgramCommitMismatch { index: 0 }.into());
732 }
733 (
734 agg_vk.internal_fri_params,
735 &agg_vk.internal_vk,
736 internal_pvs.extra_pvs.leaf_verifier_commit,
737 )
738 } else {
739 (agg_vk.leaf_fri_params, &agg_vk.leaf_vk, *program_commit)
740 };
741 let e = E::new(fri_params_final);
742 e.verify(vk_final, &proof.inner)
743 .map_err(VmVerificationError::from)?;
744
745 let pvs: &VmVerifierPvs<_> =
746 public_values_air_proof_data.public_values[..VmVerifierPvs::<u8>::width()].borrow();
747
748 if let Some(exit_code) = pvs.connector.exit_code() {
749 if exit_code != 0 {
750 return Err(VmVerificationError::ExitCodeMismatch {
751 expected: 0,
752 actual: exit_code,
753 }
754 .into());
755 }
756 } else {
757 return Err(VmVerificationError::IsTerminateMismatch {
758 expected: true,
759 actual: false,
760 }
761 .into());
762 }
763
764 let hasher = vm_poseidon2_hasher();
765 let public_values_root = hasher.merkle_root(&proof.user_public_values);
766 if public_values_root != pvs.public_values_commit {
767 tracing::debug!(
768 "Invalid public values root: expected {:?}, got {:?}",
769 pvs.public_values_commit,
770 public_values_root
771 );
772 return Err(VmVerificationError::UserPublicValuesError(
773 UserPublicValuesProofError::UserPublicValuesCommitMismatch,
774 )
775 .into());
776 }
777
778 let claimed_app_exe_commit = compute_exe_commit(
779 &hasher,
780 &pvs.app_commit,
781 &pvs.memory.initial_root,
782 pvs.connector.initial_pc,
783 );
784 let claimed_app_commit =
785 AppExecutionCommit::from_field_commit(claimed_app_exe_commit, claimed_app_vm_commit);
786 let exe_commit_bn254 = claimed_app_commit.app_exe_commit.to_bn254();
787 let vm_commit_bn254 = claimed_app_commit.app_vm_commit.to_bn254();
788
789 if exe_commit_bn254 != expected_app_commit.app_exe_commit.to_bn254() {
790 return Err(SdkError::InvalidAppExeCommit {
791 expected: expected_app_commit.app_exe_commit,
792 actual: claimed_app_commit.app_exe_commit,
793 });
794 } else if vm_commit_bn254 != expected_app_commit.app_vm_commit.to_bn254() {
795 return Err(SdkError::InvalidAppVmCommit {
796 expected: expected_app_commit.app_vm_commit,
797 actual: claimed_app_commit.app_vm_commit,
798 });
799 }
800 Ok(())
801 }
802
803 #[cfg(feature = "evm-verify")]
804 pub fn generate_halo2_verifier_solidity(&self) -> Result<types::EvmHalo2Verifier, SdkError> {
805 use std::{
806 fs::{create_dir_all, write},
807 io::Write,
808 process::{Command, Stdio},
809 };
810
811 use eyre::Context;
812 use forge_fmt::{
813 format, parse, FormatterConfig, IntTypes, MultilineFuncHeaderStyle, NumberUnderscore,
814 QuoteStyle, SingleLineBlockStyle,
815 };
816 use openvm_native_recursion::halo2::wrapper::EvmVerifierByteCode;
817 use serde_json::{json, Value};
818 use snark_verifier::halo2_base::halo2_proofs::poly::commitment::Params;
819 use snark_verifier_sdk::SHPLONK;
820 use tempfile::tempdir;
821 use types::EvmHalo2Verifier;
822
823 use crate::fs::{
824 EVM_HALO2_VERIFIER_BASE_NAME, EVM_HALO2_VERIFIER_INTERFACE_NAME,
825 EVM_HALO2_VERIFIER_PARENT_NAME,
826 };
827
828 let reader = self.halo2_params_reader();
829 let halo2_pk = self.halo2_pk();
830
831 let params = reader.read_params(halo2_pk.wrapper.pinning.metadata.config_params.k);
832 let pinning = &halo2_pk.wrapper.pinning;
833
834 assert_eq!(
835 pinning.metadata.config_params.k as u32,
836 params.k(),
837 "Provided params don't match circuit config"
838 );
839
840 let halo2_verifier_code = gen_evm_verifier_sol_code::<AggregationCircuit, SHPLONK>(
841 ¶ms,
842 pinning.pk.get_vk(),
843 pinning.metadata.num_pvs.clone(),
844 );
845
846 let wrapper_pvs = halo2_pk.wrapper.pinning.metadata.num_pvs.clone();
847 let pvs_length = match wrapper_pvs.first() {
848 Some(v) => v
851 .checked_sub(14)
852 .expect("Unexpected number of static verifier wrapper public values"),
853 _ => panic!("Unexpected amount of instance columns in the static verifier wrapper"),
854 };
855
856 assert!(
857 pvs_length <= 8192,
858 "OpenVM Halo2 verifier contract does not support more than 8192 public values"
859 );
860
861 let openvm_verifier_code = EVM_HALO2_VERIFIER_TEMPLATE
863 .replace("{PUBLIC_VALUES_LENGTH}", &pvs_length.to_string())
864 .replace("{OPENVM_VERSION}", OPENVM_VERSION);
865
866 let formatter_config = FormatterConfig {
867 line_length: 120,
868 tab_width: 4,
869 bracket_spacing: true,
870 int_types: IntTypes::Long,
871 multiline_func_header: MultilineFuncHeaderStyle::AttributesFirst,
872 quote_style: QuoteStyle::Double,
873 number_underscore: NumberUnderscore::Thousands,
874 single_line_statement_blocks: SingleLineBlockStyle::Preserve,
875 override_spacing: false,
876 wrap_comments: false,
877 ignore: vec![],
878 contract_new_lines: false,
879 };
880
881 let parsed_interface =
882 parse(EVM_HALO2_VERIFIER_INTERFACE).expect("Failed to parse interface");
883 let parsed_halo2_verifier_code =
884 parse(&halo2_verifier_code).expect("Failed to parse halo2 verifier code");
885 let parsed_openvm_verifier_code =
886 parse(&openvm_verifier_code).expect("Failed to parse openvm verifier code");
887
888 let mut formatted_interface = String::new();
889 let mut formatted_halo2_verifier_code = String::new();
890 let mut formatted_openvm_verifier_code = String::new();
891
892 format(
893 &mut formatted_interface,
894 parsed_interface,
895 formatter_config.clone(),
896 )
897 .expect("Failed to format interface");
898 format(
899 &mut formatted_halo2_verifier_code,
900 parsed_halo2_verifier_code,
901 formatter_config.clone(),
902 )
903 .expect("Failed to format halo2 verifier code");
904 format(
905 &mut formatted_openvm_verifier_code,
906 parsed_openvm_verifier_code,
907 formatter_config,
908 )
909 .expect("Failed to format openvm verifier code");
910
911 let temp_dir = tempdir()
913 .wrap_err("Failed to create temp dir")
914 .map_err(SdkError::Other)?;
915 let temp_path = temp_dir.path();
916 let root_path = Path::new("src").join(format!("v{}", OPENVM_VERSION));
917
918 let interfaces_path = root_path.join("interfaces");
920
921 create_dir_all(temp_path.join(&interfaces_path))?;
924
925 let interface_file_path = interfaces_path.join(EVM_HALO2_VERIFIER_INTERFACE_NAME);
926 let parent_file_path = root_path.join(EVM_HALO2_VERIFIER_PARENT_NAME);
927 let base_file_path = root_path.join(EVM_HALO2_VERIFIER_BASE_NAME);
928
929 write(temp_path.join(&interface_file_path), &formatted_interface)?;
932 write(
933 temp_path.join(&parent_file_path),
934 &formatted_halo2_verifier_code,
935 )?;
936 write(
937 temp_path.join(&base_file_path),
938 &formatted_openvm_verifier_code,
939 )?;
940
941 let solc_input = json!({
943 "language": "Solidity",
944 "sources": {
945 interface_file_path.to_str().unwrap(): {
946 "content": formatted_interface
947 },
948 parent_file_path.to_str().unwrap(): {
949 "content": formatted_halo2_verifier_code
950 },
951 base_file_path.to_str().unwrap(): {
952 "content": formatted_openvm_verifier_code
953 }
954 },
955 "settings": {
956 "remappings": ["forge-std/=lib/forge-std/src/"],
957 "optimizer": {
958 "enabled": true,
959 "runs": 100000,
960 "details": {
961 "constantOptimizer": false,
962 "yul": false
963 }
964 },
965 "evmVersion": "paris",
966 "viaIR": false,
967 "outputSelection": {
968 "*": {
969 "*": ["metadata", "evm.bytecode.object"]
970 }
971 }
972 }
973 });
974
975 let mut child = Command::new("solc")
976 .current_dir(temp_path)
977 .arg("--standard-json")
978 .stdin(Stdio::piped())
979 .stdout(Stdio::piped())
980 .stderr(Stdio::piped())
981 .spawn()
982 .expect("Failed to spawn solc");
983
984 child
985 .stdin
986 .as_mut()
987 .expect("Failed to open stdin")
988 .write_all(solc_input.to_string().as_bytes())
989 .expect("Failed to write to stdin");
990
991 let output = child.wait_with_output().expect("Failed to read output");
992
993 if !output.status.success() {
994 return Err(SdkError::Other(eyre::eyre!(
995 "solc exited with status {}: {}",
996 output.status,
997 String::from_utf8_lossy(&output.stderr)
998 )));
999 }
1000
1001 let parsed: Value =
1002 serde_json::from_slice(&output.stdout).map_err(|e| SdkError::Other(e.into()))?;
1003
1004 let bytecode = parsed
1005 .get("contracts")
1006 .expect("No 'contracts' field found")
1007 .get(format!("src/v{}/OpenVmHalo2Verifier.sol", OPENVM_VERSION))
1008 .unwrap_or_else(|| {
1009 panic!(
1010 "No 'src/v{}/OpenVmHalo2Verifier.sol' field found",
1011 OPENVM_VERSION
1012 )
1013 })
1014 .get("OpenVmHalo2Verifier")
1015 .expect("No 'OpenVmHalo2Verifier' field found")
1016 .get("evm")
1017 .expect("No 'evm' field found")
1018 .get("bytecode")
1019 .expect("No 'bytecode' field found")
1020 .get("object")
1021 .expect("No 'object' field found")
1022 .as_str()
1023 .expect("No 'object' field found");
1024
1025 let bytecode = hex::decode(bytecode).expect("Invalid hex in Binary");
1026
1027 let evm_verifier = EvmHalo2Verifier {
1028 halo2_verifier_code: formatted_halo2_verifier_code,
1029 openvm_verifier_code: formatted_openvm_verifier_code,
1030 openvm_verifier_interface: formatted_interface,
1031 artifact: EvmVerifierByteCode {
1032 sol_compiler_version: "0.8.19".to_string(),
1033 sol_compiler_options: solc_input.get("settings").unwrap().to_string(),
1034 bytecode,
1035 },
1036 };
1037 Ok(evm_verifier)
1038 }
1039
1040 #[cfg(feature = "evm-verify")]
1041 pub fn verify_evm_halo2_proof(
1043 openvm_verifier: &types::EvmHalo2Verifier,
1044 evm_proof: EvmProof,
1045 ) -> Result<u64, SdkError> {
1046 let calldata = evm_proof.verifier_calldata();
1047 let deployment_code = openvm_verifier.artifact.bytecode.clone();
1048
1049 let gas_cost = snark_verifier::loader::evm::deploy_and_call(deployment_code, calldata)
1050 .map_err(|reason| {
1051 SdkError::Other(eyre::eyre!("Sdk::verify_openvm_evm_proof: {reason:?}"))
1052 })?;
1053
1054 Ok(gas_cost)
1055 }
1056}