1#![no_std]
2
3extern crate alloc;
4
5#[cfg(feature = "std")]
6extern crate std;
7
8use alloc::{string::ToString, vec, vec::Vec};
9
10use ::serde::Serialize;
11use miden_air::{MidenMultiAir, ProverStatement, Statement};
12use miden_core::{Felt, field::QuadFelt, utils::RowMajorMatrix};
13use miden_crypto::stark::{
14 ProverInstance, StarkConfig,
15 lmcs::Lmcs,
16 proof::{StarkOutput, StarkProofData},
17};
18use miden_processor::{
19 FastProcessor, Program,
20 trace::{ExecutionTrace, build_trace},
21};
22use serde_wincode::{SerdeCompat, wincode};
23use tracing::instrument;
24
25mod proving_options;
26
27pub use miden_air::{DeserializationError, MidenAir, PublicInputs, config};
30pub use miden_core::proof::{DeferredProof, ExecutionProof, HashFunction, StarkProof};
31pub use miden_processor::{
32 ExecutionError, ExecutionOptions, ExecutionOutput, FutureMaybeSend, Host, InputError,
33 ProgramInfo, StackInputs, StackOutputs, SyncHost, TraceBuildInputs, TraceGenerationContext,
34 Word, advice::AdviceInputs, crypto, field, serde, utils,
35};
36pub use proving_options::ProvingOptions;
37
38#[derive(Debug)]
40pub struct TraceProvingInputs {
41 trace_inputs: TraceBuildInputs,
42 options: ProvingOptions,
43}
44
45impl TraceProvingInputs {
46 pub fn new(trace_inputs: TraceBuildInputs, options: ProvingOptions) -> Self {
48 Self { trace_inputs, options }
49 }
50
51 pub fn into_parts(self) -> (TraceBuildInputs, ProvingOptions) {
53 (self.trace_inputs, self.options)
54 }
55}
56
57#[instrument("prove_program", skip_all)]
73pub async fn prove(
74 program: &Program,
75 stack_inputs: StackInputs,
76 advice_inputs: AdviceInputs,
77 host: &mut impl Host,
78 execution_options: ExecutionOptions,
79 proving_options: ProvingOptions,
80) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
81 let processor = FastProcessor::new_with_options(stack_inputs, advice_inputs, execution_options)
83 .map_err(ExecutionError::advice_error_no_context)?;
84
85 let trace_inputs = {
86 let _span = tracing::info_span!("execute_miden_vm").entered();
87 processor.execute_trace_inputs(program, host).await?
88 };
89 prove_from_trace_sync(TraceProvingInputs::new(trace_inputs, proving_options))
90}
91
92#[instrument("prove_program_partial", skip_all)]
97pub async fn prove_partial(
98 program: &Program,
99 stack_inputs: StackInputs,
100 advice_inputs: AdviceInputs,
101 host: &mut impl Host,
102 execution_options: ExecutionOptions,
103 proving_options: ProvingOptions,
104) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
105 let processor = FastProcessor::new_with_options(stack_inputs, advice_inputs, execution_options)
106 .map_err(ExecutionError::advice_error_no_context)?;
107
108 let trace_inputs = {
109 let _span = tracing::info_span!("execute_miden_vm").entered();
110 processor.execute_trace_inputs(program, host).await?
111 };
112 prove_partial_from_trace_sync(TraceProvingInputs::new(trace_inputs, proving_options))
113}
114
115#[instrument("prove_program_sync", skip_all)]
121pub fn prove_sync(
122 program: &Program,
123 stack_inputs: StackInputs,
124 advice_inputs: AdviceInputs,
125 host: &mut impl SyncHost,
126 execution_options: ExecutionOptions,
127 proving_options: ProvingOptions,
128) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
129 #[cfg(feature = "std")]
133 let overlapped_trace_build = execution_options.overlapped_trace_build();
134 let processor = FastProcessor::new_with_options(stack_inputs, advice_inputs, execution_options)
135 .map_err(ExecutionError::advice_error_no_context)?;
136
137 #[cfg(feature = "std")]
140 if overlapped_trace_build {
141 let trace = {
142 let _span = tracing::info_span!("execute_miden_vm").entered();
143 processor.execute_and_build_trace_sync(program, host)?
144 };
145 return prove_final_execution_trace(trace, proving_options);
146 }
147
148 let trace_inputs = {
149 let _span = tracing::info_span!("execute_miden_vm").entered();
150 processor.execute_trace_inputs_sync(program, host)?
151 };
152 prove_from_trace_sync(TraceProvingInputs::new(trace_inputs, proving_options))
153}
154
155#[instrument("prove_program_partial_sync", skip_all)]
161pub fn prove_partial_sync(
162 program: &Program,
163 stack_inputs: StackInputs,
164 advice_inputs: AdviceInputs,
165 host: &mut impl SyncHost,
166 execution_options: ExecutionOptions,
167 proving_options: ProvingOptions,
168) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
169 #[cfg(feature = "std")]
173 let overlapped_trace_build = execution_options.overlapped_trace_build();
174 let processor = FastProcessor::new_with_options(stack_inputs, advice_inputs, execution_options)
175 .map_err(ExecutionError::advice_error_no_context)?;
176
177 #[cfg(feature = "std")]
179 if overlapped_trace_build {
180 let trace = {
181 let _span = tracing::info_span!("execute_miden_vm").entered();
182 processor.execute_and_build_trace_sync(program, host)?
183 };
184 return prove_partial_execution_trace(trace, proving_options);
185 }
186
187 let trace_inputs = {
188 let _span = tracing::info_span!("execute_miden_vm").entered();
189 processor.execute_trace_inputs_sync(program, host)?
190 };
191 prove_partial_from_trace_sync(TraceProvingInputs::new(trace_inputs, proving_options))
192}
193
194#[instrument("prove_trace_sync", skip_all)]
200pub fn prove_from_trace_sync(
201 inputs: TraceProvingInputs,
202) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
203 let (trace_inputs, options) = inputs.into_parts();
204 let trace = {
205 let _span = tracing::info_span!("build_miden_vm_trace").entered();
206 build_trace(trace_inputs)?
207 };
208 prove_final_execution_trace(trace, options)
209}
210
211#[instrument("prove_partial_trace_sync", skip_all)]
216pub fn prove_partial_from_trace_sync(
217 inputs: TraceProvingInputs,
218) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
219 let (trace_inputs, options) = inputs.into_parts();
220 let trace = {
221 let _span = tracing::info_span!("build_miden_vm_trace").entered();
222 build_trace(trace_inputs)?
223 };
224 prove_partial_execution_trace(trace, options)
225}
226
227fn prove_final_execution_trace(
228 trace: ExecutionTrace,
229 options: ProvingOptions,
230) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
231 let hash_fn = options.hash_fn();
232 let deferred_proof = {
233 let _span = tracing::info_span!("precompile_vm").entered();
234 miden_precompiles_prover::prove_deferred_state(trace.deferred_state(), hash_fn)
235 .map_err(|err| ExecutionError::ProvingError(err.to_string()))?
236 };
237
238 prove_miden_vm_execution_trace(trace, options, deferred_proof)
239}
240
241fn prove_partial_execution_trace(
242 trace: ExecutionTrace,
243 options: ProvingOptions,
244) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
245 let deferred_proof = {
246 let _precompile_vm_span = tracing::info_span!("precompile_vm").entered();
247 let _serialize_witness_span = tracing::info_span!("serialize_witness").entered();
248 let wire = trace
249 .deferred_state()
250 .to_wire()
251 .map_err(|err| ExecutionError::ProvingError(err.to_string()))?;
252 DeferredProof::Wire(wire)
253 };
254
255 prove_miden_vm_execution_trace(trace, options, deferred_proof)
256}
257
258#[instrument("miden_vm", skip_all)]
259fn prove_miden_vm_execution_trace(
260 trace: ExecutionTrace,
261 options: ProvingOptions,
262 deferred_proof: DeferredProof,
263) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
264 let trace_len_summary = trace.trace_len_summary();
265 tracing::event!(
266 tracing::Level::INFO,
267 "Generated execution traces: core={}, range={}, chiplets={}, poseidon2={}, padded={}",
268 trace_len_summary.core_trace_len(),
269 trace_len_summary.range_trace_len(),
270 trace_len_summary.chiplets_trace_len().trace_len(),
271 trace_len_summary.poseidon2_permutation_trace_len(),
272 trace_len_summary.padded_trace_len()
273 );
274
275 let stack_outputs = *trace.stack_outputs();
276 let hash_fn = options.hash_fn();
277
278 let (public_values, aux_inputs) = trace.public_inputs().to_air_inputs();
280
281 let (core_matrix, chiplets_matrix, poseidon2_matrix) = trace.into_air_matrices();
282
283 let params = config::pcs_params();
284 let proof_bytes = match hash_fn {
285 HashFunction::Blake3_256 => {
286 let config = config::blake3_256_config(params, config::RELATION_DIGEST);
287 prove_stark(
288 &config,
289 core_matrix,
290 chiplets_matrix,
291 poseidon2_matrix,
292 &public_values,
293 &aux_inputs,
294 )
295 },
296 HashFunction::Keccak => {
297 let config = config::keccak_config(params, config::RELATION_DIGEST);
298 prove_stark(
299 &config,
300 core_matrix,
301 chiplets_matrix,
302 poseidon2_matrix,
303 &public_values,
304 &aux_inputs,
305 )
306 },
307 HashFunction::Rpo256 => {
308 let config = config::rpo_config(params, config::RELATION_DIGEST);
309 prove_stark(
310 &config,
311 core_matrix,
312 chiplets_matrix,
313 poseidon2_matrix,
314 &public_values,
315 &aux_inputs,
316 )
317 },
318 HashFunction::Poseidon2 => {
319 let config = config::poseidon2_config(params, config::RELATION_DIGEST);
320 prove_stark(
321 &config,
322 core_matrix,
323 chiplets_matrix,
324 poseidon2_matrix,
325 &public_values,
326 &aux_inputs,
327 )
328 },
329 HashFunction::Rpx256 => {
330 let config = config::rpx_config(params, config::RELATION_DIGEST);
331 prove_stark(
332 &config,
333 core_matrix,
334 chiplets_matrix,
335 poseidon2_matrix,
336 &public_values,
337 &aux_inputs,
338 )
339 },
340 }?;
341
342 let proof = ExecutionProof::from_parts(proof_bytes, hash_fn, deferred_proof);
343
344 Ok((stack_outputs, proof))
345}
346
347#[instrument("prove_stark", skip_all)]
356pub fn prove_stark<SC>(
357 config: &SC,
358 core_trace: RowMajorMatrix<Felt>,
359 chiplets_trace: RowMajorMatrix<Felt>,
360 poseidon2_trace: RowMajorMatrix<Felt>,
361 public_values: &[Felt],
362 aux_inputs: &[Felt],
363) -> Result<Vec<u8>, ExecutionError>
364where
365 SC: StarkConfig<Felt, QuadFelt>,
366 <SC::Lmcs as Lmcs>::Commitment: Serialize,
367{
368 let mut challenger = config.challenger();
369 config::observe_protocol_params(config.pcs(), &mut challenger);
370
371 let statement =
374 Statement::new(MidenMultiAir::new(), public_values.to_vec(), aux_inputs.to_vec())
375 .map_err(|e| ExecutionError::ProvingError(e.to_string()))?;
376 let prover_statement =
377 ProverStatement::new(statement, vec![core_trace, chiplets_trace, poseidon2_trace])
378 .map_err(|e| ExecutionError::ProvingError(e.to_string()))?;
379
380 let output: StarkOutput<Felt, QuadFelt, SC> =
381 ProverInstance::new(config, &prover_statement, None)
382 .map_err(|e| ExecutionError::ProvingError(e.to_string()))?
383 .prove(challenger)
384 .map_err(|e| ExecutionError::ProvingError(e.to_string()))?;
385
386 let proof_encoding_config = wincode::config::Configuration::default();
387 let proof_bytes =
388 <SerdeCompat<StarkProofData<Felt, QuadFelt, SC>> as wincode::config::Serialize<_>>::serialize(
389 &output.proof,
390 proof_encoding_config,
391 )
392 .map_err(|e| ExecutionError::ProvingError(e.to_string()))?;
393 Ok(proof_bytes)
394}