Skip to main content

miden_prover/
lib.rs

1#![no_std]
2
3extern crate alloc;
4
5#[cfg(feature = "std")]
6extern crate std;
7
8use alloc::{string::ToString, vec::Vec};
9
10use ::serde::Serialize;
11use miden_core::{
12    Felt, WORD_SIZE,
13    field::QuadFelt,
14    utils::{Matrix, RowMajorMatrix},
15};
16use miden_crypto::stark::{
17    StarkConfig, air::VarLenPublicInputs, challenger::CanObserve, lmcs::Lmcs, proof::StarkOutput,
18};
19use miden_processor::{
20    FastProcessor, Program,
21    trace::{AuxTraceBuilders, ExecutionTrace, build_trace},
22};
23use tracing::instrument;
24
25mod proving_options;
26
27// EXPORTS
28// ================================================================================================
29pub use miden_air::{DeserializationError, ProcessorAir, PublicInputs, config};
30pub use miden_core::proof::{ExecutionProof, HashFunction};
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/// Inputs required to prove from pre-executed trace data.
39#[derive(Debug)]
40pub struct TraceProvingInputs {
41    trace_inputs: TraceBuildInputs,
42    options: ProvingOptions,
43}
44
45impl TraceProvingInputs {
46    /// Creates a new bundle of post-execution trace inputs and proof-generation options.
47    pub fn new(trace_inputs: TraceBuildInputs, options: ProvingOptions) -> Self {
48        Self { trace_inputs, options }
49    }
50
51    /// Consumes this bundle and returns its trace inputs and proof-generation options.
52    pub fn into_parts(self) -> (TraceBuildInputs, ProvingOptions) {
53        (self.trace_inputs, self.options)
54    }
55}
56
57// PROVER
58// ================================================================================================
59
60/// Executes and proves the specified `program` and returns the result together with a STARK-based
61/// proof of the program's execution.
62///
63/// - `stack_inputs` specifies the initial state of the stack for the VM.
64/// - `advice_inputs` provides the initial nondeterministic inputs for the VM.
65/// - `host` specifies the host environment which contain non-deterministic (secret) inputs for the
66///   prover.
67/// - `execution_options` defines VM execution parameters such as cycle limits and fragmentation.
68/// - `proving_options` defines parameters for STARK proof generation.
69///
70/// # Errors
71/// Returns an error if program execution or STARK proof generation fails for any reason.
72#[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    // execute the program to create an execution trace using FastProcessor
82    let processor = FastProcessor::new_with_options(stack_inputs, advice_inputs, execution_options);
83
84    let trace_inputs = processor.execute_trace_inputs(program, host).await?;
85    prove_from_trace_sync(TraceProvingInputs::new(trace_inputs, proving_options))
86}
87
88/// Synchronous wrapper for [`prove()`].
89#[instrument("prove_program_sync", skip_all)]
90pub fn prove_sync(
91    program: &Program,
92    stack_inputs: StackInputs,
93    advice_inputs: AdviceInputs,
94    host: &mut impl SyncHost,
95    execution_options: ExecutionOptions,
96    proving_options: ProvingOptions,
97) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
98    let processor = FastProcessor::new_with_options(stack_inputs, advice_inputs, execution_options);
99
100    let trace_inputs = processor.execute_trace_inputs_sync(program, host)?;
101    prove_from_trace_sync(TraceProvingInputs::new(trace_inputs, proving_options))
102}
103
104/// Builds an execution trace from pre-executed trace inputs and proves it synchronously.
105///
106/// This is useful when program execution has already happened elsewhere and only trace building
107/// plus proof generation remain. The execution settings are already reflected in the supplied
108/// `TraceBuildInputs`, so only proof-generation options remain in this API.
109#[instrument("prove_trace_sync", skip_all)]
110pub fn prove_from_trace_sync(
111    inputs: TraceProvingInputs,
112) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
113    let (trace_inputs, options) = inputs.into_parts();
114    let trace = build_trace(trace_inputs)?;
115    prove_execution_trace(trace, options)
116}
117
118fn prove_execution_trace(
119    trace: ExecutionTrace,
120    options: ProvingOptions,
121) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
122    tracing::event!(
123        tracing::Level::INFO,
124        "Generated execution trace of {} columns and {} steps (padded from {})",
125        miden_air::trace::TRACE_WIDTH,
126        trace.trace_len_summary().padded_trace_len(),
127        trace.trace_len_summary().trace_len()
128    );
129
130    let stack_outputs = *trace.stack_outputs();
131    let precompile_requests = trace.precompile_requests().to_vec();
132    let hash_fn = options.hash_fn();
133
134    let trace_matrix = {
135        let _span = tracing::info_span!("to_row_major_matrix").entered();
136        trace.to_row_major_matrix()
137    };
138
139    let (public_values, kernel_felts) = trace.public_inputs().to_air_inputs();
140    let var_len_public_inputs: &[&[Felt]] = &[&kernel_felts];
141    let aux_builder = trace.aux_trace_builders();
142
143    let params = config::pcs_params();
144    let proof_bytes = match hash_fn {
145        HashFunction::Blake3_256 => {
146            let config = config::blake3_256_config(params);
147            prove_stark(&config, &trace_matrix, &public_values, var_len_public_inputs, &aux_builder)
148        },
149        HashFunction::Keccak => {
150            let config = config::keccak_config(params);
151            prove_stark(&config, &trace_matrix, &public_values, var_len_public_inputs, &aux_builder)
152        },
153        HashFunction::Rpo256 => {
154            let config = config::rpo_config(params);
155            prove_stark(&config, &trace_matrix, &public_values, var_len_public_inputs, &aux_builder)
156        },
157        HashFunction::Poseidon2 => {
158            let config = config::poseidon2_config(params);
159            prove_stark(&config, &trace_matrix, &public_values, var_len_public_inputs, &aux_builder)
160        },
161        HashFunction::Rpx256 => {
162            let config = config::rpx_config(params);
163            prove_stark(&config, &trace_matrix, &public_values, var_len_public_inputs, &aux_builder)
164        },
165    }?;
166
167    let proof = ExecutionProof::new(proof_bytes, hash_fn, precompile_requests);
168
169    Ok((stack_outputs, proof))
170}
171
172// STARK PROOF GENERATION
173// ================================================================================================
174
175/// Generates a STARK proof for the given trace and public values.
176///
177/// Pre-seeds the challenger with `public_values`, then delegates to the lifted
178/// prover. Returns the serialized proof bytes.
179pub fn prove_stark<SC>(
180    config: &SC,
181    trace: &RowMajorMatrix<Felt>,
182    public_values: &[Felt],
183    var_len_public_inputs: VarLenPublicInputs<'_, Felt>,
184    aux_builder: &AuxTraceBuilders,
185) -> Result<Vec<u8>, ExecutionError>
186where
187    SC: StarkConfig<Felt, QuadFelt>,
188    <SC::Lmcs as Lmcs>::Commitment: Serialize,
189{
190    let log_trace_height = trace.height().ilog2() as u8;
191
192    let mut challenger = config.challenger();
193    config::observe_protocol_params(&mut challenger, log_trace_height as u64);
194    challenger.observe_slice(public_values);
195    config::observe_var_len_public_inputs(&mut challenger, var_len_public_inputs, &[WORD_SIZE]);
196    let output: StarkOutput<Felt, QuadFelt, SC> = miden_crypto::stark::prover::prove_single(
197        config,
198        &ProcessorAir,
199        trace,
200        public_values,
201        var_len_public_inputs,
202        aux_builder,
203        challenger,
204    )
205    .map_err(|e| ExecutionError::ProvingError(e.to_string()))?;
206    // Proof serialization via bincode; see https://github.com/0xMiden/miden-vm/issues/2550
207    // We serialize `(log_trace_height, proof)` as a tuple; this is a temporary approach until
208    // the lifted STARK integrates trace height on its side.
209    let proof_bytes = bincode::serialize(&(log_trace_height, &output.proof))
210        .map_err(|e| ExecutionError::ProvingError(e.to_string()))?;
211    Ok(proof_bytes)
212}