miden_air/
lib.rs

1#![no_std]
2#![allow(dead_code)]
3
4#[macro_use]
5extern crate alloc;
6
7#[cfg(feature = "std")]
8extern crate std;
9
10use alloc::{borrow::ToOwned, vec::Vec};
11
12use miden_core::{
13    ExtensionOf, ONE, ProgramInfo, StackInputs, StackOutputs, Word, ZERO,
14    utils::{ByteReader, ByteWriter, Deserializable, Serializable},
15};
16use winter_air::{
17    Air, AirContext, Assertion, EvaluationFrame, ProofOptions as WinterProofOptions, TraceInfo,
18    TransitionConstraintDegree,
19};
20use winter_prover::{
21    crypto::{RandomCoin, RandomCoinError},
22    math::get_power_series,
23    matrix::ColMatrix,
24};
25
26mod constraints;
27pub use constraints::stack;
28use constraints::{chiplets, range};
29
30pub mod trace;
31pub use trace::rows::RowIndex;
32use trace::*;
33
34mod errors;
35mod options;
36mod proof;
37
38mod utils;
39
40// RE-EXPORTS
41// ================================================================================================
42
43pub use errors::ExecutionOptionsError;
44pub use miden_core::{
45    Felt, FieldElement, StarkField,
46    utils::{DeserializationError, ToElements},
47};
48pub use options::{ExecutionOptions, ProvingOptions};
49pub use proof::{ExecutionProof, HashFunction};
50use utils::TransitionConstraintRange;
51pub use winter_air::{AuxRandElements, FieldExtension, PartitionOptions};
52
53/// Selects whether to include all existing constraints or only the ones currently encoded in
54/// the ACE circuit in the recursive verifier.
55const IS_FULL_CONSTRAINT_SET: bool = false;
56
57// PROCESSOR AIR
58// ================================================================================================
59
60/// TODO: add docs
61pub struct ProcessorAir {
62    context: AirContext<Felt>,
63    stack_inputs: StackInputs,
64    stack_outputs: StackOutputs,
65    program_digest: Word,
66    kernel_digests: Vec<Word>,
67    constraint_ranges: TransitionConstraintRange,
68}
69
70impl ProcessorAir {
71    /// Returns last step of the execution trace.
72    pub fn last_step(&self) -> usize {
73        self.trace_length() - self.context().num_transition_exemptions()
74    }
75}
76
77impl Air for ProcessorAir {
78    type BaseField = Felt;
79    type PublicInputs = PublicInputs;
80
81    fn new(trace_info: TraceInfo, pub_inputs: PublicInputs, options: WinterProofOptions) -> Self {
82        // --- system -----------------------------------------------------------------------------
83        let mut main_degrees = vec![
84            TransitionConstraintDegree::new(1), // clk' = clk + 1
85        ];
86
87        if IS_FULL_CONSTRAINT_SET {
88            // --- stack constraints
89            // ---------------------------------------------------------------------
90            let mut stack_degrees = stack::get_transition_constraint_degrees();
91            main_degrees.append(&mut stack_degrees);
92
93            // --- range checker
94            // ----------------------------------------------------------------------
95            let mut range_checker_degrees = range::get_transition_constraint_degrees();
96            main_degrees.append(&mut range_checker_degrees);
97
98            // --- chiplets (hasher, bitwise, memory) -------------------------
99            let mut chiplets_degrees = chiplets::get_transition_constraint_degrees();
100            main_degrees.append(&mut chiplets_degrees);
101        }
102
103        let aux_degrees = range::get_aux_transition_constraint_degrees();
104
105        // Define the transition constraint ranges.
106        let constraint_ranges = TransitionConstraintRange::new(
107            1,
108            stack::get_transition_constraint_count(),
109            range::get_transition_constraint_count(),
110            chiplets::get_transition_constraint_count(),
111        );
112
113        // Define the number of boundary constraints for the main execution trace segment.
114        // TODO: determine dynamically
115        let num_main_assertions = if IS_FULL_CONSTRAINT_SET {
116            2 + stack::NUM_ASSERTIONS + range::NUM_ASSERTIONS
117        } else {
118            1
119        };
120
121        // Define the number of boundary constraints for the auxiliary execution trace segment.
122        let num_aux_assertions = if IS_FULL_CONSTRAINT_SET {
123            stack::NUM_AUX_ASSERTIONS + range::NUM_AUX_ASSERTIONS
124        } else {
125            3
126        };
127
128        // Create the context and set the number of transition constraint exemptions to two; this
129        // allows us to inject random values into the last row of the execution trace.
130        let context = AirContext::new_multi_segment(
131            trace_info,
132            main_degrees,
133            aux_degrees,
134            num_main_assertions,
135            num_aux_assertions,
136            options,
137        )
138        .set_num_transition_exemptions(2);
139
140        Self {
141            context,
142            stack_inputs: pub_inputs.stack_inputs,
143            stack_outputs: pub_inputs.stack_outputs,
144            constraint_ranges,
145            program_digest: pub_inputs.program_info.program_hash().to_owned(),
146            kernel_digests: pub_inputs.program_info.kernel_procedures().to_owned(),
147        }
148    }
149
150    // PERIODIC COLUMNS
151    // --------------------------------------------------------------------------------------------
152
153    /// Returns a set of periodic columns for the ProcessorAir.
154    fn get_periodic_column_values(&self) -> Vec<Vec<Felt>> {
155        chiplets::get_periodic_column_values()
156    }
157
158    // ASSERTIONS
159    // --------------------------------------------------------------------------------------------
160
161    fn get_assertions(&self) -> Vec<Assertion<Felt>> {
162        // --- set assertions for the first step --------------------------------------------------
163        // first value of clk is 0
164        let mut result = vec![Assertion::single(CLK_COL_IDX, 0, ZERO)];
165
166        if IS_FULL_CONSTRAINT_SET {
167            // first value of fmp is 2^30
168            result.push(Assertion::single(FMP_COL_IDX, 0, Felt::new(2u64.pow(30))));
169
170            // add initial assertions for the stack.
171            stack::get_assertions_first_step(&mut result, &*self.stack_inputs);
172
173            // Add initial assertions for the range checker.
174            range::get_assertions_first_step(&mut result);
175
176            // --- set assertions for the last step
177            // ---------------------------------------------------
178            let last_step = self.last_step();
179
180            // add the stack's assertions for the last step.
181            stack::get_assertions_last_step(&mut result, last_step, &self.stack_outputs);
182
183            // Add the range checker's assertions for the last step.
184            range::get_assertions_last_step(&mut result, last_step);
185        }
186
187        result
188    }
189
190    fn get_aux_assertions<E: FieldElement<BaseField = Self::BaseField>>(
191        &self,
192        aux_rand_elements: &AuxRandElements<E>,
193    ) -> Vec<Assertion<E>> {
194        let mut result: Vec<Assertion<E>> = Vec::new();
195
196        // Add initial assertions for the range checker's auxiliary columns.
197        range::get_aux_assertions_first_step::<E>(&mut result);
198
199        // Add initial assertion for the chiplets' bus auxiliary column.
200        chiplets::get_aux_assertions_first_step::<E>(
201            &mut result,
202            &self.kernel_digests,
203            aux_rand_elements,
204        );
205
206        // --- set assertions for the first step --------------------------------------------------
207        if IS_FULL_CONSTRAINT_SET {
208            // add initial assertions for the stack's auxiliary columns.
209            stack::get_aux_assertions_first_step(&mut result);
210
211            // --- set assertions for the last step
212            // ---------------------------------------------------
213            let last_step = self.last_step();
214
215            // add the stack's auxiliary column assertions for the last step.
216            stack::get_aux_assertions_last_step(&mut result, last_step);
217        }
218        // Add the range checker's auxiliary column assertions for the last step.
219        let last_step = self.last_step();
220        range::get_aux_assertions_last_step::<E>(&mut result, last_step);
221
222        result
223    }
224
225    // TRANSITION CONSTRAINTS
226    // --------------------------------------------------------------------------------------------
227
228    fn evaluate_transition<E: FieldElement<BaseField = Felt>>(
229        &self,
230        frame: &EvaluationFrame<E>,
231        periodic_values: &[E],
232        result: &mut [E],
233    ) {
234        let current = frame.current();
235        let next = frame.next();
236
237        // --- system -----------------------------------------------------------------------------
238        // clk' = clk + 1
239        result[0] = next[CLK_COL_IDX] - (current[CLK_COL_IDX] + E::ONE);
240
241        if IS_FULL_CONSTRAINT_SET {
242            // --- stack operations
243            // -------------------------------------------------------------------
244            stack::enforce_constraints::<E>(
245                frame,
246                select_result_range!(result, self.constraint_ranges.stack),
247            );
248
249            // --- range checker
250            // ----------------------------------------------------------------------
251            range::enforce_constraints::<E>(
252                frame,
253                select_result_range!(result, self.constraint_ranges.range_checker),
254            );
255
256            // --- chiplets (hasher, bitwise, memory) -------------------------
257            chiplets::enforce_constraints::<E>(
258                frame,
259                periodic_values,
260                select_result_range!(result, self.constraint_ranges.chiplets),
261            );
262        }
263    }
264
265    fn evaluate_aux_transition<F, E>(
266        &self,
267        main_frame: &EvaluationFrame<F>,
268        aux_frame: &EvaluationFrame<E>,
269        _periodic_values: &[F],
270        aux_rand_elements: &AuxRandElements<E>,
271        result: &mut [E],
272    ) where
273        F: FieldElement<BaseField = Felt>,
274        E: FieldElement<BaseField = Felt> + ExtensionOf<F>,
275    {
276        // --- range checker ----------------------------------------------------------------------
277        range::enforce_aux_constraints::<F, E>(
278            main_frame,
279            aux_frame,
280            aux_rand_elements.rand_elements(),
281            result,
282        );
283    }
284
285    fn context(&self) -> &AirContext<Felt> {
286        &self.context
287    }
288
289    fn get_aux_rand_elements<E, R>(
290        &self,
291        public_coin: &mut R,
292    ) -> Result<AuxRandElements<E>, RandomCoinError>
293    where
294        E: FieldElement<BaseField = Self::BaseField>,
295        R: RandomCoin<BaseField = Self::BaseField>,
296    {
297        let num_elements = self.trace_info().get_num_aux_segment_rand_elements();
298        let mut rand_elements = Vec::with_capacity(num_elements);
299        let max_message_length = num_elements - 1;
300
301        let alpha = public_coin.draw()?;
302        let beta = public_coin.draw()?;
303
304        let betas = get_power_series(beta, max_message_length);
305
306        rand_elements.push(alpha);
307        rand_elements.extend_from_slice(&betas);
308
309        Ok(AuxRandElements::new(rand_elements))
310    }
311}
312
313// PUBLIC INPUTS
314// ================================================================================================
315
316#[derive(Debug)]
317pub struct PublicInputs {
318    program_info: ProgramInfo,
319    stack_inputs: StackInputs,
320    stack_outputs: StackOutputs,
321}
322
323impl PublicInputs {
324    pub fn new(
325        program_info: ProgramInfo,
326        stack_inputs: StackInputs,
327        stack_outputs: StackOutputs,
328    ) -> Self {
329        Self {
330            program_info,
331            stack_inputs,
332            stack_outputs,
333        }
334    }
335}
336
337impl miden_core::ToElements<Felt> for PublicInputs {
338    fn to_elements(&self) -> Vec<Felt> {
339        let mut result = self.stack_inputs.to_vec();
340        result.append(&mut self.stack_outputs.to_vec());
341        result.append(&mut self.program_info.to_elements());
342        result
343    }
344}
345
346// SERIALIZATION
347// ================================================================================================
348
349impl Serializable for PublicInputs {
350    fn write_into<W: ByteWriter>(&self, target: &mut W) {
351        self.program_info.write_into(target);
352        self.stack_inputs.write_into(target);
353        self.stack_outputs.write_into(target);
354    }
355}
356
357impl Deserializable for PublicInputs {
358    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
359        let program_info = ProgramInfo::read_from(source)?;
360        let stack_inputs = StackInputs::read_from(source)?;
361        let stack_outputs = StackOutputs::read_from(source)?;
362
363        Ok(PublicInputs {
364            program_info,
365            stack_inputs,
366            stack_outputs,
367        })
368    }
369}