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
34pub 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#[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 #[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 let pc_requests = trace.take_precompile_requests();
93
94 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
148struct 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 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 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 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}