1use std::sync::Arc;
2
3use derivative::Derivative;
4use openvm_circuit::{
5 arch::{AirInventoryError, SystemConfig, VirtualMachine, VirtualMachineError, VmCircuitConfig},
6 system::memory::dimensions::MemoryDimensions,
7};
8use openvm_continuations::verifier::{
9 internal::InternalVmVerifierConfig, leaf::LeafVmVerifierConfig, root::RootVmVerifierConfig,
10};
11use openvm_native_circuit::{NativeConfig, NativeCpuBuilder, NATIVE_MAX_TRACE_HEIGHTS};
12use openvm_native_compiler::ir::DIGEST_SIZE;
13use openvm_stark_backend::{
14 config::Val,
15 engine::StarkEngine,
16 p3_field::{BasedVectorSpace, PrimeField32, TwoAdicField},
17};
18use openvm_stark_sdk::{
19 config::{
20 baby_bear_poseidon2::BabyBearPoseidon2Engine,
21 baby_bear_poseidon2_root::BabyBearPoseidon2RootEngine, FriParameters,
22 },
23 engine::StarkFriEngine,
24 openvm_stark_backend::{
25 config::{Com, StarkGenericConfig},
26 keygen::types::MultiStarkVerifyingKey,
27 proof::Proof,
28 },
29};
30use serde::{Deserialize, Serialize};
31use tracing::{info_span, instrument};
32#[cfg(feature = "evm-prove")]
33use {
34 openvm_continuations::static_verifier::StaticVerifierPvHandler,
35 openvm_native_recursion::halo2::{
36 utils::Halo2ParamsReader, verifier::Halo2VerifierProvingKey,
37 wrapper::Halo2WrapperProvingKey,
38 },
39};
40
41#[cfg(feature = "evm-prove")]
42use crate::config::Halo2Config;
43use crate::{
44 commit::VmCommittedExe,
45 config::{AggregationConfig, AppConfig},
46 keygen::{
47 dummy::{compute_root_proof_heights, dummy_internal_proof_riscv_app_vm},
48 perm::AirIdPermutation,
49 },
50 prover::vm::types::VmProvingKey,
51 util::check_max_constraint_degrees,
52 RootSC, SC,
53};
54
55pub mod asm;
56pub(crate) mod dummy;
57pub mod perm;
58#[cfg(feature = "evm-prove")]
59pub mod static_verifier;
60
61#[derive(Clone, Serialize, Deserialize)]
63pub struct AppProvingKey<VC> {
64 pub leaf_committed_exe: Arc<VmCommittedExe<SC>>,
67 pub leaf_fri_params: FriParameters,
68 pub app_vm_pk: Arc<VmProvingKey<SC, VC>>,
69}
70
71#[derive(Clone, Serialize, Deserialize)]
72pub struct AppVerifyingKey {
73 pub fri_params: FriParameters,
75 pub vk: MultiStarkVerifyingKey<SC>,
77 pub memory_dimensions: MemoryDimensions,
78}
79
80#[derive(Clone, Serialize, Deserialize)]
85pub struct AggProvingKey {
86 pub leaf_vm_pk: Arc<VmProvingKey<SC, NativeConfig>>,
87 pub internal_vm_pk: Arc<VmProvingKey<SC, NativeConfig>>,
88 pub internal_committed_exe: Arc<VmCommittedExe<SC>>,
89 pub root_verifier_pk: RootVerifierProvingKey,
90}
91
92#[derive(Clone, Serialize, Deserialize)]
93pub struct AggVerifyingKey {
94 pub(super) leaf_fri_params: FriParameters,
95 pub(super) leaf_vk: MultiStarkVerifyingKey<SC>,
96 pub(super) internal_fri_params: FriParameters,
98 pub(super) internal_vk: MultiStarkVerifyingKey<SC>,
99 pub(super) internal_verifier_program_commit: Com<SC>,
100}
101
102#[cfg(feature = "evm-prove")]
106#[derive(Clone, Serialize, Deserialize)]
107pub struct Halo2ProvingKey {
108 pub verifier: Arc<Halo2VerifierProvingKey>,
110 pub wrapper: Arc<Halo2WrapperProvingKey>,
113 pub profiling: bool,
115}
116
117impl<VC> AppProvingKey<VC>
118where
119 VC: Clone + VmCircuitConfig<SC> + AsRef<SystemConfig>,
120{
121 pub fn keygen(config: AppConfig<VC>) -> Result<Self, AirInventoryError> {
122 let app_engine = BabyBearPoseidon2Engine::new(config.app_fri_params.fri_params);
123 let app_vm_pk = {
124 let vm_pk = config.app_vm_config.create_airs()?.keygen(&app_engine);
125 assert!(
126 vm_pk.max_constraint_degree
127 <= config.app_fri_params.fri_params.max_constraint_degree()
128 );
129 check_max_constraint_degrees(
130 config.app_vm_config.as_ref(),
131 &config.app_fri_params.fri_params,
132 );
133 VmProvingKey {
134 fri_params: config.app_fri_params.fri_params,
135 vm_config: config.app_vm_config.clone(),
136 vm_pk,
137 }
138 };
139 check_recursive_verifier_size(
140 &app_vm_pk.vm_pk.get_vk(),
141 config.app_fri_params.fri_params,
142 config.leaf_fri_params.fri_params.log_blowup,
143 );
144 let leaf_committed_exe = {
145 let leaf_engine = BabyBearPoseidon2Engine::new(config.leaf_fri_params.fri_params);
146 let leaf_program = LeafVmVerifierConfig {
147 app_fri_params: config.app_fri_params.fri_params,
148 app_system_config: config.app_vm_config.as_ref().clone(),
149 compiler_options: config.compiler_options,
150 }
151 .build_program(&app_vm_pk.vm_pk.get_vk());
152 Arc::new(VmCommittedExe::commit(
153 leaf_program.into(),
154 leaf_engine.config().pcs(),
155 ))
156 };
157 Ok(Self {
158 leaf_committed_exe,
159 leaf_fri_params: config.leaf_fri_params.fri_params,
160 app_vm_pk: Arc::new(app_vm_pk),
161 })
162 }
163
164 pub fn num_public_values(&self) -> usize {
165 self.app_vm_pk.vm_config.as_ref().num_public_values
166 }
167
168 pub fn get_app_vk(&self) -> AppVerifyingKey {
169 AppVerifyingKey {
170 fri_params: self.app_vm_pk.fri_params,
171 vk: self.app_vm_pk.vm_pk.get_vk(),
172 memory_dimensions: self
173 .app_vm_pk
174 .vm_config
175 .as_ref()
176 .memory_config
177 .memory_dimensions(),
178 }
179 }
180
181 pub fn leaf_verifier_program_commit(&self) -> Com<SC> {
182 self.leaf_committed_exe.get_program_commit()
183 }
184
185 pub fn app_fri_params(&self) -> FriParameters {
186 self.app_vm_pk.fri_params
187 }
188
189 pub fn vm_config(&self) -> &VC {
190 &self.app_vm_pk.vm_config
191 }
192
193 pub fn app_config(&self) -> AppConfig<VC> {
194 AppConfig {
195 app_fri_params: self.app_fri_params().into(),
196 app_vm_config: self.vm_config().clone(),
197 leaf_fri_params: self.leaf_fri_params.into(),
198 compiler_options: Default::default(),
199 }
200 }
201}
202
203fn check_recursive_verifier_size<SC: StarkGenericConfig>(
209 vk: &MultiStarkVerifyingKey<SC>,
210 fri_params: FriParameters,
211 next_log_blowup: usize,
212) where
213 Val<SC>: PrimeField32 + TwoAdicField,
214{
215 let vk = &vk.inner;
216
217 let mut rounds = vec![];
219
220 rounds.extend(
222 vk.per_air
223 .iter()
224 .filter_map(|vk| vk.params.width.preprocessed)
225 .map(|width| (width, 1, 2)),
226 );
227
228 let common_main_total_width = vk
229 .per_air
230 .iter()
231 .map(|vk| vk.params.width.common_main)
232 .sum();
233 rounds.push((common_main_total_width, vk.per_air.len(), 2));
234
235 for vk in vk.per_air.iter() {
236 for &cached_main_width in &vk.params.width.cached_mains {
237 rounds.push((cached_main_width, 1, 2));
238 }
239 }
240
241 let mut after_challenge_rounds = vec![];
242 for vk in vk.per_air.iter() {
243 let widths = &vk.params.width.after_challenge;
244 if widths.len() > after_challenge_rounds.len() {
245 after_challenge_rounds.resize(widths.len(), (0, 0, 2));
246 }
247 for (i, &width) in widths.iter().enumerate() {
248 after_challenge_rounds[i].0 += SC::Challenge::DIMENSION * width;
249 after_challenge_rounds[i].1 += 1;
250 }
251 }
252 rounds.extend(after_challenge_rounds);
253
254 let quotient_round = (
255 vk.per_air
256 .iter()
257 .map(|vk| SC::Challenge::DIMENSION * vk.quotient_degree as usize)
258 .sum(),
259 vk.per_air.len(),
260 1,
261 );
262 rounds.push(quotient_round);
263
264 let fri_reduced_opening_trace_height = fri_params.num_queries
267 * rounds
268 .iter()
269 .map(|(total_width, num_airs, total_pts)| total_pts * (total_width + 2 * num_airs))
270 .sum::<usize>();
271 if fri_reduced_opening_trace_height > (1 << (Val::<SC>::TWO_ADICITY - next_log_blowup)) {
273 tracing::warn!("recursive verifier size may be too large; FriReducedOpening height ({fri_reduced_opening_trace_height}) > {}", 1 << (Val::<SC>::TWO_ADICITY - next_log_blowup));
274 }
275 let native_max_height: u32 = *NATIVE_MAX_TRACE_HEIGHTS.iter().max().unwrap();
278 if fri_reduced_opening_trace_height as u32 > native_max_height {
279 tracing::warn!(
280 "recursive verifier size may violate log up soundness constraints; {} > {}",
281 fri_reduced_opening_trace_height,
282 native_max_height
283 );
284 }
285}
286
287impl AggProvingKey {
288 #[instrument(
289 name = "agg_stark_keygen",
290 fields(group = "agg_stark_keygen"),
291 skip_all
292 )]
293 pub fn keygen(config: AggregationConfig) -> Result<Self, VirtualMachineError> {
294 let (pk, _) = Self::dummy_proof_and_keygen(config)?;
295 Ok(pk)
296 }
297
298 #[tracing::instrument(level = "info", fields(group = "agg_keygen"), skip_all)]
299 pub(crate) fn dummy_proof_and_keygen(
300 config: AggregationConfig,
301 ) -> Result<(Self, Proof<SC>), VirtualMachineError> {
302 let leaf_vm_config = config.leaf_vm_config();
303 let internal_vm_config = config.internal_vm_config();
304 let root_vm_config = config.root_verifier_vm_config();
305
306 let leaf_engine = BabyBearPoseidon2Engine::new(config.leaf_fri_params);
307 let leaf_vm_pk = {
308 let (_, vm_pk) = VirtualMachine::new_with_keygen(
309 leaf_engine,
310 NativeCpuBuilder,
311 leaf_vm_config.clone(),
312 )?;
313 assert!(vm_pk.max_constraint_degree <= config.leaf_fri_params.max_constraint_degree());
314 check_max_constraint_degrees(&leaf_vm_config.system, &config.leaf_fri_params);
315 Arc::new(VmProvingKey {
316 fri_params: config.leaf_fri_params,
317 vm_config: leaf_vm_config,
318 vm_pk,
319 })
320 };
321 let leaf_vm_vk = leaf_vm_pk.vm_pk.get_vk();
322 check_recursive_verifier_size(
323 &leaf_vm_vk,
324 config.leaf_fri_params,
325 config.internal_fri_params.log_blowup,
326 );
327
328 let internal_engine = BabyBearPoseidon2Engine::new(config.internal_fri_params);
329 let (internal_vm, vm_pk) = VirtualMachine::new_with_keygen(
330 internal_engine,
331 NativeCpuBuilder,
332 internal_vm_config.clone(),
333 )?;
334 check_max_constraint_degrees(&internal_vm_config.system, &config.internal_fri_params);
335 assert!(vm_pk.max_constraint_degree <= config.internal_fri_params.max_constraint_degree());
336 let internal_vm_pk = Arc::new(VmProvingKey {
337 fri_params: config.internal_fri_params,
338 vm_config: internal_vm_config,
339 vm_pk,
340 });
341 let internal_vm_vk = internal_vm_pk.vm_pk.get_vk();
342 check_recursive_verifier_size(
343 &internal_vm_vk,
344 config.internal_fri_params,
345 config.internal_fri_params.log_blowup,
346 );
347
348 let internal_program = InternalVmVerifierConfig {
349 leaf_fri_params: config.leaf_fri_params,
350 internal_fri_params: config.internal_fri_params,
351 compiler_options: config.compiler_options,
352 }
353 .build_program(&leaf_vm_vk, &internal_vm_vk);
354 let internal_committed_exe = Arc::new(VmCommittedExe::commit(
355 internal_program.into(),
356 internal_vm.engine.config().pcs(),
357 ));
358
359 let internal_proof = dummy_internal_proof_riscv_app_vm(
360 leaf_vm_pk.clone(),
361 internal_vm_pk.clone(),
362 internal_committed_exe.clone(),
363 config.max_num_user_public_values,
364 )?;
365
366 let root_verifier_pk = {
367 let mut root_engine = BabyBearPoseidon2RootEngine::new(config.root_fri_params);
368 root_engine.max_constraint_degree = config.root_max_constraint_degree;
369 let root_program = RootVmVerifierConfig {
370 leaf_fri_params: config.leaf_fri_params,
371 internal_fri_params: config.internal_fri_params,
372 num_user_public_values: config.max_num_user_public_values,
373 internal_vm_verifier_commit: internal_committed_exe.get_program_commit().into(),
374 compiler_options: config.compiler_options,
375 }
376 .build_program(&leaf_vm_vk, &internal_vm_vk);
377 let (mut vm, mut vm_pk) = VirtualMachine::new_with_keygen(
378 root_engine,
379 NativeCpuBuilder,
380 root_vm_config.clone(),
381 )?;
382 let root_committed_exe = Arc::new(VmCommittedExe::commit(
383 root_program.into(),
384 vm.engine.config().pcs(),
385 ));
386
387 assert!(vm_pk.max_constraint_degree <= config.root_fri_params.max_constraint_degree());
388
389 let air_heights =
390 compute_root_proof_heights(&mut vm, &root_committed_exe, &internal_proof)?;
391 let root_air_perm = AirIdPermutation::compute(&air_heights);
392 root_air_perm.permute(&mut vm_pk.per_air);
395 #[cfg(not(feature = "legacy-v1-3-evm-verifier"))]
396 for thc in &mut vm_pk.trace_height_constraints {
397 root_air_perm.permute(&mut thc.coefficients);
398 }
399
400 RootVerifierProvingKey {
401 vm_pk: Arc::new(VmProvingKey {
402 fri_params: config.root_fri_params,
403 vm_config: root_vm_config,
404 vm_pk,
405 }),
406 root_committed_exe,
407 air_heights,
408 }
409 };
410 Ok((
411 Self {
412 leaf_vm_pk,
413 internal_vm_pk,
414 internal_committed_exe,
415 root_verifier_pk,
416 },
417 internal_proof,
418 ))
419 }
420
421 pub fn get_agg_vk(&self) -> AggVerifyingKey {
422 let leaf_fri_params = self.leaf_vm_pk.fri_params;
423 let leaf_vk = self.leaf_vm_pk.vm_pk.get_vk();
424 let internal_fri_params = self.internal_vm_pk.fri_params;
425 let internal_vk = self.internal_vm_pk.vm_pk.get_vk();
426 let internal_verifier_program_commit = self.internal_committed_exe.get_program_commit();
427 AggVerifyingKey {
428 leaf_fri_params,
429 leaf_vk,
430 internal_fri_params,
431 internal_vk,
432 internal_verifier_program_commit,
433 }
434 }
435
436 pub fn num_user_public_values(&self) -> usize {
437 self.root_verifier_pk
438 .vm_pk
439 .vm_config
440 .system
441 .num_public_values
442 - (2 * DIGEST_SIZE)
443 }
444}
445
446#[derive(Serialize, Deserialize, Derivative)]
451#[derivative(Clone(bound = "Com<SC>: Clone"))]
452pub struct RootVerifierProvingKey {
453 pub vm_pk: Arc<VmProvingKey<RootSC, NativeConfig>>,
458 pub root_committed_exe: Arc<VmCommittedExe<RootSC>>,
460 pub air_heights: Vec<u32>,
462}
463
464#[cfg(feature = "evm-prove")]
465impl RootVerifierProvingKey {
466 pub(crate) fn air_id_permutation(&self) -> AirIdPermutation {
467 AirIdPermutation::compute(&self.air_heights)
468 }
469}
470
471#[cfg(feature = "evm-prove")]
472impl Halo2ProvingKey {
473 #[tracing::instrument(level = "info", fields(group = "halo2_keygen"), skip_all)]
477 pub fn keygen(
478 halo2_config: Halo2Config,
479 reader: &impl Halo2ParamsReader,
480 pv_handler: &impl StaticVerifierPvHandler,
481 agg_pk: &AggProvingKey,
482 dummy_internal_proof: Proof<SC>,
483 ) -> Result<Self, VirtualMachineError> {
484 let dummy_root_proof = agg_pk
485 .root_verifier_pk
486 .generate_dummy_root_proof(dummy_internal_proof)?;
487 let verifier = agg_pk.root_verifier_pk.keygen_static_verifier(
488 &reader.read_params(halo2_config.verifier_k),
489 dummy_root_proof,
490 pv_handler,
491 );
492 let dummy_snark = verifier.generate_dummy_snark(reader);
493 let wrapper = if let Some(wrapper_k) = halo2_config.wrapper_k {
494 Halo2WrapperProvingKey::keygen(&reader.read_params(wrapper_k), dummy_snark)
495 } else {
496 Halo2WrapperProvingKey::keygen_auto_tune(reader, dummy_snark)
497 };
498 Ok(Halo2ProvingKey {
499 verifier: Arc::new(verifier),
500 wrapper: Arc::new(wrapper),
501 profiling: halo2_config.profiling,
502 })
503 }
504}
505
506pub fn _leaf_keygen(
508 fri_params: FriParameters,
509 leaf_vm_config: NativeConfig,
510) -> Result<Arc<VmProvingKey<SC, NativeConfig>>, AirInventoryError> {
511 let leaf_engine = BabyBearPoseidon2Engine::new(fri_params);
512 let leaf_vm_pk = info_span!("keygen", group = "leaf").in_scope(|| {
513 leaf_vm_config
514 .create_airs()
515 .map(|airs| airs.keygen(&leaf_engine))
516 })?;
517 Ok(Arc::new(VmProvingKey {
518 fri_params,
519 vm_config: leaf_vm_config,
520 vm_pk: leaf_vm_pk,
521 }))
522}