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