1use std::sync::Arc;
2
3use derivative::Derivative;
4use openvm_circuit::{
6 arch::{AirInventoryError, SystemConfig, VirtualMachine, VirtualMachineError, VmCircuitConfig},
7 system::memory::dimensions::MemoryDimensions,
8};
9use openvm_continuations::verifier::{
10 internal::InternalVmVerifierConfig, leaf::LeafVmVerifierConfig, root::RootVmVerifierConfig,
11};
12use openvm_native_circuit::{NativeConfig, NativeCpuBuilder};
13use openvm_native_compiler::ir::DIGEST_SIZE;
14use openvm_stark_backend::{
15 config::Val,
16 engine::StarkEngine,
17 p3_field::{FieldExtensionAlgebra, PrimeField32, TwoAdicField},
18};
19use openvm_stark_sdk::{
20 config::{
21 baby_bear_poseidon2::BabyBearPoseidon2Engine,
22 baby_bear_poseidon2_root::BabyBearPoseidon2RootEngine, FriParameters,
23 },
24 engine::StarkFriEngine,
25 openvm_stark_backend::{
26 config::{Com, StarkGenericConfig},
27 keygen::types::MultiStarkVerifyingKey,
28 proof::Proof,
29 },
30};
31use serde::{Deserialize, Serialize};
32use tracing::{info_span, instrument};
33#[cfg(feature = "evm-prove")]
34use {
35 openvm_continuations::static_verifier::StaticVerifierPvHandler,
36 openvm_native_recursion::halo2::{
37 utils::Halo2ParamsReader, verifier::Halo2VerifierProvingKey,
38 wrapper::Halo2WrapperProvingKey,
39 },
40};
41
42#[cfg(feature = "evm-prove")]
43use crate::config::Halo2Config;
44use crate::{
45 commit::VmCommittedExe,
46 config::{AggregationConfig, AppConfig},
47 keygen::{
48 dummy::{compute_root_proof_heights, dummy_internal_proof_riscv_app_vm},
49 perm::AirIdPermutation,
50 },
51 prover::vm::types::VmProvingKey,
52 util::check_max_constraint_degrees,
53 RootSC, SC,
54};
55
56pub mod asm;
57pub(crate) mod dummy;
58pub mod perm;
59#[cfg(feature = "evm-prove")]
60pub mod static_verifier;
61
62#[derive(Clone, Serialize, Deserialize)]
64pub struct AppProvingKey<VC> {
65 pub leaf_committed_exe: Arc<VmCommittedExe<SC>>,
68 pub leaf_fri_params: FriParameters,
69 pub app_vm_pk: Arc<VmProvingKey<SC, VC>>,
70}
71
72#[derive(Clone, Serialize, Deserialize)]
73pub struct AppVerifyingKey {
74 pub fri_params: FriParameters,
76 pub vk: MultiStarkVerifyingKey<SC>,
78 pub memory_dimensions: MemoryDimensions,
79}
80
81#[derive(Clone, Serialize, Deserialize)]
86pub struct AggProvingKey {
87 pub leaf_vm_pk: Arc<VmProvingKey<SC, NativeConfig>>,
88 pub internal_vm_pk: Arc<VmProvingKey<SC, NativeConfig>>,
89 pub internal_committed_exe: Arc<VmCommittedExe<SC>>,
90 pub root_verifier_pk: RootVerifierProvingKey,
91}
92
93#[derive(Clone, Serialize, Deserialize)]
94pub struct AggVerifyingKey {
95 pub(super) leaf_fri_params: FriParameters,
96 pub(super) leaf_vk: MultiStarkVerifyingKey<SC>,
97 pub(super) internal_fri_params: FriParameters,
99 pub(super) internal_vk: MultiStarkVerifyingKey<SC>,
100 pub(super) internal_verifier_program_commit: Com<SC>,
101}
102
103#[cfg(feature = "evm-prove")]
107#[derive(Clone, Serialize, Deserialize)]
108pub struct Halo2ProvingKey {
109 pub verifier: Arc<Halo2VerifierProvingKey>,
111 pub wrapper: Arc<Halo2WrapperProvingKey>,
114 pub profiling: bool,
116}
117
118impl<VC> AppProvingKey<VC>
119where
120 VC: Clone + VmCircuitConfig<SC> + AsRef<SystemConfig>,
121{
122 pub fn keygen(config: AppConfig<VC>) -> Result<Self, AirInventoryError> {
123 let app_engine = BabyBearPoseidon2Engine::new(config.app_fri_params.fri_params);
124 let app_vm_pk = {
125 let vm_pk = config.app_vm_config.create_airs()?.keygen(&app_engine);
126 assert!(
127 vm_pk.max_constraint_degree
128 <= config.app_fri_params.fri_params.max_constraint_degree()
129 );
130 check_max_constraint_degrees(
131 config.app_vm_config.as_ref(),
132 &config.app_fri_params.fri_params,
133 );
134 VmProvingKey {
135 fri_params: config.app_fri_params.fri_params,
136 vm_config: config.app_vm_config.clone(),
137 vm_pk,
138 }
139 };
140 check_recursive_verifier_size(
141 &app_vm_pk.vm_pk.get_vk(),
142 config.app_fri_params.fri_params,
143 config.leaf_fri_params.fri_params.log_blowup,
144 );
145 let leaf_committed_exe = {
146 let leaf_engine = BabyBearPoseidon2Engine::new(config.leaf_fri_params.fri_params);
147 let leaf_program = LeafVmVerifierConfig {
148 app_fri_params: config.app_fri_params.fri_params,
149 app_system_config: config.app_vm_config.as_ref().clone(),
150 compiler_options: config.compiler_options,
151 }
152 .build_program(&app_vm_pk.vm_pk.get_vk());
153 Arc::new(VmCommittedExe::commit(
154 leaf_program.into(),
155 leaf_engine.config().pcs(),
156 ))
157 };
158 Ok(Self {
159 leaf_committed_exe,
160 leaf_fri_params: config.leaf_fri_params.fri_params,
161 app_vm_pk: Arc::new(app_vm_pk),
162 })
163 }
164
165 pub fn num_public_values(&self) -> usize {
166 self.app_vm_pk.vm_config.as_ref().num_public_values
167 }
168
169 pub fn get_app_vk(&self) -> AppVerifyingKey {
170 AppVerifyingKey {
171 fri_params: self.app_vm_pk.fri_params,
172 vk: self.app_vm_pk.vm_pk.get_vk(),
173 memory_dimensions: self
174 .app_vm_pk
175 .vm_config
176 .as_ref()
177 .memory_config
178 .memory_dimensions(),
179 }
180 }
181
182 pub fn leaf_verifier_program_commit(&self) -> Com<SC> {
183 self.leaf_committed_exe.get_program_commit()
184 }
185
186 pub fn app_fri_params(&self) -> FriParameters {
187 self.app_vm_pk.fri_params
188 }
189
190 pub fn vm_config(&self) -> &VC {
191 &self.app_vm_pk.vm_config
192 }
193
194 pub fn app_config(&self) -> AppConfig<VC> {
195 AppConfig {
196 app_fri_params: self.app_fri_params().into(),
197 app_vm_config: self.vm_config().clone(),
198 leaf_fri_params: self.leaf_fri_params.into(),
199 compiler_options: Default::default(),
200 }
201 }
202}
203
204fn check_recursive_verifier_size<SC: StarkGenericConfig>(
210 vk: &MultiStarkVerifyingKey<SC>,
211 fri_params: FriParameters,
212 next_log_blowup: usize,
213) where
214 Val<SC>: PrimeField32 + TwoAdicField,
215{
216 let vk = &vk.inner;
217
218 let mut rounds = vec![];
220
221 rounds.extend(
223 vk.per_air
224 .iter()
225 .filter_map(|vk| vk.params.width.preprocessed)
226 .map(|width| (width, 1, 2)),
227 );
228
229 let common_main_total_width = vk
230 .per_air
231 .iter()
232 .map(|vk| vk.params.width.common_main)
233 .sum();
234 rounds.push((common_main_total_width, vk.per_air.len(), 2));
235
236 for vk in vk.per_air.iter() {
237 for &cached_main_width in &vk.params.width.cached_mains {
238 rounds.push((cached_main_width, 1, 2));
239 }
240 }
241
242 let mut after_challenge_rounds = vec![];
243 for vk in vk.per_air.iter() {
244 let widths = &vk.params.width.after_challenge;
245 if widths.len() > after_challenge_rounds.len() {
246 after_challenge_rounds.resize(widths.len(), (0, 0, 2));
247 }
248 for (i, &width) in widths.iter().enumerate() {
249 after_challenge_rounds[i].0 += SC::Challenge::D * width;
250 after_challenge_rounds[i].1 += 1;
251 }
252 }
253 rounds.extend(after_challenge_rounds);
254
255 let quotient_round = (
256 vk.per_air
257 .iter()
258 .map(|vk| SC::Challenge::D * vk.quotient_degree as usize)
259 .sum(),
260 vk.per_air.len(),
261 1,
262 );
263 rounds.push(quotient_round);
264
265 let fri_reduced_opening_trace_height = fri_params.num_queries
268 * rounds
269 .iter()
270 .map(|(total_width, num_airs, total_pts)| total_pts * (total_width + 2 * num_airs))
271 .sum::<usize>();
272 if fri_reduced_opening_trace_height > (1 << (Val::<SC>::TWO_ADICITY - next_log_blowup)) {
274 tracing::warn!("recursive verifier size may be too large; FriReducedOpening height ({fri_reduced_opening_trace_height}) > {}", 1 << (Val::<SC>::TWO_ADICITY - next_log_blowup));
275 }
276 if fri_reduced_opening_trace_height as u32 >= Val::<SC>::ORDER_U32 / 200 {
279 tracing::warn!(
280 "recursive verifier size may violate log up soundness constraints; {} > {}",
281 200 * fri_reduced_opening_trace_height,
282 Val::<SC>::ORDER_U32
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}