1use alloc::{string::ToString, vec, vec::Vec};
2
3use miden_core::proof::{ExecutionProof, HashFunction, PrecompileProof, PrecompileStatus, VmProof};
4use miden_processor::{
5 ExecutionError, ExecutionOptions, ExecutionWitness, FastProcessor, PrecompileWitness, Program,
6 StackInputs, StackOutputs, SyncHost, VmWitness,
7 advice::AdviceInputs,
8 trace::{self, VmTrace, build_trace_with_budget},
9};
10
11use crate::{config, prove_stark};
12
13#[derive(Debug, Clone, Eq, PartialEq)]
19pub struct Prover {
20 hash_fn: HashFunction,
21 max_prover_memory_bytes: u64,
22}
23
24impl Prover {
25 pub const DEFAULT_MAX_PROVER_MEMORY_BYTES: u64 = trace::DEFAULT_MAX_PROVER_MEMORY_BYTES;
31
32 pub const fn new() -> Self {
34 Self {
35 hash_fn: HashFunction::Blake3_256,
36 max_prover_memory_bytes: Self::DEFAULT_MAX_PROVER_MEMORY_BYTES,
37 }
38 }
39
40 #[must_use]
42 pub const fn with_hash_fn(mut self, hash_fn: HashFunction) -> Self {
43 self.hash_fn = hash_fn;
44 self
45 }
46
47 #[must_use]
50 pub const fn with_max_prover_memory_bytes(mut self, max_prover_memory_bytes: u64) -> Self {
51 self.max_prover_memory_bytes = max_prover_memory_bytes;
52 self
53 }
54
55 pub const fn max_prover_memory_bytes(&self) -> u64 {
58 self.max_prover_memory_bytes
59 }
60
61 pub fn prove(&self, witness: ExecutionWitness) -> Result<ExecutionProof, ProverError> {
66 let (vm_witness, precompile_witness) = witness.into_parts();
67 let vm = self.prove_vm(vm_witness)?;
68 let Some(precompile_witness) = precompile_witness else {
69 return Ok(ExecutionProof::new(vm, PrecompileStatus::Empty));
70 };
71 Ok(ExecutionProof::new(vm, PrecompileStatus::Deferred(precompile_witness)))
72 }
73
74 pub fn prove_full(&self, witness: ExecutionWitness) -> Result<ExecutionProof, ProverError> {
78 let (vm_witness, precompile_witness) = witness.into_parts();
79 let vm = self.prove_vm(vm_witness)?;
80 let precompile = precompile_witness
81 .map(|witness| self.prove_precompiles(vec![witness]))
82 .transpose()?;
83 let precompile = match precompile {
84 Some(precompile) => PrecompileStatus::Proven(precompile),
85 None => PrecompileStatus::Empty,
86 };
87 Ok(ExecutionProof::new(vm, precompile))
88 }
89
90 pub fn prove_vm_witness(&self, witness: VmWitness) -> Result<ExecutionProof, ProverError> {
95 if witness.has_precompiles() {
96 return Err(ProverError::VmWitnessHasPrecompiles);
97 }
98
99 let vm = self.prove_vm(witness)?;
100 Ok(ExecutionProof::new(vm, PrecompileStatus::Empty))
101 }
102
103 fn prove_vm(&self, witness: VmWitness) -> Result<VmProof, ProverError> {
105 let trace = {
106 let _span = tracing::info_span!("build_miden_vm_trace").entered();
107 build_trace_with_budget(witness, self.max_prover_memory_bytes)
108 .map_err(ProverError::TraceGeneration)?
109 };
110
111 self.prove_vm_trace(trace)
112 }
113
114 pub fn prove_precompiles(
123 &self,
124 witnesses: Vec<PrecompileWitness>,
125 ) -> Result<PrecompileProof, ProverError> {
126 miden_precompiles_prover::prove_precompiles(witnesses, self.hash_fn)
127 .map_err(ProverError::PrecompileProofGeneration)
128 }
129
130 #[cfg(feature = "std")]
131 fn prove_full_trace(
132 &self,
133 trace: VmTrace,
134 precompile: Option<PrecompileWitness>,
135 ) -> Result<ExecutionProof, ProverError> {
136 let vm = self.prove_vm_trace(trace)?;
137 let precompile =
138 precompile.map(|witness| self.prove_precompiles(vec![witness])).transpose()?;
139 let precompile = match precompile {
140 Some(precompile) => PrecompileStatus::Proven(precompile),
141 None => PrecompileStatus::Empty,
142 };
143 Ok(ExecutionProof::new(vm, precompile))
144 }
145
146 #[tracing::instrument(name = "miden_vm", skip_all)]
151 fn prove_vm_trace(&self, trace: VmTrace) -> Result<VmProof, ProverError> {
152 let trace_len_summary = trace.trace_len_summary();
153 let params = config::pcs_params();
154 tracing::event!(
155 tracing::Level::INFO,
156 "Generated execution traces: core={}, range={}, chiplets={}, poseidon2={}, padded={}, \
157 estimated_prover_memory_bytes={:?}",
158 trace_len_summary.core_trace_len(),
159 trace_len_summary.range_trace_len(),
160 trace_len_summary.chiplets_trace_len().trace_len(),
161 trace_len_summary.poseidon2_permutation_trace_len(),
162 trace_len_summary.padded_trace_len(),
163 trace_len_summary.prover_memory_bytes(¶ms)
164 );
165
166 let precompile_root = trace.precompile_root();
167 let (public_values, aux_inputs) = trace.public_inputs().to_air_inputs();
168 let (core_matrix, chiplets_matrix, poseidon2_matrix) = trace.into_air_matrices();
169
170 let proof_bytes = match self.hash_fn {
171 HashFunction::Blake3_256 => {
172 let config = config::blake3_256_config(params, config::RELATION_DIGEST);
173 prove_stark(
174 &config,
175 core_matrix,
176 chiplets_matrix,
177 poseidon2_matrix,
178 &public_values,
179 &aux_inputs,
180 )
181 },
182 HashFunction::Keccak => {
183 let config = config::keccak_config(params, config::RELATION_DIGEST);
184 prove_stark(
185 &config,
186 core_matrix,
187 chiplets_matrix,
188 poseidon2_matrix,
189 &public_values,
190 &aux_inputs,
191 )
192 },
193 HashFunction::Rpo256 => {
194 let config = config::rpo_config(params, config::RELATION_DIGEST);
195 prove_stark(
196 &config,
197 core_matrix,
198 chiplets_matrix,
199 poseidon2_matrix,
200 &public_values,
201 &aux_inputs,
202 )
203 },
204 HashFunction::Poseidon2 => {
205 let config = config::poseidon2_config(params, config::RELATION_DIGEST);
206 prove_stark(
207 &config,
208 core_matrix,
209 chiplets_matrix,
210 poseidon2_matrix,
211 &public_values,
212 &aux_inputs,
213 )
214 },
215 HashFunction::Rpx256 => {
216 let config = config::rpx_config(params, config::RELATION_DIGEST);
217 prove_stark(
218 &config,
219 core_matrix,
220 chiplets_matrix,
221 poseidon2_matrix,
222 &public_values,
223 &aux_inputs,
224 )
225 },
226 }
227 .map_err(ProverError::VmProofGeneration)?;
228
229 let proof = miden_core::proof::StarkProof::new(proof_bytes, self.hash_fn);
230 Ok(VmProof { proof, precompile_root })
231 }
232}
233
234#[tracing::instrument(name = "prove_program_sync", skip_all)]
243pub fn prove_sync(
244 prover: &Prover,
245 program: &Program,
246 stack_inputs: StackInputs,
247 advice_inputs: AdviceInputs,
248 host: &mut impl SyncHost,
249 execution_options: ExecutionOptions,
250) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
251 #[cfg(feature = "std")]
252 let overlapped_trace_build = execution_options.overlapped_trace_build();
253 let processor = FastProcessor::new_with_options(stack_inputs, advice_inputs, execution_options)
254 .map_err(ExecutionError::advice_error_no_context)?;
255
256 #[cfg(feature = "std")]
257 if overlapped_trace_build {
258 let (trace, precompile) = {
259 let _span = tracing::info_span!("execute_miden_vm").entered();
260 processor.execute_and_build_trace_sync(
261 program,
262 host,
263 prover.max_prover_memory_bytes(),
264 )?
265 };
266 let stack_outputs = *trace.stack_outputs();
267 let proof = prover
268 .prove_full_trace(trace, precompile)
269 .map_err(ProverError::into_execution_error)?;
270 return Ok((stack_outputs, proof));
271 }
272
273 let witness = {
274 let _span = tracing::info_span!("execute_miden_vm").entered();
275 processor.execute_for_proving_sync(program, host)?
276 };
277 let stack_outputs = *witness.claim().stack_outputs();
278 let proof = prover.prove_full(witness).map_err(ProverError::into_execution_error)?;
279 Ok((stack_outputs, proof))
280}
281
282impl Default for Prover {
283 fn default() -> Self {
284 Self::new()
285 }
286}
287
288#[derive(Debug, thiserror::Error)]
290#[non_exhaustive]
291pub enum ProverError {
292 #[error("VM witness contains deferred precompile work")]
294 VmWitnessHasPrecompiles,
295 #[error("failed to materialize VM execution trace: {0}")]
297 TraceGeneration(#[source] ExecutionError),
298 #[error("failed to prove VM execution trace: {0}")]
300 VmProofGeneration(#[source] ExecutionError),
301 #[error("failed to prove precompile witness: {0}")]
303 PrecompileProofGeneration(#[source] miden_precompiles_prover::PrecompileProvingError),
304}
305
306impl ProverError {
307 fn into_execution_error(self) -> ExecutionError {
308 match self {
309 Self::VmWitnessHasPrecompiles => ExecutionError::ProvingError(self.to_string()),
310 Self::TraceGeneration(error) | Self::VmProofGeneration(error) => error,
311 Self::PrecompileProofGeneration(error) => {
312 ExecutionError::ProvingError(error.to_string())
313 },
314 }
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321
322 #[test]
323 fn prover_uses_canonical_default_and_allows_hash_override() {
324 let prover = Prover::new();
325 assert_eq!(prover.hash_fn, HashFunction::Blake3_256);
326
327 let prover = prover.with_hash_fn(HashFunction::Poseidon2);
328 assert_eq!(prover.hash_fn, HashFunction::Poseidon2);
329 }
330
331 #[test]
332 fn prover_uses_canonical_memory_budget_and_allows_override() {
333 let prover = Prover::new();
334 assert_eq!(prover.max_prover_memory_bytes(), Prover::DEFAULT_MAX_PROVER_MEMORY_BYTES);
335
336 let prover = prover.with_max_prover_memory_bytes(1 << 20);
337 assert_eq!(prover.max_prover_memory_bytes(), 1 << 20);
338 }
339}