1use std::sync::Arc;
2
3use derivative::Derivative;
4use dummy::{compute_root_proof_heights, dummy_internal_proof_riscv_app_vm};
5use openvm_circuit::{
6 arch::{VirtualMachine, VmConfig},
7 system::{memory::dimensions::MemoryDimensions, program::trace::VmCommittedExe},
8};
9use openvm_continuations::{
10 static_verifier::StaticVerifierPvHandler,
11 verifier::{
12 internal::InternalVmVerifierConfig, leaf::LeafVmVerifierConfig, root::RootVmVerifierConfig,
13 },
14};
15use openvm_native_circuit::NativeConfig;
16use openvm_native_compiler::ir::DIGEST_SIZE;
17use openvm_native_recursion::halo2::{
18 utils::Halo2ParamsReader, verifier::Halo2VerifierProvingKey, wrapper::Halo2WrapperProvingKey,
19};
20use openvm_stark_backend::{
21 config::Val,
22 p3_field::{FieldExtensionAlgebra, PrimeField32, TwoAdicField},
23};
24use openvm_stark_sdk::{
25 config::{
26 baby_bear_poseidon2::BabyBearPoseidon2Engine,
27 baby_bear_poseidon2_root::BabyBearPoseidon2RootEngine, FriParameters,
28 },
29 engine::StarkFriEngine,
30 openvm_stark_backend::{
31 config::{Com, StarkGenericConfig},
32 keygen::types::MultiStarkVerifyingKey,
33 proof::Proof,
34 Chip,
35 },
36 p3_bn254_fr::Bn254Fr,
37};
38use serde::{Deserialize, Serialize};
39use tracing::info_span;
40
41use crate::{
42 commit::babybear_digest_to_bn254,
43 config::{AggConfig, AggStarkConfig, AppConfig},
44 keygen::perm::AirIdPermutation,
45 prover::vm::types::VmProvingKey,
46 NonRootCommittedExe, RootSC, F, SC,
47};
48
49pub(crate) mod dummy;
50pub mod perm;
51pub mod static_verifier;
52
53#[derive(Clone, Serialize, Deserialize)]
54pub struct AppProvingKey<VC> {
55 pub leaf_committed_exe: Arc<NonRootCommittedExe>,
56 pub leaf_fri_params: FriParameters,
57 pub app_vm_pk: Arc<VmProvingKey<SC, VC>>,
58}
59
60#[derive(Clone, Serialize, Deserialize)]
61pub struct AppVerifyingKey {
62 pub fri_params: FriParameters,
63 pub app_vm_vk: MultiStarkVerifyingKey<SC>,
64 pub memory_dimensions: MemoryDimensions,
65}
66
67#[derive(Clone, Serialize, Deserialize)]
68pub struct AggProvingKey {
69 pub agg_stark_pk: AggStarkProvingKey,
70 pub halo2_pk: Halo2ProvingKey,
71}
72
73#[derive(Clone, Serialize, Deserialize)]
74pub struct AggStarkProvingKey {
75 pub leaf_vm_pk: Arc<VmProvingKey<SC, NativeConfig>>,
76 pub internal_vm_pk: Arc<VmProvingKey<SC, NativeConfig>>,
77 pub internal_committed_exe: Arc<NonRootCommittedExe>,
78 pub root_verifier_pk: RootVerifierProvingKey,
79}
80
81#[derive(Clone, Serialize, Deserialize)]
83pub struct Halo2ProvingKey {
84 pub verifier: Halo2VerifierProvingKey,
86 pub wrapper: Halo2WrapperProvingKey,
88 pub profiling: bool,
90}
91
92impl<VC: VmConfig<F>> AppProvingKey<VC>
93where
94 VC::Executor: Chip<SC>,
95 VC::Periphery: Chip<SC>,
96{
97 pub fn keygen(config: AppConfig<VC>) -> Self {
98 let app_engine = BabyBearPoseidon2Engine::new(config.app_fri_params.fri_params);
99 let app_vm_pk = {
100 let vm = VirtualMachine::new(app_engine, config.app_vm_config.clone());
101 let vm_pk = vm.keygen();
102 assert!(
103 vm_pk.max_constraint_degree
104 <= config.app_fri_params.fri_params.max_constraint_degree()
105 );
106 VmProvingKey {
107 fri_params: config.app_fri_params.fri_params,
108 vm_config: config.app_vm_config.clone(),
109 vm_pk,
110 }
111 };
112 check_recursive_verifier_size(
113 &app_vm_pk.vm_pk.get_vk(),
114 config.app_fri_params.fri_params,
115 config.leaf_fri_params.fri_params.log_blowup,
116 );
117 let leaf_committed_exe = {
118 let leaf_engine = BabyBearPoseidon2Engine::new(config.leaf_fri_params.fri_params);
119 let leaf_program = LeafVmVerifierConfig {
120 app_fri_params: config.app_fri_params.fri_params,
121 app_system_config: config.app_vm_config.system().clone(),
122 compiler_options: config.compiler_options,
123 }
124 .build_program(&app_vm_pk.vm_pk.get_vk());
125 Arc::new(VmCommittedExe::commit(
126 leaf_program.into(),
127 leaf_engine.config.pcs(),
128 ))
129 };
130 Self {
131 leaf_committed_exe,
132 leaf_fri_params: config.leaf_fri_params.fri_params,
133 app_vm_pk: Arc::new(app_vm_pk),
134 }
135 }
136
137 pub fn num_public_values(&self) -> usize {
138 self.app_vm_pk.vm_config.system().num_public_values
139 }
140
141 pub fn get_app_vk(&self) -> AppVerifyingKey {
142 AppVerifyingKey {
143 fri_params: self.app_vm_pk.fri_params,
144 app_vm_vk: self.app_vm_pk.vm_pk.get_vk(),
145 memory_dimensions: self
146 .app_vm_pk
147 .vm_config
148 .system()
149 .memory_config
150 .memory_dimensions(),
151 }
152 }
153
154 pub fn app_fri_params(&self) -> FriParameters {
155 self.app_vm_pk.fri_params
156 }
157
158 pub fn commit_in_bn254(&self) -> Bn254Fr {
159 babybear_digest_to_bn254(&self.commit_in_babybear())
160 }
161
162 pub fn commit_in_babybear(&self) -> [F; DIGEST_SIZE] {
163 self.leaf_committed_exe.get_program_commit().into()
164 }
165}
166
167fn check_recursive_verifier_size<SC: StarkGenericConfig>(
173 vk: &MultiStarkVerifyingKey<SC>,
174 fri_params: FriParameters,
175 next_log_blowup: usize,
176) where
177 Val<SC>: PrimeField32 + TwoAdicField,
178{
179 let vk = &vk.inner;
180
181 let mut rounds = vec![];
183
184 rounds.extend(
186 vk.per_air
187 .iter()
188 .filter_map(|vk| vk.params.width.preprocessed)
189 .map(|width| (width, 1, 2)),
190 );
191
192 let common_main_total_width = vk
193 .per_air
194 .iter()
195 .map(|vk| vk.params.width.common_main)
196 .sum();
197 rounds.push((common_main_total_width, vk.per_air.len(), 2));
198
199 for vk in vk.per_air.iter() {
200 for &cached_main_width in &vk.params.width.cached_mains {
201 rounds.push((cached_main_width, 1, 2));
202 }
203 }
204
205 let mut after_challenge_rounds = vec![];
206 for vk in vk.per_air.iter() {
207 let widths = &vk.params.width.after_challenge;
208 if widths.len() > after_challenge_rounds.len() {
209 after_challenge_rounds.resize(widths.len(), (0, 0, 2));
210 }
211 for (i, &width) in widths.iter().enumerate() {
212 after_challenge_rounds[i].0 += SC::Challenge::D * width;
213 after_challenge_rounds[i].1 += 1;
214 }
215 }
216 rounds.extend(after_challenge_rounds);
217
218 let quotient_round = (
219 vk.per_air
220 .iter()
221 .map(|vk| SC::Challenge::D * vk.quotient_degree as usize)
222 .sum(),
223 vk.per_air.len(),
224 1,
225 );
226 rounds.push(quotient_round);
227
228 let fri_reduced_opening_trace_height = fri_params.num_queries
231 * rounds
232 .iter()
233 .map(|(total_width, num_airs, total_pts)| total_pts * (total_width + 2 * num_airs))
234 .sum::<usize>();
235 if fri_reduced_opening_trace_height > (1 << (Val::<SC>::TWO_ADICITY - next_log_blowup)) {
237 tracing::warn!("recursive verifier size may be too large; FriReducedOpening height ({fri_reduced_opening_trace_height}) > {}", 1 << (Val::<SC>::TWO_ADICITY - next_log_blowup));
238 }
239 if fri_reduced_opening_trace_height as u32 >= Val::<SC>::ORDER_U32 / 200 {
241 tracing::warn!(
242 "recursive verifier size may violate log up soundness constraints; {} > {}",
243 200 * fri_reduced_opening_trace_height,
244 Val::<SC>::ORDER_U32
245 );
246 }
247}
248
249impl AggStarkProvingKey {
250 pub fn keygen(config: AggStarkConfig) -> Self {
251 tracing::info_span!("agg_stark_keygen", group = "agg_stark_keygen")
252 .in_scope(|| Self::dummy_proof_and_keygen(config).0)
253 }
254
255 pub fn dummy_proof_and_keygen(config: AggStarkConfig) -> (Self, Proof<SC>) {
256 let leaf_vm_config = config.leaf_vm_config();
257 let internal_vm_config = config.internal_vm_config();
258 let root_vm_config = config.root_verifier_vm_config();
259
260 let leaf_engine = BabyBearPoseidon2Engine::new(config.leaf_fri_params);
261 let leaf_vm_pk = Arc::new({
262 let vm = VirtualMachine::new(leaf_engine, leaf_vm_config.clone());
263 let vm_pk = vm.keygen();
264 assert!(vm_pk.max_constraint_degree <= config.leaf_fri_params.max_constraint_degree());
265 VmProvingKey {
266 fri_params: config.leaf_fri_params,
267 vm_config: leaf_vm_config,
268 vm_pk,
269 }
270 });
271 let leaf_vm_vk = leaf_vm_pk.vm_pk.get_vk();
272 check_recursive_verifier_size(
273 &leaf_vm_vk,
274 config.leaf_fri_params,
275 config.internal_fri_params.log_blowup,
276 );
277
278 let internal_engine = BabyBearPoseidon2Engine::new(config.internal_fri_params);
279 let internal_vm = VirtualMachine::new(internal_engine, internal_vm_config.clone());
280 let internal_vm_pk = Arc::new({
281 let vm_pk = internal_vm.keygen();
282 assert!(
283 vm_pk.max_constraint_degree <= config.internal_fri_params.max_constraint_degree()
284 );
285 VmProvingKey {
286 fri_params: config.internal_fri_params,
287 vm_config: internal_vm_config,
288 vm_pk,
289 }
290 });
291 let internal_vm_vk = internal_vm_pk.vm_pk.get_vk();
292 check_recursive_verifier_size(
293 &internal_vm_vk,
294 config.internal_fri_params,
295 config.internal_fri_params.log_blowup,
296 );
297
298 let internal_program = InternalVmVerifierConfig {
299 leaf_fri_params: config.leaf_fri_params,
300 internal_fri_params: config.internal_fri_params,
301 compiler_options: config.compiler_options,
302 }
303 .build_program(&leaf_vm_vk, &internal_vm_vk);
304 let internal_committed_exe = Arc::new(VmCommittedExe::<SC>::commit(
305 internal_program.into(),
306 internal_vm.engine.config.pcs(),
307 ));
308
309 let internal_proof = dummy_internal_proof_riscv_app_vm(
310 leaf_vm_pk.clone(),
311 internal_vm_pk.clone(),
312 internal_committed_exe.clone(),
313 config.max_num_user_public_values,
314 );
315
316 let root_verifier_pk = {
317 let mut root_engine = BabyBearPoseidon2RootEngine::new(config.root_fri_params);
318 root_engine.max_constraint_degree = config.root_max_constraint_degree;
319 let root_program = RootVmVerifierConfig {
320 leaf_fri_params: config.leaf_fri_params,
321 internal_fri_params: config.internal_fri_params,
322 num_public_values: config.max_num_user_public_values,
323 internal_vm_verifier_commit: internal_committed_exe.get_program_commit().into(),
324 compiler_options: config.compiler_options,
325 }
326 .build_program(&leaf_vm_vk, &internal_vm_vk);
327 let root_committed_exe = Arc::new(VmCommittedExe::<RootSC>::commit(
328 root_program.into(),
329 root_engine.config.pcs(),
330 ));
331
332 let vm = VirtualMachine::new(root_engine, root_vm_config.clone());
333 let mut vm_pk = vm.keygen();
334 assert!(vm_pk.max_constraint_degree <= config.root_fri_params.max_constraint_degree());
335
336 let (air_heights, _internal_heights) = compute_root_proof_heights(
337 root_vm_config.clone(),
338 root_committed_exe.exe.clone(),
339 &internal_proof,
340 );
341 let root_air_perm = AirIdPermutation::compute(&air_heights);
342 root_air_perm.permute(&mut vm_pk.per_air);
343
344 RootVerifierProvingKey {
345 vm_pk: Arc::new(VmProvingKey {
346 fri_params: config.root_fri_params,
347 vm_config: root_vm_config,
348 vm_pk,
349 }),
350 root_committed_exe,
351 air_heights,
352 }
353 };
354 (
355 Self {
356 leaf_vm_pk,
357 internal_vm_pk,
358 internal_committed_exe,
359 root_verifier_pk,
360 },
361 internal_proof,
362 )
363 }
364
365 pub fn internal_program_commit(&self) -> [F; DIGEST_SIZE] {
366 self.internal_committed_exe.get_program_commit().into()
367 }
368
369 pub fn num_public_values(&self) -> usize {
370 self.root_verifier_pk
371 .vm_pk
372 .vm_config
373 .system
374 .num_public_values
375 - (2 * DIGEST_SIZE)
376 }
377}
378
379#[derive(Serialize, Deserialize, Derivative)]
384#[derivative(Clone(bound = "Com<SC>: Clone"))]
385pub struct RootVerifierProvingKey {
386 pub vm_pk: Arc<VmProvingKey<RootSC, NativeConfig>>,
391 pub root_committed_exe: Arc<VmCommittedExe<RootSC>>,
393 pub air_heights: Vec<usize>,
395 }
399
400impl RootVerifierProvingKey {
401 pub fn air_id_permutation(&self) -> AirIdPermutation {
402 AirIdPermutation::compute(&self.air_heights)
403 }
404}
405
406impl AggProvingKey {
407 #[tracing::instrument(level = "info", fields(group = "agg_keygen"), skip_all)]
411 pub fn keygen(
412 config: AggConfig,
413 reader: &impl Halo2ParamsReader,
414 pv_handler: &impl StaticVerifierPvHandler,
415 ) -> Self {
416 let AggConfig {
417 agg_stark_config,
418 halo2_config,
419 } = config;
420 let (agg_stark_pk, dummy_internal_proof) =
421 AggStarkProvingKey::dummy_proof_and_keygen(agg_stark_config);
422 let dummy_root_proof = agg_stark_pk
423 .root_verifier_pk
424 .generate_dummy_root_proof(dummy_internal_proof);
425 let verifier = agg_stark_pk.root_verifier_pk.keygen_static_verifier(
426 &reader.read_params(halo2_config.verifier_k),
427 dummy_root_proof,
428 pv_handler,
429 );
430 let dummy_snark = verifier.generate_dummy_snark(reader);
431 let wrapper = if let Some(wrapper_k) = halo2_config.wrapper_k {
432 Halo2WrapperProvingKey::keygen(&reader.read_params(wrapper_k), dummy_snark)
433 } else {
434 Halo2WrapperProvingKey::keygen_auto_tune(reader, dummy_snark)
435 };
436 let halo2_pk = Halo2ProvingKey {
437 verifier,
438 wrapper,
439 profiling: halo2_config.profiling,
440 };
441 Self {
442 agg_stark_pk,
443 halo2_pk,
444 }
445 }
446}
447
448pub fn leaf_keygen(
449 fri_params: FriParameters,
450 leaf_vm_config: NativeConfig,
451) -> Arc<VmProvingKey<SC, NativeConfig>> {
452 let leaf_engine = BabyBearPoseidon2Engine::new(fri_params);
453 let leaf_vm_pk = info_span!("keygen", group = "leaf")
454 .in_scope(|| VirtualMachine::new(leaf_engine, leaf_vm_config.clone()).keygen());
455 Arc::new(VmProvingKey {
456 fri_params,
457 vm_config: leaf_vm_config,
458 vm_pk: leaf_vm_pk,
459 })
460}