1use std::sync::{Arc, OnceLock};
2
3use getset::Getters;
4use itertools::Itertools;
5use openvm_circuit::{
6 arch::{
7 hasher::poseidon2::{vm_poseidon2_hasher, Poseidon2Hasher},
8 instructions::exe::VmExe,
9 verify_segments, ContinuationVmProof, ContinuationVmProver, Executor, MeteredExecutor,
10 PreflightExecutor, VerifiedExecutionPayload, VirtualMachine, VirtualMachineError,
11 VmBuilder, VmExecutionConfig, VmInstance, VmVerificationError,
12 },
13 system::memory::CHUNK,
14};
15use openvm_stark_backend::{
16 config::{Com, Val},
17 keygen::types::MultiStarkVerifyingKey,
18 p3_field::PrimeField32,
19};
20use openvm_stark_sdk::{
21 config::baby_bear_poseidon2::BabyBearPoseidon2Engine,
22 engine::{StarkEngine, StarkFriEngine},
23};
24use tracing::info_span;
25
26use crate::{
27 commit::{AppExecutionCommit, CommitBytes},
28 keygen::AppVerifyingKey,
29 prover::vm::{new_local_prover, types::VmProvingKey},
30 util::check_max_constraint_degrees,
31 StdIn, F, SC,
32};
33
34#[derive(Getters)]
35pub struct AppProver<E, VB>
36where
37 E: StarkEngine,
38 VB: VmBuilder<E>,
39{
40 pub program_name: Option<String>,
41 #[getset(get = "pub")]
42 instance: VmInstance<E, VB>,
43 #[getset(get = "pub")]
44 app_vm_vk: MultiStarkVerifyingKey<E::SC>,
45 #[getset(get = "pub")]
46 leaf_verifier_program_commit: Com<E::SC>,
47
48 app_execution_commit: OnceLock<AppExecutionCommit>,
49}
50
51impl<E, VB> AppProver<E, VB>
52where
53 E: StarkFriEngine,
54 VB: VmBuilder<E>,
55 Val<E::SC>: PrimeField32,
56 Com<E::SC>: AsRef<[Val<E::SC>; CHUNK]> + From<[Val<E::SC>; CHUNK]> + Into<[Val<E::SC>; CHUNK]>,
57{
58 pub fn new(
65 vm_builder: VB,
66 app_vm_pk: &VmProvingKey<E::SC, VB::VmConfig>,
67 app_exe: Arc<VmExe<Val<E::SC>>>,
68 leaf_verifier_program_commit: Com<E::SC>,
69 ) -> Result<Self, VirtualMachineError> {
70 let instance = new_local_prover(vm_builder, app_vm_pk, app_exe)?;
71 let app_vm_vk = app_vm_pk.vm_pk.get_vk();
72
73 Ok(Self::new_from_instance(
74 instance,
75 app_vm_vk,
76 leaf_verifier_program_commit,
77 ))
78 }
79
80 pub fn new_from_instance(
81 instance: VmInstance<E, VB>,
82 app_vm_vk: MultiStarkVerifyingKey<E::SC>,
83 leaf_verifier_program_commit: Com<E::SC>,
84 ) -> Self {
85 Self {
86 program_name: None,
87 instance,
88 app_vm_vk,
89 leaf_verifier_program_commit,
90 app_execution_commit: OnceLock::new(),
91 }
92 }
93
94 pub fn set_program_name(&mut self, program_name: impl AsRef<str>) -> &mut Self {
95 self.program_name = Some(program_name.as_ref().to_string());
96 self
97 }
98 pub fn with_program_name(mut self, program_name: impl AsRef<str>) -> Self {
99 self.set_program_name(program_name);
100 self
101 }
102
103 pub fn app_commit(&self) -> AppExecutionCommit {
106 *self.app_execution_commit.get_or_init(|| {
107 AppExecutionCommit::compute::<E::SC>(
108 &self.instance().vm.config().as_ref().memory_config,
109 self.instance().exe(),
110 self.instance().program_commitment().clone(),
111 self.leaf_verifier_program_commit.clone(),
112 )
113 })
114 }
115
116 pub fn app_program_commit(&self) -> Com<E::SC> {
117 self.instance().program_commitment().clone()
118 }
119
120 pub fn prove(
125 &mut self,
126 input: StdIn<Val<E::SC>>,
127 ) -> Result<ContinuationVmProof<E::SC>, VirtualMachineError>
128 where
129 <VB::VmConfig as VmExecutionConfig<Val<E::SC>>>::Executor: Executor<Val<E::SC>>
130 + MeteredExecutor<Val<E::SC>>
131 + PreflightExecutor<Val<E::SC>, VB::RecordArena>,
132 {
133 assert!(
134 self.vm_config().as_ref().continuation_enabled,
135 "Use generate_app_proof_without_continuations instead."
136 );
137 check_max_constraint_degrees(
138 self.vm_config().as_ref(),
139 &self.instance.vm.engine.fri_params(),
140 );
141 let proofs = info_span!(
142 "app proof",
143 group = self
144 .program_name
145 .as_ref()
146 .unwrap_or(&"app_proof".to_string())
147 )
148 .in_scope(|| {
149 #[cfg(feature = "metrics")]
150 metrics::counter!("fri.log_blowup")
151 .absolute(self.instance.vm.engine.fri_params().log_blowup as u64);
152 ContinuationVmProver::prove(&mut self.instance, input)
153 })?;
154 let res = verify_segments(
157 &self.instance.vm.engine,
158 &self.app_vm_vk,
159 &proofs.per_segment,
160 )?;
161 let app_exe_commit_u32s = self.app_commit().app_exe_commit.to_u32_digest();
162 let exe_commit_u32s = res.exe_commit.map(|x| x.as_canonical_u32());
163 if exe_commit_u32s != app_exe_commit_u32s {
164 return Err(VmVerificationError::ExeCommitMismatch {
165 expected: app_exe_commit_u32s,
166 actual: exe_commit_u32s,
167 }
168 .into());
169 }
170 Ok(proofs)
171 }
172
173 pub fn exe(&self) -> Arc<VmExe<Val<E::SC>>> {
175 self.instance.exe().clone()
176 }
177
178 pub fn vm(&self) -> &VirtualMachine<E, VB> {
180 &self.instance.vm
181 }
182
183 pub fn vm_config(&self) -> &VB::VmConfig {
185 self.instance.vm.config()
186 }
187}
188
189pub struct VerifiedAppArtifacts {
192 pub app_exe_commit: CommitBytes,
200 pub user_public_values: Vec<u8>,
201}
202
203pub fn verify_app_proof(
212 app_vk: &AppVerifyingKey,
213 proof: &ContinuationVmProof<SC>,
214) -> Result<VerifiedAppArtifacts, VmVerificationError> {
215 static POSEIDON2_HASHER: OnceLock<Poseidon2Hasher<F>> = OnceLock::new();
216 let engine = BabyBearPoseidon2Engine::new(app_vk.fri_params);
217 let VerifiedExecutionPayload {
218 exe_commit,
219 final_memory_root,
220 } = verify_segments(&engine, &app_vk.vk, &proof.per_segment)?;
221
222 proof.user_public_values.verify(
223 POSEIDON2_HASHER.get_or_init(vm_poseidon2_hasher),
224 app_vk.memory_dimensions,
225 final_memory_root,
226 )?;
227
228 let app_exe_commit = CommitBytes::from_u32_digest(&exe_commit.map(|x| x.as_canonical_u32()));
229 let user_public_values = proof
231 .user_public_values
232 .public_values
233 .iter()
234 .map(|x| x.as_canonical_u32().try_into().unwrap())
235 .collect_vec();
236 Ok(VerifiedAppArtifacts {
237 app_exe_commit,
238 user_public_values,
239 })
240}