openvm_circuit/system/program/
trace.rs1use std::borrow::BorrowMut;
2
3use itertools::Itertools;
4use openvm_circuit::{arch::hasher::poseidon2::Poseidon2Hasher, primitives::Chip};
5use openvm_cpu_backend::CpuBackend;
6use openvm_instructions::{
7 exe::VmExe,
8 program::{Program, DEFAULT_PC_STEP},
9 LocalOpcode, SystemOpcode,
10};
11use openvm_stark_backend::{
12 p3_field::{Field, PrimeCharacteristicRing, PrimeField32},
13 p3_matrix::dense::RowMajorMatrix,
14 p3_maybe_rayon::prelude::*,
15 prover::AirProvingContext,
16 StarkProtocolConfig, Val,
17};
18
19use super::{Instruction, ProgramExecutionCols, EXIT_CODE_FAIL};
20use crate::{
21 arch::{
22 hasher::{poseidon2::vm_poseidon2_hasher, Hasher},
23 MemoryConfig,
24 },
25 system::{
26 memory::{merkle::MerkleTree, AddressMap, CHUNK},
27 program::ProgramChip,
28 },
29};
30
31impl<SC: StarkProtocolConfig> Chip<(), CpuBackend<SC>> for ProgramChip<SC> {
32 fn generate_proving_ctx(&self, _: ()) -> AirProvingContext<CpuBackend<SC>> {
35 let cached = self
36 .cached
37 .clone()
38 .expect("cached program trace must be loaded");
39 assert!(self.filtered_exec_frequencies.len() <= cached.height());
40 let mut freqs = Val::<SC>::zero_vec(cached.height());
41 freqs
42 .par_iter_mut()
43 .zip(self.filtered_exec_frequencies.par_iter())
44 .for_each(|(f, x)| *f = Val::<SC>::from_u32(*x));
45 let common_trace = RowMajorMatrix::new(freqs, 1);
46 AirProvingContext {
47 cached_mains: vec![cached],
48 common_main: common_trace,
49 public_values: vec![],
50 }
51 }
52}
53
54pub fn compute_exe_commit_from_mem_config<F: PrimeField32>(
67 program_commitment: &[F; CHUNK],
68 exe: &VmExe<F>,
69 memory_config: &MemoryConfig,
70) -> [F; CHUNK] {
71 let hasher = vm_poseidon2_hasher();
72 let memory_dimensions = memory_config.memory_dimensions();
73 let mut memory_image = AddressMap::new(memory_config.addr_spaces.clone());
74 memory_image.set_from_sparse(&exe.init_memory);
75 let init_memory_commit =
76 MerkleTree::from_memory(&memory_image, &memory_dimensions, &hasher).root();
77 compute_exe_commit(
78 &hasher,
79 program_commitment,
80 &init_memory_commit,
81 F::from_u32(exe.pc_start),
82 )
83}
84
85pub fn compute_exe_commit<F: PrimeField32>(
93 hasher: &Poseidon2Hasher<F>,
94 program_commit: &[F; CHUNK],
95 init_memory_root: &[F; CHUNK],
96 pc_start: F,
97) -> [F; CHUNK] {
98 let mut padded_pc_start = [F::ZERO; CHUNK];
99 padded_pc_start[0] = pc_start;
100 let program_hash = hasher.hash(program_commit);
101 let memory_hash = hasher.hash(init_memory_root);
102 let pc_hash = hasher.hash(&padded_pc_start);
103 hasher.compress(&hasher.compress(&program_hash, &memory_hash), &pc_hash)
104}
105
106pub(crate) fn generate_cached_trace<F: Field>(program: &Program<F>) -> RowMajorMatrix<F> {
107 let width = ProgramExecutionCols::<F>::width();
108 let mut instructions = program
109 .enumerate_by_pc()
110 .into_iter()
111 .map(|(pc, instruction, _)| (pc, instruction))
112 .collect_vec();
113
114 let padding = padding_instruction();
115 while !instructions.len().is_power_of_two() {
116 instructions.push((
117 program.pc_base + instructions.len() as u32 * DEFAULT_PC_STEP,
118 padding.clone(),
119 ));
120 }
121
122 let mut rows = F::zero_vec(instructions.len() * width);
123 rows.par_chunks_mut(width)
124 .zip(instructions)
125 .for_each(|(row, (pc, instruction))| {
126 let row: &mut ProgramExecutionCols<F> = row.borrow_mut();
127 *row = ProgramExecutionCols {
128 pc: F::from_u32(pc),
129 opcode: instruction.opcode.to_field(),
130 a: instruction.a,
131 b: instruction.b,
132 c: instruction.c,
133 d: instruction.d,
134 e: instruction.e,
135 f: instruction.f,
136 g: instruction.g,
137 };
138 });
139
140 RowMajorMatrix::new(rows, width)
141}
142
143pub(super) fn padding_instruction<F: Field>() -> Instruction<F> {
144 Instruction::from_usize(
145 SystemOpcode::TERMINATE.global_opcode(),
146 [0, 0, EXIT_CODE_FAIL],
147 )
148}