miden_prover/
lib.rs

1#![no_std]
2
3#[cfg_attr(all(feature = "metal", target_arch = "aarch64", target_os = "macos"), macro_use)]
4extern crate alloc;
5
6#[cfg(feature = "std")]
7extern crate std;
8
9use core::marker::PhantomData;
10
11use miden_air::{AuxRandElements, PartitionOptions, ProcessorAir, PublicInputs};
12#[cfg(all(feature = "metal", target_arch = "aarch64", target_os = "macos"))]
13use miden_gpu::HashFn;
14use miden_processor::{
15    ExecutionTrace, Program,
16    crypto::{
17        Blake3_192, Blake3_256, ElementHasher, Poseidon2, RandomCoin, Rpo256, RpoRandomCoin,
18        Rpx256, RpxRandomCoin, WinterRandomCoin,
19    },
20    math::{Felt, FieldElement},
21};
22use tracing::instrument;
23use winter_maybe_async::{maybe_async, maybe_await};
24use winter_prover::{
25    CompositionPoly, CompositionPolyTrace, ConstraintCompositionCoefficients,
26    DefaultConstraintCommitment, DefaultConstraintEvaluator, DefaultTraceLde,
27    ProofOptions as WinterProofOptions, Prover, StarkDomain, TraceInfo, TracePolyTable,
28    matrix::ColMatrix,
29};
30#[cfg(feature = "std")]
31use {std::time::Instant, winter_prover::Trace};
32mod gpu;
33
34// EXPORTS
35// ================================================================================================
36
37pub use miden_air::{
38    DeserializationError, ExecutionProof, FieldExtension, HashFunction, ProvingOptions,
39};
40pub use miden_processor::{
41    AdviceInputs, AsyncHost, BaseHost, ExecutionError, InputError, PrecompileRequest, StackInputs,
42    StackOutputs, SyncHost, Word, crypto, math, utils,
43};
44pub use winter_prover::{Proof, crypto::MerkleTree as MerkleTreeVC};
45
46// PROVER
47// ================================================================================================
48
49/// Executes and proves the specified `program` and returns the result together with a STARK-based
50/// proof of the program's execution.
51///
52/// - `stack_inputs` specifies the initial state of the stack for the VM.
53/// - `host` specifies the host environment which contain non-deterministic (secret) inputs for the
54///   prover
55/// - `options` defines parameters for STARK proof generation.
56///
57/// # Errors
58/// Returns an error if program execution or STARK proof generation fails for any reason.
59#[instrument("prove_program", skip_all)]
60#[maybe_async]
61pub fn prove(
62    program: &Program,
63    stack_inputs: StackInputs,
64    advice_inputs: AdviceInputs,
65    host: &mut impl SyncHost,
66    options: ProvingOptions,
67) -> Result<(StackOutputs, ExecutionProof), ExecutionError> {
68    // execute the program to create an execution trace
69    #[cfg(feature = "std")]
70    let now = Instant::now();
71    let mut trace = miden_processor::execute(
72        program,
73        stack_inputs.clone(),
74        advice_inputs,
75        host,
76        *options.execution_options(),
77    )?;
78    #[cfg(feature = "std")]
79    tracing::event!(
80        tracing::Level::INFO,
81        "Generated execution trace of {} columns and {} steps ({}% padded) in {} ms",
82        trace.info().main_trace_width(),
83        trace.trace_len_summary().padded_trace_len(),
84        trace.trace_len_summary().padding_percentage(),
85        now.elapsed().as_millis()
86    );
87
88    let stack_outputs = trace.stack_outputs().clone();
89    let hash_fn = options.hash_fn();
90
91    // extract precompile requests from the trace to include in the proof
92    let pc_requests = trace.take_precompile_requests();
93
94    // generate STARK proof
95    let proof = match hash_fn {
96        HashFunction::Blake3_192 => {
97            let prover = ExecutionProver::<Blake3_192, WinterRandomCoin<_>>::new(
98                options,
99                stack_inputs,
100                stack_outputs.clone(),
101            );
102            maybe_await!(prover.prove(trace))
103        },
104        HashFunction::Blake3_256 => {
105            let prover = ExecutionProver::<Blake3_256, WinterRandomCoin<_>>::new(
106                options,
107                stack_inputs,
108                stack_outputs.clone(),
109            );
110            maybe_await!(prover.prove(trace))
111        },
112        HashFunction::Rpo256 => {
113            let prover = ExecutionProver::<Rpo256, RpoRandomCoin>::new(
114                options,
115                stack_inputs,
116                stack_outputs.clone(),
117            );
118            #[cfg(all(feature = "metal", target_arch = "aarch64", target_os = "macos"))]
119            let prover = gpu::metal::MetalExecutionProver::new(prover, HashFn::Rpo256);
120            maybe_await!(prover.prove(trace))
121        },
122        HashFunction::Rpx256 => {
123            let prover = ExecutionProver::<Rpx256, RpxRandomCoin>::new(
124                options,
125                stack_inputs,
126                stack_outputs.clone(),
127            );
128            #[cfg(all(feature = "metal", target_arch = "aarch64", target_os = "macos"))]
129            let prover = gpu::metal::MetalExecutionProver::new(prover, HashFn::Rpx256);
130            maybe_await!(prover.prove(trace))
131        },
132        HashFunction::Poseidon2 => {
133            let prover = ExecutionProver::<Poseidon2, WinterRandomCoin<_>>::new(
134                options,
135                stack_inputs,
136                stack_outputs.clone(),
137            );
138            maybe_await!(prover.prove(trace))
139        },
140    }
141    .map_err(ExecutionError::ProverError)?;
142
143    let proof = ExecutionProof::new(proof, hash_fn, pc_requests);
144
145    Ok((stack_outputs, proof))
146}
147
148// PROVER
149// ================================================================================================
150
151struct ExecutionProver<H, R>
152where
153    H: ElementHasher<BaseField = Felt>,
154    R: RandomCoin<BaseField = Felt, Hasher = H>,
155{
156    random_coin: PhantomData<R>,
157    options: WinterProofOptions,
158    stack_inputs: StackInputs,
159    stack_outputs: StackOutputs,
160}
161
162impl<H, R> ExecutionProver<H, R>
163where
164    H: ElementHasher<BaseField = Felt>,
165    R: RandomCoin<BaseField = Felt, Hasher = H>,
166{
167    pub fn new(
168        options: ProvingOptions,
169        stack_inputs: StackInputs,
170        stack_outputs: StackOutputs,
171    ) -> Self {
172        Self {
173            random_coin: PhantomData,
174            options: options.into(),
175            stack_inputs,
176            stack_outputs,
177        }
178    }
179
180    // HELPER FUNCTIONS
181    // --------------------------------------------------------------------------------------------
182
183    /// Validates the stack inputs against the provided execution trace and returns true if valid.
184    fn are_inputs_valid(&self, trace: &ExecutionTrace) -> bool {
185        self.stack_inputs
186            .iter()
187            .zip(trace.init_stack_state().iter())
188            .all(|(l, r)| l == r)
189    }
190
191    /// Validates the stack outputs against the provided execution trace and returns true if valid.
192    fn are_outputs_valid(&self, trace: &ExecutionTrace) -> bool {
193        self.stack_outputs
194            .iter()
195            .zip(trace.last_stack_state().iter())
196            .all(|(l, r)| l == r)
197    }
198}
199
200impl<H, R> Prover for ExecutionProver<H, R>
201where
202    H: ElementHasher<BaseField = Felt> + Sync,
203    R: RandomCoin<BaseField = Felt, Hasher = H> + Send,
204{
205    type BaseField = Felt;
206    type Air = ProcessorAir;
207    type Trace = ExecutionTrace;
208    type HashFn = H;
209    type VC = MerkleTreeVC<Self::HashFn>;
210    type RandomCoin = R;
211    type TraceLde<E: FieldElement<BaseField = Felt>> = DefaultTraceLde<E, H, Self::VC>;
212    type ConstraintEvaluator<'a, E: FieldElement<BaseField = Felt>> =
213        DefaultConstraintEvaluator<'a, ProcessorAir, E>;
214    type ConstraintCommitment<E: FieldElement<BaseField = Felt>> =
215        DefaultConstraintCommitment<E, H, Self::VC>;
216
217    fn options(&self) -> &WinterProofOptions {
218        &self.options
219    }
220
221    fn get_pub_inputs(&self, trace: &ExecutionTrace) -> PublicInputs {
222        // ensure inputs and outputs are consistent with the execution trace.
223        debug_assert!(
224            self.are_inputs_valid(trace),
225            "provided inputs do not match the execution trace"
226        );
227        debug_assert!(
228            self.are_outputs_valid(trace),
229            "provided outputs do not match the execution trace"
230        );
231
232        let program_info = trace.program_info().clone();
233        let final_pc_transcript = trace.final_precompile_transcript();
234        PublicInputs::new(
235            program_info,
236            self.stack_inputs.clone(),
237            self.stack_outputs.clone(),
238            final_pc_transcript.state(),
239        )
240    }
241
242    #[maybe_async]
243    fn new_trace_lde<E: FieldElement<BaseField = Felt>>(
244        &self,
245        trace_info: &TraceInfo,
246        main_trace: &ColMatrix<Felt>,
247        domain: &StarkDomain<Felt>,
248        partition_options: PartitionOptions,
249    ) -> (Self::TraceLde<E>, TracePolyTable<E>) {
250        DefaultTraceLde::new(trace_info, main_trace, domain, partition_options)
251    }
252
253    #[maybe_async]
254    fn new_evaluator<'a, E: FieldElement<BaseField = Felt>>(
255        &self,
256        air: &'a ProcessorAir,
257        aux_rand_elements: Option<AuxRandElements<E>>,
258        composition_coefficients: ConstraintCompositionCoefficients<E>,
259    ) -> Self::ConstraintEvaluator<'a, E> {
260        DefaultConstraintEvaluator::new(air, aux_rand_elements, composition_coefficients)
261    }
262
263    #[instrument(skip_all)]
264    #[maybe_async]
265    fn build_aux_trace<E: FieldElement<BaseField = Self::BaseField>>(
266        &self,
267        trace: &Self::Trace,
268        aux_rand_elements: &AuxRandElements<E>,
269    ) -> ColMatrix<E> {
270        trace.build_aux_trace(aux_rand_elements.rand_elements()).unwrap()
271    }
272
273    #[maybe_async]
274    fn build_constraint_commitment<E: FieldElement<BaseField = Felt>>(
275        &self,
276        composition_poly_trace: CompositionPolyTrace<E>,
277        num_constraint_composition_columns: usize,
278        domain: &StarkDomain<Self::BaseField>,
279        partition_options: PartitionOptions,
280    ) -> (Self::ConstraintCommitment<E>, CompositionPoly<E>) {
281        DefaultConstraintCommitment::new(
282            composition_poly_trace,
283            num_constraint_composition_columns,
284            domain,
285            partition_options,
286        )
287    }
288}