use super::{
chiplets::AuxTraceBuilder as ChipletsAuxTraceBuilder, crypto::RpoRandomCoin,
decoder::AuxTraceBuilder as DecoderAuxTraceBuilder,
range::AuxTraceBuilder as RangeCheckerAuxTraceBuilder,
stack::AuxTraceBuilder as StackAuxTraceBuilder, ColMatrix, Digest, Felt, FieldElement, Host,
Process, StackTopState,
};
use crate::utils::collections::*;
use miden_air::trace::{
decoder::{NUM_USER_OP_HELPERS, USER_OP_HELPERS_OFFSET},
main_trace::MainTrace,
AUX_TRACE_RAND_ELEMENTS, AUX_TRACE_WIDTH, DECODER_TRACE_OFFSET, MIN_TRACE_LEN,
STACK_TRACE_OFFSET, TRACE_WIDTH,
};
use vm_core::{stack::STACK_TOP_SIZE, ProgramInfo, StackOutputs, ZERO};
use winter_prover::{crypto::RandomCoin, EvaluationFrame, Trace, TraceLayout};
mod utils;
pub use utils::{AuxColumnBuilder, ChipletsLengths, TraceFragment, TraceLenSummary};
#[cfg(test)]
mod tests;
#[cfg(test)]
use super::EMPTY_WORD;
pub const NUM_RAND_ROWS: usize = 1;
pub struct AuxTraceBuilders {
pub(crate) decoder: DecoderAuxTraceBuilder,
pub(crate) stack: StackAuxTraceBuilder,
pub(crate) range: RangeCheckerAuxTraceBuilder,
pub(crate) chiplets: ChipletsAuxTraceBuilder,
}
pub struct ExecutionTrace {
meta: Vec<u8>,
layout: TraceLayout,
main_trace: MainTrace,
aux_trace_builders: AuxTraceBuilders,
program_info: ProgramInfo,
stack_outputs: StackOutputs,
trace_len_summary: TraceLenSummary,
}
impl ExecutionTrace {
pub const NUM_RAND_ROWS: usize = NUM_RAND_ROWS;
pub(super) fn new<H>(process: Process<H>, stack_outputs: StackOutputs) -> Self
where
H: Host,
{
let program_hash = process.decoder.program_hash();
let rng = RpoRandomCoin::new(program_hash);
let kernel = process.kernel().clone();
let program_info = ProgramInfo::new(program_hash.into(), kernel);
let (main_trace, aux_trace_hints, trace_len_summary) = finalize_trace(process, rng);
Self {
meta: Vec::new(),
layout: TraceLayout::new(TRACE_WIDTH, [AUX_TRACE_WIDTH], [AUX_TRACE_RAND_ELEMENTS]),
aux_trace_builders: aux_trace_hints,
main_trace,
program_info,
stack_outputs,
trace_len_summary,
}
}
pub fn program_info(&self) -> &ProgramInfo {
&self.program_info
}
pub fn program_hash(&self) -> &Digest {
self.program_info.program_hash()
}
pub fn stack_outputs(&self) -> &StackOutputs {
&self.stack_outputs
}
pub fn init_stack_state(&self) -> StackTopState {
let mut result = [ZERO; STACK_TOP_SIZE];
for (i, result) in result.iter_mut().enumerate() {
*result = self.main_trace.get_column(i + STACK_TRACE_OFFSET)[0];
}
result
}
pub fn last_stack_state(&self) -> StackTopState {
let last_step = self.last_step();
let mut result = [ZERO; STACK_TOP_SIZE];
for (i, result) in result.iter_mut().enumerate() {
*result = self.main_trace.get_column(i + STACK_TRACE_OFFSET)[last_step];
}
result
}
pub fn get_user_op_helpers_at(&self, clk: u32) -> [Felt; NUM_USER_OP_HELPERS] {
let mut result = [ZERO; NUM_USER_OP_HELPERS];
for (i, result) in result.iter_mut().enumerate() {
*result = self.main_trace.get_column(DECODER_TRACE_OFFSET + USER_OP_HELPERS_OFFSET + i)
[clk as usize];
}
result
}
pub fn get_trace_len(&self) -> usize {
self.main_trace.num_rows()
}
pub fn trace_len_summary(&self) -> &TraceLenSummary {
&self.trace_len_summary
}
fn last_step(&self) -> usize {
self.length() - NUM_RAND_ROWS - 1
}
#[cfg(feature = "std")]
#[allow(dead_code)]
pub fn print(&self) {
let mut row = [ZERO; TRACE_WIDTH];
for i in 0..self.length() {
self.main_trace.read_row_into(i, &mut row);
println!("{:?}", row.iter().map(|v| v.as_int()).collect::<Vec<_>>());
}
}
#[cfg(test)]
pub fn test_finalize_trace<H>(
process: Process<H>,
) -> (MainTrace, AuxTraceBuilders, TraceLenSummary)
where
H: Host,
{
let rng = RpoRandomCoin::new(EMPTY_WORD);
finalize_trace(process, rng)
}
}
impl Trace for ExecutionTrace {
type BaseField = Felt;
fn layout(&self) -> &TraceLayout {
&self.layout
}
fn length(&self) -> usize {
self.main_trace.num_rows()
}
fn meta(&self) -> &[u8] {
&self.meta
}
fn main_segment(&self) -> &ColMatrix<Felt> {
&self.main_trace
}
fn build_aux_segment<E: FieldElement<BaseField = Felt>>(
&mut self,
aux_segments: &[ColMatrix<E>],
rand_elements: &[E],
) -> Option<ColMatrix<E>> {
if !aux_segments.is_empty() {
return None;
}
let decoder_aux_columns = self
.aux_trace_builders
.decoder
.build_aux_columns(&self.main_trace, rand_elements);
let stack_aux_columns =
self.aux_trace_builders.stack.build_aux_columns(&self.main_trace, rand_elements);
let range_aux_columns =
self.aux_trace_builders.range.build_aux_columns(&self.main_trace, rand_elements);
let chiplets = self
.aux_trace_builders
.chiplets
.build_aux_columns(&self.main_trace, rand_elements);
let mut aux_columns = decoder_aux_columns
.into_iter()
.chain(stack_aux_columns)
.chain(range_aux_columns)
.chain(chiplets)
.collect::<Vec<_>>();
let mut rng = RpoRandomCoin::new(self.program_hash().into());
for i in self.length() - NUM_RAND_ROWS..self.length() {
for column in aux_columns.iter_mut() {
column[i] = rng.draw().expect("failed to draw a random value");
}
}
Some(ColMatrix::new(aux_columns))
}
fn read_main_frame(&self, row_idx: usize, frame: &mut EvaluationFrame<Felt>) {
let next_row_idx = (row_idx + 1) % self.length();
self.main_trace.read_row_into(row_idx, frame.current_mut());
self.main_trace.read_row_into(next_row_idx, frame.next_mut());
}
}
fn finalize_trace<H>(
process: Process<H>,
mut rng: RpoRandomCoin,
) -> (MainTrace, AuxTraceBuilders, TraceLenSummary)
where
H: Host,
{
let (system, decoder, stack, mut range, chiplets, _) = process.into_parts();
let clk = system.clk();
assert_eq!(clk as usize, system.trace_len(), "inconsistent system trace lengths");
assert_eq!(clk as usize, decoder.trace_len(), "inconsistent decoder trace length");
assert_eq!(clk as usize, stack.trace_len(), "inconsistent stack trace lengths");
chiplets.append_range_checks(&mut range);
let range_table_len = range.get_number_range_checker_rows();
let max_len = range_table_len.max(clk as usize).max(chiplets.trace_len());
let trace_len = (max_len + NUM_RAND_ROWS).next_power_of_two();
assert!(
trace_len >= MIN_TRACE_LEN,
"trace length must be at least {MIN_TRACE_LEN}, but was {trace_len}",
);
let trace_len_summary =
TraceLenSummary::new(clk as usize, range_table_len, ChipletsLengths::new(&chiplets));
let system_trace = system.into_trace(trace_len, NUM_RAND_ROWS);
let decoder_trace = decoder.into_trace(trace_len, NUM_RAND_ROWS);
let stack_trace = stack.into_trace(trace_len, NUM_RAND_ROWS);
let chiplets_trace = chiplets.into_trace(trace_len, NUM_RAND_ROWS);
let range_check_trace = range.into_trace_with_table(range_table_len, trace_len, NUM_RAND_ROWS);
let mut trace = system_trace
.into_iter()
.chain(decoder_trace.trace)
.chain(stack_trace.trace)
.chain(range_check_trace.trace)
.chain(chiplets_trace.trace)
.collect::<Vec<_>>();
for i in trace_len - NUM_RAND_ROWS..trace_len {
for column in trace.iter_mut() {
column[i] = rng.draw().expect("failed to draw a random value");
}
}
let aux_trace_hints = AuxTraceBuilders {
decoder: decoder_trace.aux_builder,
stack: stack_trace.aux_builder,
range: range_check_trace.aux_builder,
chiplets: chiplets_trace.aux_builder,
};
let main_trace = MainTrace::new(ColMatrix::new(trace));
(main_trace, aux_trace_hints, trace_len_summary)
}