1use alloc::vec::Vec;
2#[cfg(any(test, feature = "testing"))]
3use core::ops::Range;
4
5use miden_air::{
6 AirWitness, AuxBuilder, ProcessorAir, PublicInputs, debug,
7 trace::{
8 DECODER_TRACE_OFFSET, MainTrace, PADDED_TRACE_WIDTH, 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::{ExtensionField, QuadFelt},
18 precompile::{PrecompileRequest, PrecompileTranscript, PrecompileTranscriptDigest},
19 utils::{ColMatrix, Matrix, RowMajorMatrix},
20};
21
22pub(crate) mod utils;
23use miden_air::trace::Challenges;
24use utils::{AuxColumnBuilder, TraceFragment};
25
26pub mod chiplets;
27pub(crate) mod execution_tracer;
28
29mod decoder;
30mod parallel;
31mod range;
32mod stack;
33mod trace_state;
34
35#[cfg(test)]
36mod tests;
37
38pub use execution_tracer::TraceGenerationContext;
42pub use miden_air::trace::RowIndex;
43pub use parallel::{CORE_TRACE_WIDTH, build_trace, build_trace_with_max_len};
44pub use utils::{ChipletsLengths, TraceLenSummary};
45
46#[derive(Debug)]
48pub struct TraceBuildInputs {
49 trace_output: TraceBuildOutput,
50 trace_generation_context: TraceGenerationContext,
51 program_info: ProgramInfo,
52}
53
54#[derive(Debug)]
55pub(crate) struct TraceBuildOutput {
56 stack_outputs: StackOutputs,
57 final_precompile_transcript: PrecompileTranscript,
58 precompile_requests: Vec<PrecompileRequest>,
59 precompile_requests_digest: [u8; 32],
60}
61
62impl TraceBuildOutput {
63 fn from_execution_output(execution_output: ExecutionOutput) -> Self {
64 let ExecutionOutput {
65 stack,
66 mut advice,
67 memory: _,
68 final_precompile_transcript,
69 } = execution_output;
70
71 Self {
72 stack_outputs: stack,
73 final_precompile_transcript,
74 precompile_requests: advice.take_precompile_requests(),
75 precompile_requests_digest: [0; 32],
76 }
77 .with_precompile_requests_digest()
78 }
79
80 fn with_precompile_requests_digest(mut self) -> Self {
81 self.precompile_requests_digest =
82 Blake3_256::hash(&self.precompile_requests.to_bytes()).into();
83 self
84 }
85
86 fn has_matching_precompile_requests_digest(&self) -> bool {
87 let expected_digest: [u8; 32] =
88 Blake3_256::hash(&self.precompile_requests.to_bytes()).into();
89 self.precompile_requests_digest == expected_digest
90 }
91}
92
93impl TraceBuildInputs {
94 pub(crate) fn from_execution(
95 program: &Program,
96 execution_output: ExecutionOutput,
97 trace_generation_context: TraceGenerationContext,
98 ) -> Self {
99 let trace_output = TraceBuildOutput::from_execution_output(execution_output);
100 let program_info = program.to_info();
101 Self {
102 trace_output,
103 trace_generation_context,
104 program_info,
105 }
106 }
107
108 pub fn stack_outputs(&self) -> &StackOutputs {
110 &self.trace_output.stack_outputs
111 }
112
113 pub fn precompile_requests(&self) -> &[PrecompileRequest] {
115 &self.trace_output.precompile_requests
116 }
117
118 pub fn final_precompile_transcript(&self) -> &PrecompileTranscript {
120 &self.trace_output.final_precompile_transcript
121 }
122
123 pub fn precompile_transcript_digest(&self) -> PrecompileTranscriptDigest {
125 self.final_precompile_transcript().finalize()
126 }
127
128 pub fn program_info(&self) -> &ProgramInfo {
130 &self.program_info
131 }
132
133 #[cfg(any(test, feature = "testing"))]
135 #[cfg_attr(all(feature = "testing", not(test)), expect(dead_code))]
136 pub(crate) fn into_parts(self) -> (TraceBuildOutput, TraceGenerationContext, ProgramInfo) {
137 (self.trace_output, self.trace_generation_context, self.program_info)
138 }
139
140 #[cfg(any(test, feature = "testing"))]
141 pub fn trace_generation_context(&self) -> &TraceGenerationContext {
143 &self.trace_generation_context
144 }
145
146 #[cfg(any(test, feature = "testing"))]
148 #[cfg_attr(all(feature = "testing", not(test)), expect(dead_code))]
149 pub(crate) fn trace_generation_context_mut(&mut self) -> &mut TraceGenerationContext {
150 &mut self.trace_generation_context
151 }
152
153 #[cfg(test)]
154 pub(crate) fn from_parts(
155 trace_output: TraceBuildOutput,
156 trace_generation_context: TraceGenerationContext,
157 program_info: ProgramInfo,
158 ) -> Self {
159 Self {
160 trace_output,
161 trace_generation_context,
162 program_info,
163 }
164 }
165}
166
167#[derive(Debug)]
180pub struct ExecutionTrace {
181 main_trace: MainTrace,
182 aux_trace_builders: AuxTraceBuilders,
183 program_info: ProgramInfo,
184 stack_outputs: StackOutputs,
185 precompile_requests: Vec<PrecompileRequest>,
186 final_precompile_transcript: PrecompileTranscript,
187 trace_len_summary: TraceLenSummary,
188}
189
190impl ExecutionTrace {
191 pub(crate) fn new_from_parts(
195 program_info: ProgramInfo,
196 trace_output: TraceBuildOutput,
197 main_trace: MainTrace,
198 aux_trace_builders: AuxTraceBuilders,
199 trace_len_summary: TraceLenSummary,
200 ) -> Self {
201 let TraceBuildOutput {
202 stack_outputs,
203 final_precompile_transcript,
204 precompile_requests,
205 ..
206 } = trace_output;
207
208 Self {
209 main_trace,
210 aux_trace_builders,
211 program_info,
212 stack_outputs,
213 precompile_requests,
214 final_precompile_transcript,
215 trace_len_summary,
216 }
217 }
218
219 pub fn program_info(&self) -> &ProgramInfo {
224 &self.program_info
225 }
226
227 pub fn program_hash(&self) -> &Word {
229 self.program_info.program_hash()
230 }
231
232 pub fn stack_outputs(&self) -> &StackOutputs {
234 &self.stack_outputs
235 }
236
237 pub fn public_inputs(&self) -> PublicInputs {
239 PublicInputs::new(
240 self.program_info.clone(),
241 self.init_stack_state(),
242 self.stack_outputs,
243 self.final_precompile_transcript.state(),
244 )
245 }
246
247 pub fn to_public_values(&self) -> Vec<Felt> {
249 self.public_inputs().to_elements()
250 }
251
252 pub fn aux_trace_builders(&self) -> AuxTraceBuilders {
254 self.aux_trace_builders.clone()
255 }
256
257 pub fn main_trace(&self) -> &MainTrace {
259 &self.main_trace
260 }
261
262 pub fn main_trace_mut(&mut self) -> &mut MainTrace {
264 &mut self.main_trace
265 }
266
267 pub fn precompile_requests(&self) -> &[PrecompileRequest] {
269 &self.precompile_requests
270 }
271
272 pub fn final_precompile_transcript(&self) -> PrecompileTranscript {
274 self.final_precompile_transcript
275 }
276
277 pub fn precompile_transcript_digest(&self) -> PrecompileTranscriptDigest {
279 self.final_precompile_transcript().finalize()
280 }
281
282 pub fn into_outputs(self) -> (StackOutputs, Vec<PrecompileRequest>, PrecompileTranscript) {
284 (self.stack_outputs, self.precompile_requests, self.final_precompile_transcript)
285 }
286
287 pub fn init_stack_state(&self) -> StackInputs {
289 let mut result = [ZERO; MIN_STACK_DEPTH];
290 let row = RowIndex::from(0_u32);
291 for (i, result) in result.iter_mut().enumerate() {
292 *result = self.main_trace.stack_element(i, row);
293 }
294 result.into()
295 }
296
297 pub fn last_stack_state(&self) -> StackOutputs {
299 let last_step = RowIndex::from(self.last_step());
300 let mut result = [ZERO; MIN_STACK_DEPTH];
301 for (i, result) in result.iter_mut().enumerate() {
302 *result = self.main_trace.stack_element(i, last_step);
303 }
304 result.into()
305 }
306
307 pub fn get_user_op_helpers_at(&self, clk: u32) -> [Felt; NUM_USER_OP_HELPERS] {
309 let mut result = [ZERO; NUM_USER_OP_HELPERS];
310 let row = RowIndex::from(clk);
311 for (i, result) in result.iter_mut().enumerate() {
312 *result = self.main_trace.get(row, DECODER_TRACE_OFFSET + USER_OP_HELPERS_OFFSET + i);
313 }
314 result
315 }
316
317 pub fn get_trace_len(&self) -> usize {
319 self.main_trace.num_rows()
320 }
321
322 pub fn length(&self) -> usize {
324 self.get_trace_len()
325 }
326
327 pub fn trace_len_summary(&self) -> &TraceLenSummary {
329 &self.trace_len_summary
330 }
331
332 pub fn check_constraints(&self) {
346 let public_inputs = self.public_inputs();
347 let trace_matrix = self.to_row_major_matrix();
348
349 let (public_values, kernel_felts) = public_inputs.to_air_inputs();
350 let var_len_public_inputs: &[&[Felt]] = &[&kernel_felts];
351
352 let aux_builder = self.aux_trace_builders();
353
354 let digest = crate::crypto::hash::Poseidon2::hash_elements(&public_values);
357 let challenges =
358 [QuadFelt::new([digest[0], digest[1]]), QuadFelt::new([digest[2], digest[3]])];
359
360 let witness = AirWitness::new(&trace_matrix, &public_values, var_len_public_inputs);
361 debug::check_constraints(&ProcessorAir, witness, &aux_builder, &challenges);
362 }
363
364 pub fn to_row_major_matrix(&self) -> RowMajorMatrix<Felt> {
371 let stored_w = self.main_trace.width();
372 if stored_w == TRACE_WIDTH {
373 return self.main_trace.to_row_major();
374 }
375
376 assert_eq!(stored_w, PADDED_TRACE_WIDTH);
377 self.main_trace.to_row_major_stripped(TRACE_WIDTH)
378 }
379
380 fn last_step(&self) -> usize {
385 self.length() - 1
386 }
387
388 #[cfg(feature = "std")]
391 pub fn print(&self) {
392 use miden_air::trace::TRACE_WIDTH;
393
394 let mut row = [ZERO; PADDED_TRACE_WIDTH];
395 for i in 0..self.length() {
396 self.main_trace.read_row_into(i, &mut row);
397 std::println!(
398 "{:?}",
399 row.iter().take(TRACE_WIDTH).map(|v| v.as_canonical_u64()).collect::<Vec<_>>()
400 );
401 }
402 }
403
404 #[cfg(any(test, feature = "testing"))]
405 pub fn get_column_range(&self, range: Range<usize>) -> Vec<Vec<Felt>> {
406 self.main_trace.get_column_range(range)
407 }
408
409 pub fn build_aux_trace<E>(&self, rand_elements: &[E]) -> Option<ColMatrix<E>>
410 where
411 E: ExtensionField<Felt>,
412 {
413 let aux_columns =
414 self.aux_trace_builders.build_aux_columns(&self.main_trace, rand_elements);
415
416 Some(ColMatrix::new(aux_columns))
417 }
418}
419
420#[derive(Debug, Clone)]
424pub struct AuxTraceBuilders {
425 pub(crate) decoder: decoder::AuxTraceBuilder,
426 pub(crate) stack: stack::AuxTraceBuilder,
427 pub(crate) range: range::AuxTraceBuilder,
428 pub(crate) chiplets: chiplets::AuxTraceBuilder,
429}
430
431impl AuxTraceBuilders {
432 pub fn build_aux_columns<E>(&self, main_trace: &MainTrace, challenges: &[E]) -> Vec<Vec<E>>
436 where
437 E: ExtensionField<Felt>,
438 {
439 let challenges = Challenges::<E>::new(challenges[0], challenges[1]);
442
443 let (decoder_cols, stack_cols, range_cols, chiplets_cols) = {
444 let ((decoder_cols, stack_cols), (range_cols, chiplets_cols)) = rayon::join(
445 || {
446 rayon::join(
447 || self.decoder.build_aux_columns(main_trace, &challenges),
448 || self.stack.build_aux_columns(main_trace, &challenges),
449 )
450 },
451 || {
452 rayon::join(
453 || self.range.build_aux_columns(main_trace, &challenges),
454 || {
455 let [a, b, c] =
456 self.chiplets.build_aux_columns(main_trace, &challenges);
457 vec![a, b, c]
458 },
459 )
460 },
461 );
462 (decoder_cols, stack_cols, range_cols, chiplets_cols)
463 };
464
465 decoder_cols
466 .into_iter()
467 .chain(stack_cols)
468 .chain(range_cols)
469 .chain(chiplets_cols)
470 .collect()
471 }
472}
473
474impl<EF: ExtensionField<Felt>> AuxBuilder<Felt, EF> for AuxTraceBuilders {
478 fn build_aux_trace(
479 &self,
480 main: &RowMajorMatrix<Felt>,
481 challenges: &[EF],
482 ) -> (RowMajorMatrix<EF>, Vec<EF>) {
483 let _span = tracing::info_span!("build_aux_trace").entered();
484
485 let main_for_aux = {
489 let num_rows = main.height();
490 let clk0 = main.get(0, 0).expect("valid indices");
492 let last_program_row = if num_rows <= 1 {
493 0
494 } else if main.get(num_rows - 1, 0).expect("valid indices")
495 == clk0 + Felt::new((num_rows - 1) as u64)
496 {
497 num_rows - 1
498 } else {
499 let mut lo = 1usize;
500 let mut hi = num_rows - 1;
501 while lo < hi {
502 let mid = lo + (hi - lo) / 2;
503 let expected = clk0 + Felt::new(mid as u64);
504 if main.get(mid, 0).expect("valid indices") == expected {
505 lo = mid + 1;
506 } else {
507 hi = mid;
508 }
509 }
510 lo - 1
511 };
512 let transposed = main.transpose();
513 MainTrace::from_transposed(transposed, RowIndex::from(last_program_row))
514 };
515
516 let aux_columns = self.build_aux_columns(&main_for_aux, challenges);
517 assert!(!aux_columns.is_empty(), "aux columns should not be empty");
518
519 let trace_len = main.height();
520 let num_ef_cols = aux_columns.len();
521 for col in &aux_columns {
522 debug_assert_eq!(col.len(), trace_len, "aux column length must match main height");
523 }
524
525 let mut flat = Vec::with_capacity(trace_len * num_ef_cols);
526 for col in &aux_columns {
527 flat.extend_from_slice(col);
528 }
529 let aux_trace = RowMajorMatrix::new(flat, trace_len).transpose();
530
531 let last_row = aux_trace
533 .row_slice(trace_len - 1)
534 .expect("aux trace has at least one row")
535 .to_vec();
536
537 (aux_trace, last_row)
538 }
539}