1use alloc::vec::Vec;
2
3use miden_air::trace::{
4 AUX_TRACE_RAND_ELEMENTS, AUX_TRACE_WIDTH, DECODER_TRACE_OFFSET, MIN_TRACE_LEN,
5 STACK_TRACE_OFFSET, TRACE_WIDTH,
6 decoder::{NUM_USER_OP_HELPERS, USER_OP_HELPERS_OFFSET},
7 main_trace::MainTrace,
8};
9use vm_core::{ProgramInfo, StackInputs, StackOutputs, ZERO, stack::MIN_STACK_DEPTH};
10use winter_prover::{EvaluationFrame, Trace, TraceInfo, crypto::RandomCoin};
11
12use super::{
13 ColMatrix, Digest, Felt, FieldElement, Process,
14 chiplets::AuxTraceBuilder as ChipletsAuxTraceBuilder, crypto::RpoRandomCoin,
15 decoder::AuxTraceBuilder as DecoderAuxTraceBuilder,
16 range::AuxTraceBuilder as RangeCheckerAuxTraceBuilder,
17 stack::AuxTraceBuilder as StackAuxTraceBuilder,
18};
19
20mod utils;
21pub use utils::{AuxColumnBuilder, ChipletsLengths, TraceFragment, TraceLenSummary};
22
23#[cfg(test)]
24mod tests;
25#[cfg(test)]
26use super::EMPTY_WORD;
27
28pub const NUM_RAND_ROWS: usize = 1;
33
34pub struct AuxTraceBuilders {
38 pub(crate) decoder: DecoderAuxTraceBuilder,
39 pub(crate) stack: StackAuxTraceBuilder,
40 pub(crate) range: RangeCheckerAuxTraceBuilder,
41 pub(crate) chiplets: ChipletsAuxTraceBuilder,
42}
43
44pub struct ExecutionTrace {
52 meta: Vec<u8>,
53 trace_info: TraceInfo,
54 main_trace: MainTrace,
55 aux_trace_builders: AuxTraceBuilders,
56 program_info: ProgramInfo,
57 stack_outputs: StackOutputs,
58 trace_len_summary: TraceLenSummary,
59}
60
61impl ExecutionTrace {
62 pub const NUM_RAND_ROWS: usize = NUM_RAND_ROWS;
67
68 pub fn new(process: Process, stack_outputs: StackOutputs) -> Self {
72 let program_hash = process.decoder.program_hash();
77 let rng = RpoRandomCoin::new(program_hash);
78
79 let kernel = process.kernel().clone();
81 let program_info = ProgramInfo::new(program_hash.into(), kernel);
82 let (main_trace, aux_trace_builders, trace_len_summary) = finalize_trace(process, rng);
83 let trace_info = TraceInfo::new_multi_segment(
84 TRACE_WIDTH,
85 AUX_TRACE_WIDTH,
86 AUX_TRACE_RAND_ELEMENTS,
87 main_trace.num_rows(),
88 vec![],
89 );
90
91 Self {
92 meta: Vec::new(),
93 trace_info,
94 aux_trace_builders,
95 main_trace,
96 program_info,
97 stack_outputs,
98 trace_len_summary,
99 }
100 }
101
102 pub fn program_info(&self) -> &ProgramInfo {
107 &self.program_info
108 }
109
110 pub fn program_hash(&self) -> &Digest {
112 self.program_info.program_hash()
113 }
114
115 pub fn stack_outputs(&self) -> &StackOutputs {
117 &self.stack_outputs
118 }
119
120 pub fn init_stack_state(&self) -> StackInputs {
122 let mut result = [ZERO; MIN_STACK_DEPTH];
123 for (i, result) in result.iter_mut().enumerate() {
124 *result = self.main_trace.get_column(i + STACK_TRACE_OFFSET)[0];
125 }
126 result.into()
127 }
128
129 pub fn last_stack_state(&self) -> StackOutputs {
131 let last_step = self.last_step();
132 let mut result = [ZERO; MIN_STACK_DEPTH];
133 for (i, result) in result.iter_mut().enumerate() {
134 *result = self.main_trace.get_column(i + STACK_TRACE_OFFSET)[last_step];
135 }
136 result.into()
137 }
138
139 pub fn get_user_op_helpers_at(&self, clk: u32) -> [Felt; NUM_USER_OP_HELPERS] {
141 let mut result = [ZERO; NUM_USER_OP_HELPERS];
142 for (i, result) in result.iter_mut().enumerate() {
143 *result = self.main_trace.get_column(DECODER_TRACE_OFFSET + USER_OP_HELPERS_OFFSET + i)
144 [clk as usize];
145 }
146 result
147 }
148
149 pub fn get_trace_len(&self) -> usize {
151 self.main_trace.num_rows()
152 }
153
154 pub fn trace_len_summary(&self) -> &TraceLenSummary {
156 &self.trace_len_summary
157 }
158
159 pub fn meta(&self) -> &[u8] {
161 &self.meta
162 }
163
164 fn last_step(&self) -> usize {
169 self.length() - NUM_RAND_ROWS - 1
170 }
171
172 #[cfg(feature = "std")]
175 #[allow(dead_code)]
176 pub fn print(&self) {
177 let mut row = [ZERO; TRACE_WIDTH];
178 for i in 0..self.length() {
179 self.main_trace.read_row_into(i, &mut row);
180 std::println!("{:?}", row.iter().map(|v| v.as_int()).collect::<Vec<_>>());
181 }
182 }
183
184 #[cfg(test)]
185 pub fn test_finalize_trace(process: Process) -> (MainTrace, AuxTraceBuilders, TraceLenSummary) {
186 let rng = RpoRandomCoin::new(EMPTY_WORD);
187 finalize_trace(process, rng)
188 }
189
190 pub fn build_aux_trace<E>(&self, rand_elements: &[E]) -> Option<ColMatrix<E>>
191 where
192 E: FieldElement<BaseField = Felt>,
193 {
194 let decoder_aux_columns = self
196 .aux_trace_builders
197 .decoder
198 .build_aux_columns(&self.main_trace, rand_elements);
199
200 let stack_aux_columns =
202 self.aux_trace_builders.stack.build_aux_columns(&self.main_trace, rand_elements);
203
204 let range_aux_columns =
206 self.aux_trace_builders.range.build_aux_columns(&self.main_trace, rand_elements);
207
208 let chiplets = self
210 .aux_trace_builders
211 .chiplets
212 .build_aux_columns(&self.main_trace, rand_elements);
213
214 let mut aux_columns = decoder_aux_columns
216 .into_iter()
217 .chain(stack_aux_columns)
218 .chain(range_aux_columns)
219 .chain(chiplets)
220 .collect::<Vec<_>>();
221
222 let mut rng = RpoRandomCoin::new(self.program_hash().into());
224 for i in self.length() - NUM_RAND_ROWS..self.length() {
225 for column in aux_columns.iter_mut() {
226 column[i] = rng.draw().expect("failed to draw a random value");
227 }
228 }
229
230 Some(ColMatrix::new(aux_columns))
231 }
232}
233
234impl Trace for ExecutionTrace {
238 type BaseField = Felt;
239
240 fn length(&self) -> usize {
241 self.main_trace.num_rows()
242 }
243
244 fn main_segment(&self) -> &ColMatrix<Felt> {
245 &self.main_trace
246 }
247
248 fn read_main_frame(&self, row_idx: usize, frame: &mut EvaluationFrame<Felt>) {
249 let next_row_idx = (row_idx + 1) % self.length();
250 self.main_trace.read_row_into(row_idx, frame.current_mut());
251 self.main_trace.read_row_into(next_row_idx, frame.next_mut());
252 }
253
254 fn info(&self) -> &TraceInfo {
255 &self.trace_info
256 }
257}
258
259fn finalize_trace(
271 process: Process,
272 mut rng: RpoRandomCoin,
273) -> (MainTrace, AuxTraceBuilders, TraceLenSummary) {
274 let (system, decoder, stack, mut range, chiplets) = process.into_parts();
275
276 let clk = system.clk();
277
278 assert_eq!(clk.as_usize(), system.trace_len(), "inconsistent system trace lengths");
280 assert_eq!(clk.as_usize(), decoder.trace_len(), "inconsistent decoder trace length");
281 assert_eq!(clk.as_usize(), stack.trace_len(), "inconsistent stack trace lengths");
282
283 chiplets.append_range_checks(&mut range);
285
286 let range_table_len = range.get_number_range_checker_rows();
288
289 let max_len = range_table_len.max(clk.into()).max(chiplets.trace_len());
291
292 let trace_len = (max_len + NUM_RAND_ROWS).next_power_of_two();
295 assert!(
296 trace_len >= MIN_TRACE_LEN,
297 "trace length must be at least {MIN_TRACE_LEN}, but was {trace_len}",
298 );
299
300 let trace_len_summary =
302 TraceLenSummary::new(clk.into(), range_table_len, ChipletsLengths::new(&chiplets));
303
304 let system_trace = system.into_trace(trace_len, NUM_RAND_ROWS);
306 let decoder_trace = decoder.into_trace(trace_len, NUM_RAND_ROWS);
307 let stack_trace = stack.into_trace(trace_len, NUM_RAND_ROWS);
308 let chiplets_trace = chiplets.into_trace(trace_len, NUM_RAND_ROWS);
309
310 let range_check_trace = range.into_trace_with_table(range_table_len, trace_len, NUM_RAND_ROWS);
312
313 let mut trace = system_trace
314 .into_iter()
315 .chain(decoder_trace.trace)
316 .chain(stack_trace.trace)
317 .chain(range_check_trace.trace)
318 .chain(chiplets_trace.trace)
319 .collect::<Vec<_>>();
320
321 for i in trace_len - NUM_RAND_ROWS..trace_len {
323 for column in trace.iter_mut() {
324 column[i] = rng.draw().expect("failed to draw a random value");
325 }
326 }
327
328 let aux_trace_hints = AuxTraceBuilders {
329 decoder: decoder_trace.aux_builder,
330 stack: StackAuxTraceBuilder,
331 range: range_check_trace.aux_builder,
332 chiplets: chiplets_trace.aux_builder,
333 };
334
335 let main_trace = MainTrace::new(ColMatrix::new(trace), clk);
336
337 (main_trace, aux_trace_hints, trace_len_summary)
338}