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