Skip to main content

miden_processor/trace/
mod.rs

1use alloc::vec::Vec;
2#[cfg(any(test, feature = "testing"))]
3use core::ops::Range;
4
5use miden_air::{
6    AirWitness, ProcessorAir, PublicInputs, debug,
7    trace::{
8        DECODER_TRACE_OFFSET, MainTrace, TRACE_WIDTH,
9        decoder::{NUM_USER_OP_HELPERS, USER_OP_HELPERS_OFFSET},
10    },
11};
12use miden_core::{crypto::hash::Blake3_256, serde::Serializable};
13
14use crate::{
15    Felt, MIN_STACK_DEPTH, Program, ProgramInfo, StackInputs, StackOutputs, Word, ZERO,
16    fast::ExecutionOutput,
17    field::QuadFelt,
18    precompile::{PrecompileRequest, PrecompileTranscript, PrecompileTranscriptDigest},
19    utils::{Matrix, RowMajorMatrix},
20};
21
22pub(crate) mod utils;
23use utils::TraceFragment;
24
25pub mod chiplets;
26pub(crate) mod execution_tracer;
27
28mod block_stack;
29mod parallel;
30mod range;
31mod stack;
32mod trace_state;
33
34#[cfg(test)]
35mod tests;
36
37// RE-EXPORTS
38// ================================================================================================
39
40pub use execution_tracer::TraceGenerationContext;
41pub use miden_air::trace::RowIndex;
42pub use parallel::{CORE_TRACE_WIDTH, build_trace, build_trace_with_max_len};
43pub use utils::{ChipletsLengths, TraceLenSummary};
44
45/// Inputs required to build an execution trace from pre-executed data.
46#[derive(Debug)]
47pub struct TraceBuildInputs {
48    trace_output: TraceBuildOutput,
49    trace_generation_context: TraceGenerationContext,
50    program_info: ProgramInfo,
51}
52
53#[derive(Debug)]
54pub(crate) struct TraceBuildOutput {
55    stack_outputs: StackOutputs,
56    final_precompile_transcript: PrecompileTranscript,
57    precompile_requests: Vec<PrecompileRequest>,
58    precompile_requests_digest: [u8; 32],
59}
60
61impl TraceBuildOutput {
62    fn from_execution_output(execution_output: ExecutionOutput) -> Self {
63        let ExecutionOutput {
64            stack,
65            mut advice,
66            memory: _,
67            final_precompile_transcript,
68        } = execution_output;
69
70        Self {
71            stack_outputs: stack,
72            final_precompile_transcript,
73            precompile_requests: advice.take_precompile_requests(),
74            precompile_requests_digest: [0; 32],
75        }
76        .with_precompile_requests_digest()
77    }
78
79    fn with_precompile_requests_digest(mut self) -> Self {
80        self.precompile_requests_digest =
81            Blake3_256::hash(&self.precompile_requests.to_bytes()).into();
82        self
83    }
84
85    fn has_matching_precompile_requests_digest(&self) -> bool {
86        let expected_digest: [u8; 32] =
87            Blake3_256::hash(&self.precompile_requests.to_bytes()).into();
88        self.precompile_requests_digest == expected_digest
89    }
90}
91
92impl TraceBuildInputs {
93    pub(crate) fn from_execution(
94        program: &Program,
95        execution_output: ExecutionOutput,
96        trace_generation_context: TraceGenerationContext,
97    ) -> Self {
98        let trace_output = TraceBuildOutput::from_execution_output(execution_output);
99        let program_info = program.to_info();
100        Self {
101            trace_output,
102            trace_generation_context,
103            program_info,
104        }
105    }
106
107    /// Returns the stack outputs captured for the execution being replayed.
108    pub fn stack_outputs(&self) -> &StackOutputs {
109        &self.trace_output.stack_outputs
110    }
111
112    /// Returns deferred precompile requests generated during execution.
113    pub fn precompile_requests(&self) -> &[PrecompileRequest] {
114        &self.trace_output.precompile_requests
115    }
116
117    /// Returns the final precompile transcript observed during execution.
118    pub fn final_precompile_transcript(&self) -> &PrecompileTranscript {
119        &self.trace_output.final_precompile_transcript
120    }
121
122    /// Returns the digest of the final precompile transcript observed during execution.
123    pub fn precompile_transcript_digest(&self) -> PrecompileTranscriptDigest {
124        self.final_precompile_transcript().finalize()
125    }
126
127    /// Returns the program info captured for the execution being replayed.
128    pub fn program_info(&self) -> &ProgramInfo {
129        &self.program_info
130    }
131
132    // Kept for mismatch and edge-case tests that mutate replay inputs directly.
133    #[cfg(any(test, feature = "testing"))]
134    #[cfg_attr(all(feature = "testing", not(test)), expect(dead_code))]
135    pub(crate) fn into_parts(self) -> (TraceBuildOutput, TraceGenerationContext, ProgramInfo) {
136        (self.trace_output, self.trace_generation_context, self.program_info)
137    }
138
139    #[cfg(any(test, feature = "testing"))]
140    /// Returns the trace replay context captured during execution.
141    pub fn trace_generation_context(&self) -> &TraceGenerationContext {
142        &self.trace_generation_context
143    }
144
145    // Kept for tests that force invalid replay contexts without widening the public API.
146    #[cfg(any(test, feature = "testing"))]
147    #[cfg_attr(all(feature = "testing", not(test)), expect(dead_code))]
148    pub(crate) fn trace_generation_context_mut(&mut self) -> &mut TraceGenerationContext {
149        &mut self.trace_generation_context
150    }
151
152    #[cfg(test)]
153    pub(crate) fn from_parts(
154        trace_output: TraceBuildOutput,
155        trace_generation_context: TraceGenerationContext,
156        program_info: ProgramInfo,
157    ) -> Self {
158        Self {
159            trace_output,
160            trace_generation_context,
161            program_info,
162        }
163    }
164}
165
166// VM EXECUTION TRACE
167// ================================================================================================
168
169/// Execution trace which is generated when a program is executed on the VM.
170///
171/// The trace consists of the following components:
172/// - Main traces of System, Decoder, Operand Stack, Range Checker, and Chiplets.
173/// - Information about the program (program hash and the kernel).
174/// - Information about execution outputs (stack state, deferred precompile requests, and the final
175///   precompile transcript).
176/// - Summary of trace lengths of the main trace components.
177#[derive(Debug)]
178pub struct ExecutionTrace {
179    main_trace: MainTrace,
180    program_info: ProgramInfo,
181    stack_outputs: StackOutputs,
182    precompile_requests: Vec<PrecompileRequest>,
183    final_precompile_transcript: PrecompileTranscript,
184    trace_len_summary: TraceLenSummary,
185}
186
187impl ExecutionTrace {
188    // CONSTRUCTOR
189    // --------------------------------------------------------------------------------------------
190
191    pub(crate) fn new_from_parts(
192        program_info: ProgramInfo,
193        trace_output: TraceBuildOutput,
194        main_trace: MainTrace,
195        trace_len_summary: TraceLenSummary,
196    ) -> Self {
197        let TraceBuildOutput {
198            stack_outputs,
199            final_precompile_transcript,
200            precompile_requests,
201            ..
202        } = trace_output;
203
204        Self {
205            main_trace,
206            program_info,
207            stack_outputs,
208            precompile_requests,
209            final_precompile_transcript,
210            trace_len_summary,
211        }
212    }
213
214    // PUBLIC ACCESSORS
215    // --------------------------------------------------------------------------------------------
216
217    /// Returns the program info of this execution trace.
218    pub fn program_info(&self) -> &ProgramInfo {
219        &self.program_info
220    }
221
222    /// Returns hash of the program execution of which resulted in this execution trace.
223    pub fn program_hash(&self) -> &Word {
224        self.program_info.program_hash()
225    }
226
227    /// Returns outputs of the program execution which resulted in this execution trace.
228    pub fn stack_outputs(&self) -> &StackOutputs {
229        &self.stack_outputs
230    }
231
232    /// Returns the public inputs for this execution trace.
233    pub fn public_inputs(&self) -> PublicInputs {
234        PublicInputs::new(
235            self.program_info.clone(),
236            self.init_stack_state(),
237            self.stack_outputs,
238            self.final_precompile_transcript.state(),
239        )
240    }
241
242    /// Returns the public values for this execution trace.
243    pub fn to_public_values(&self) -> Vec<Felt> {
244        self.public_inputs().to_elements()
245    }
246
247    /// Returns a reference to the main trace.
248    pub fn main_trace(&self) -> &MainTrace {
249        &self.main_trace
250    }
251
252    /// Returns a mutable reference to the main trace.
253    pub fn main_trace_mut(&mut self) -> &mut MainTrace {
254        &mut self.main_trace
255    }
256
257    /// Returns the precompile requests generated during program execution.
258    pub fn precompile_requests(&self) -> &[PrecompileRequest] {
259        &self.precompile_requests
260    }
261
262    /// Returns the final precompile transcript observed during execution.
263    pub fn final_precompile_transcript(&self) -> PrecompileTranscript {
264        self.final_precompile_transcript
265    }
266
267    /// Returns the digest of the final precompile transcript observed during execution.
268    pub fn precompile_transcript_digest(&self) -> PrecompileTranscriptDigest {
269        self.final_precompile_transcript().finalize()
270    }
271
272    /// Returns the owned execution outputs required for proof packaging.
273    pub fn into_outputs(self) -> (StackOutputs, Vec<PrecompileRequest>, PrecompileTranscript) {
274        (self.stack_outputs, self.precompile_requests, self.final_precompile_transcript)
275    }
276
277    /// Returns the initial state of the top 16 stack registers.
278    pub fn init_stack_state(&self) -> StackInputs {
279        let mut result = [ZERO; MIN_STACK_DEPTH];
280        let row = RowIndex::from(0_u32);
281        for (i, result) in result.iter_mut().enumerate() {
282            *result = self.main_trace.stack_element(i, row);
283        }
284        result.into()
285    }
286
287    /// Returns the final state of the top 16 stack registers.
288    pub fn last_stack_state(&self) -> StackOutputs {
289        let last_step = RowIndex::from(self.last_step());
290        let mut result = [ZERO; MIN_STACK_DEPTH];
291        for (i, result) in result.iter_mut().enumerate() {
292            *result = self.main_trace.stack_element(i, last_step);
293        }
294        result.into()
295    }
296
297    /// Returns helper registers state at the specified `clk` of the VM
298    pub fn get_user_op_helpers_at(&self, clk: u32) -> [Felt; NUM_USER_OP_HELPERS] {
299        let mut result = [ZERO; NUM_USER_OP_HELPERS];
300        let row = RowIndex::from(clk);
301        for (i, result) in result.iter_mut().enumerate() {
302            *result = self.main_trace.get(row, DECODER_TRACE_OFFSET + USER_OP_HELPERS_OFFSET + i);
303        }
304        result
305    }
306
307    /// Returns the trace length.
308    pub fn get_trace_len(&self) -> usize {
309        self.main_trace.num_rows()
310    }
311
312    /// Returns the length of the trace (number of rows in the main trace).
313    pub fn length(&self) -> usize {
314        self.get_trace_len()
315    }
316
317    /// Returns a summary of the lengths of main, range and chiplet traces.
318    pub fn trace_len_summary(&self) -> &TraceLenSummary {
319        &self.trace_len_summary
320    }
321
322    // DEBUG CONSTRAINT CHECKING
323    // --------------------------------------------------------------------------------------------
324
325    /// Validates this execution trace against all AIR constraints without generating a STARK
326    /// proof.
327    ///
328    /// This is the recommended way to test trace correctness. It is much faster than full STARK
329    /// proving and provides better error diagnostics (panics on the first constraint violation
330    /// with the instance and row index).
331    ///
332    /// # Panics
333    ///
334    /// Panics if any AIR constraint evaluates to nonzero.
335    pub fn check_constraints(&self) {
336        let public_inputs = self.public_inputs();
337        let trace_matrix = self.to_row_major_matrix();
338
339        let (public_values, kernel_felts) = public_inputs.to_air_inputs();
340        let var_len_public_inputs: &[&[Felt]] = &[&kernel_felts];
341
342        // Derive deterministic challenges by hashing public values with Poseidon2.
343        // The 4-element digest maps directly to 2 QuadFelt challenges.
344        let digest = crate::crypto::hash::Poseidon2::hash_elements(&public_values);
345        let challenges =
346            [QuadFelt::new([digest[0], digest[1]]), QuadFelt::new([digest[2], digest[3]])];
347
348        let witness = AirWitness::new(&trace_matrix, &public_values, var_len_public_inputs);
349        debug::check_constraints(&ProcessorAir, witness, &ProcessorAir, &challenges);
350    }
351
352    /// Returns the main trace as a row-major matrix for proving.
353    pub fn to_row_major_matrix(&self) -> RowMajorMatrix<Felt> {
354        let row_major = self.main_trace.to_row_major();
355        debug_assert_eq!(row_major.width(), TRACE_WIDTH);
356        row_major
357    }
358
359    // HELPER METHODS
360    // --------------------------------------------------------------------------------------------
361
362    /// Returns the index of the last row in the trace.
363    fn last_step(&self) -> usize {
364        self.length() - 1
365    }
366
367    // TEST HELPERS
368    // --------------------------------------------------------------------------------------------
369    #[cfg(feature = "std")]
370    pub fn print(&self) {
371        let mut row = [ZERO; TRACE_WIDTH];
372        for i in 0..self.length() {
373            self.main_trace.read_row_into(i, &mut row);
374            std::println!("{:?}", row.map(|v| v.as_canonical_u64()));
375        }
376    }
377
378    #[cfg(any(test, feature = "testing"))]
379    pub fn get_column_range(&self, range: Range<usize>) -> Vec<Vec<Felt>> {
380        self.main_trace.get_column_range(range)
381    }
382}