miden_processor/trace/chiplets/mod.rs
1use alloc::vec::Vec;
2
3use miden_air::trace::{CHIPLETS_WIDTH, chiplets::hasher::HasherState};
4use miden_core::{mast::OpBatch, precompile::PrecompileTranscriptState, program::Kernel};
5
6use crate::{
7 Felt, ONE, Word, ZERO,
8 crypto::merkle::MerklePath,
9 trace::{RowIndex, TraceFragment, range::RangeChecker},
10};
11
12mod bitwise;
13use bitwise::Bitwise;
14
15mod hasher;
16use hasher::Hasher;
17#[cfg(test)]
18pub(crate) use hasher::init_state_from_words;
19
20mod memory;
21use memory::Memory;
22
23mod ace;
24use ace::AceHints;
25pub use ace::{Ace, CircuitEvaluation, MAX_NUM_ACE_WIRES, PTR_OFFSET_ELEM, PTR_OFFSET_WORD};
26
27mod kernel_rom;
28use kernel_rom::KernelRom;
29
30mod aux_trace;
31
32pub use aux_trace::AuxTraceBuilder;
33
34#[cfg(test)]
35mod tests;
36
37// TRACE
38// ================================================================================================
39
40pub struct ChipletsTrace {
41 pub(crate) trace: [Vec<Felt>; CHIPLETS_WIDTH],
42 pub(crate) aux_builder: AuxTraceBuilder,
43}
44
45// CHIPLETS MODULE OF HASHER, BITWISE, MEMORY, ACE, AND KERNEL ROM CHIPLETS
46// ================================================================================================
47
48/// This module manages the VM's hasher, bitwise, memory, arithmetic circuit evaluation (ACE)
49/// and kernel ROM chiplets and is responsible for building a final execution trace from their
50/// stacked execution traces and chiplet selectors.
51///
52/// The module's trace can be thought of as 6 stacked segments in the following form:
53///
54/// * Hasher segment: contains the trace and selector for the hasher chiplet. This segment fills the
55/// first rows of the trace up to the length of the hasher `trace_len`.
56/// - column 0: selector column with values set to ZERO
57/// - columns 1-16: execution trace of hash chiplet
58/// - columns 17-20: unused columns padded with ZERO
59///
60/// * Bitwise segment: contains the trace and selectors for the bitwise chiplet. This segment begins
61/// at the end of the hasher segment and fills the next rows of the trace for the `trace_len` of
62/// the bitwise chiplet.
63/// - column 0: selector column with values set to ONE
64/// - column 1: selector column with values set to ZERO
65/// - columns 2-14: execution trace of bitwise chiplet
66/// - columns 15-20: unused columns padded with ZERO
67///
68/// * Memory segment: contains the trace and selectors for the memory chiplet. This segment begins
69/// at the end of the bitwise segment and fills the next rows of the trace for the `trace_len` of
70/// the memory chiplet.
71/// - column 0-1: selector columns with values set to ONE
72/// - column 2: selector column with values set to ZERO
73/// - columns 3-17: execution trace of memory chiplet
74/// - columns 18-20: unused columns padded with ZERO
75///
76/// * ACE segment: contains the trace and selectors for the arithmetic circuit evaluation chiplet.
77/// This segment begins at the end of the memory segment and fills the next rows of the trace for
78/// the `trace_len` of the ACE chiplet.
79/// - column 0-2: selector columns with values set to ONE
80/// - column 3: selector column with values set to ZERO
81/// - columns 4-20: execution trace of ACE chiplet
82///
83/// * Kernel ROM segment: contains the trace and selectors for the kernel ROM chiplet * This segment
84/// begins at the end of the memory segment and fills the next rows of the trace for the
85/// `trace_len` of the kernel ROM chiplet.
86/// - column 0-3: selector columns with values set to ONE
87/// - column 4: selector column with values set to ZERO
88/// - columns 5-9: execution trace of kernel ROM chiplet
89/// - columns 10-20: unused column padded with ZERO
90///
91/// * Padding segment: unused. This segment begins at the end of the kernel ROM segment and fills
92/// the rest of the execution trace minus the number of random rows. When it finishes, the
93/// execution trace should have exactly enough rows remaining for the specified number of random
94/// rows.
95/// - columns 0-4: selector columns with values set to ONE
96/// - columns 5-20: unused columns padded with ZERO
97///
98///
99/// The following is a pictorial representation of the chiplet module:
100///
101/// ```text
102/// +---+--------------------------------------------------------------+------+
103/// | 0 | |------|
104/// | . | Hash chiplet |------|
105/// | . | 16 columns |------|
106/// | . | constraint degree 8 |------|
107/// | 0 | |------|
108/// +---+---+------------------------------------------------------+---+------+
109/// | 1 | 0 | |----------|
110/// | . | . | Bitwise chiplet |----------|
111/// | . | . | 13 columns |----------|
112/// | . | . | constraint degree 5 |----------|
113/// | . | . | |----------|
114/// | . | 0 | |----------|
115/// | . +---+---+--------------------------------------------------+-----+----+
116/// | . | 1 | 0 | |----|
117/// | . | . | . | Memory chiplet |----|
118/// | . | . | . | 15 columns |----|
119/// | . | . | . | constraint degree 9 |----|
120/// | . | . | 0 | |----|
121/// | . + . +---+---+----------------------------------------------------+----+
122/// | . | . | 1 | 0 | |
123/// | . | . | . | . | ACE chiplet |
124/// | . | . | . | . | 16 columns |
125/// | . | . | . | . | constraint degree 5 |
126/// | . | . | . | 0 | |
127/// | . + . | . +---+---+---------------------------+-------------------------+
128/// | . | . | . | 1 | 0 | |-------------------------|
129/// | . | . | . | . | . | Kernel ROM chiplet |-------------------------|
130/// | . | . | . | . | . | 5 columns |-------------------------|
131/// | . | . | . | . | . | constraint degree 9 |-------------------------|
132/// | . | . | . | . | 0 | |-------------------------|
133/// | . + . | . | . +---+---+-----------------------+-------------------------+
134/// | . | . | . | . | 1 | 0 |-------------------------------------------------|
135/// | . | . | . | . | . | . |-------------------------------------------------|
136/// | . | . | . | . | . | . |-------------------------------------------------|
137/// | . | . | . | . | . | . |-------------------------------------------------|
138/// | . | . | . | . | . | . |-------------------- Padding --------------------|
139/// | . + . | . | . | . | . |-------------------------------------------------|
140/// | . | . | . | . | . | . |-------------------------------------------------|
141/// | . | . | . | . | . | . |-------------------------------------------------|
142/// | . | . | . | . | . | . |-------------------------------------------------|
143/// | 1 | 1 | 1 | 1 | 1 | 0 |-------------------------------------------------|
144/// +---+---+---+---+---------------------------------------------------------+
145/// ```
146#[derive(Debug)]
147pub struct Chiplets {
148 pub hasher: Hasher,
149 pub bitwise: Bitwise,
150 pub memory: Memory,
151 pub ace: Ace,
152 pub kernel_rom: KernelRom,
153}
154
155impl Chiplets {
156 // CONSTRUCTOR
157 // --------------------------------------------------------------------------------------------
158 /// Returns a new [Chiplets] component instantiated with the provided Kernel.
159 pub fn new(kernel: Kernel) -> Self {
160 Self {
161 hasher: Hasher::default(),
162 bitwise: Bitwise::default(),
163 memory: Memory::default(),
164 kernel_rom: KernelRom::new(kernel),
165 ace: Ace::default(),
166 }
167 }
168
169 // PUBLIC ACCESSORS
170 // --------------------------------------------------------------------------------------------
171
172 /// Returns the length of the trace required to accommodate chiplet components and 1
173 /// mandatory padding row required for ensuring sufficient trace length for auxiliary connector
174 /// columns that rely on the memory chiplet.
175 pub fn trace_len(&self) -> usize {
176 self.hasher.trace_len()
177 + self.bitwise.trace_len()
178 + self.memory.trace_len()
179 + self.kernel_rom.trace_len()
180 + self.ace.trace_len()
181 + 1
182 }
183
184 /// Returns the index of the first row of `Bitwise` execution trace.
185 pub fn bitwise_start(&self) -> RowIndex {
186 self.hasher.trace_len().into()
187 }
188
189 /// Returns the index of the first row of the `Memory` execution trace.
190 pub fn memory_start(&self) -> RowIndex {
191 self.bitwise_start() + self.bitwise.trace_len()
192 }
193
194 /// Returns the index of the first row of `KernelRom` execution trace.
195 pub fn ace_start(&self) -> RowIndex {
196 self.memory_start() + self.memory.trace_len()
197 }
198
199 /// Returns the index of the first row of `KernelRom` execution trace.
200 pub fn kernel_rom_start(&self) -> RowIndex {
201 self.ace_start() + self.ace.trace_len()
202 }
203
204 /// Returns the index of the first row of the padding section of the execution trace.
205 pub fn padding_start(&self) -> RowIndex {
206 self.kernel_rom_start() + self.kernel_rom.trace_len()
207 }
208
209 // EXECUTION TRACE
210 // --------------------------------------------------------------------------------------------
211
212 /// Adds all range checks required by the memory chiplet to the provided `RangeChecker``
213 /// instance.
214 pub fn append_range_checks(&self, range_checker: &mut RangeChecker) {
215 self.memory.append_range_checks(self.memory_start(), range_checker);
216 }
217
218 /// Returns an execution trace of the chiplets containing the stacked traces of the
219 /// Hasher, Bitwise, ACE, Memory chiplets, and kernel ROM chiplet.
220 pub fn into_trace(
221 self,
222 trace_len: usize,
223 pc_transcript_state: PrecompileTranscriptState,
224 ) -> ChipletsTrace {
225 assert!(self.trace_len() <= trace_len, "target trace length too small");
226
227 let kernel = self.kernel_rom.kernel().clone();
228
229 // Allocate columns for the trace of the chiplets.
230 let mut trace = (0..CHIPLETS_WIDTH)
231 .map(|_| vec![Felt::ZERO; trace_len])
232 .collect::<Vec<_>>()
233 .try_into()
234 .expect("failed to convert vector to array");
235 let ace_hint = self.fill_trace(&mut trace);
236
237 ChipletsTrace {
238 trace,
239 aux_builder: AuxTraceBuilder::new(kernel, ace_hint, pc_transcript_state),
240 }
241 }
242
243 // HELPER METHODS
244 // --------------------------------------------------------------------------------------------
245
246 /// Fills the provided trace for the chiplets module with the stacked execution traces of the
247 /// Hasher, Bitwise, Memory, ACE, and kernel ROM chiplets along with selector columns
248 /// to identify each individual chiplet trace in addition to padding to fill the rest of
249 /// the trace.
250 fn fill_trace(self, trace: &mut [Vec<Felt>; CHIPLETS_WIDTH]) -> AceHints {
251 // get the rows where:usize chiplets begin.
252 let bitwise_start: usize = self.bitwise_start().into();
253 let memory_start: usize = self.memory_start().into();
254 let ace_start: usize = self.ace_start().into();
255 let kernel_rom_start: usize = self.kernel_rom_start().into();
256 let padding_start: usize = self.padding_start().into();
257
258 let Chiplets { hasher, bitwise, memory, kernel_rom, ace } = self;
259
260 // populate external selector columns for all chiplets
261 trace[0][bitwise_start..].fill(ONE);
262 trace[1][memory_start..].fill(ONE);
263 trace[2][ace_start..].fill(ONE);
264 trace[3][kernel_rom_start..].fill(ONE);
265 trace[4][padding_start..].fill(ONE);
266
267 // allocate fragments to be filled with the respective execution traces of each chiplet
268 let mut hasher_fragment = TraceFragment::new(CHIPLETS_WIDTH, hasher.trace_len());
269 let mut bitwise_fragment = TraceFragment::new(CHIPLETS_WIDTH, bitwise.trace_len());
270 let mut memory_fragment = TraceFragment::new(CHIPLETS_WIDTH, memory.trace_len());
271 let mut ace_fragment = TraceFragment::new(CHIPLETS_WIDTH, ace.trace_len());
272 let mut kernel_rom_fragment = TraceFragment::new(CHIPLETS_WIDTH, kernel_rom.trace_len());
273
274 // add the hasher, bitwise, memory, ACE, and kernel ROM segments to their respective
275 // fragments so they can be filled with the chiplet traces
276 for (column_num, column) in trace.iter_mut().enumerate().skip(1) {
277 match column_num {
278 1 => {
279 // column 1 is relevant only for the hasher
280 hasher_fragment.push_column_slice(column);
281 },
282 2 => {
283 // column 2 is relevant to the hasher and to bitwise chiplet
284 let rest = hasher_fragment.push_column_slice(column);
285 bitwise_fragment.push_column_slice(rest);
286 },
287 3 => {
288 // column 3 is relevant for hasher, bitwise, and memory chiplets
289 let rest = hasher_fragment.push_column_slice(column);
290 let rest = bitwise_fragment.push_column_slice(rest);
291 memory_fragment.push_column_slice(rest);
292 },
293 4 | 10..=14 => {
294 // columns 4 - 10 to 14 are relevant for hasher, bitwise, memory chiplets and
295 // ace chiplet
296 let rest = hasher_fragment.push_column_slice(column);
297 let rest = bitwise_fragment.push_column_slice(rest);
298 let rest = memory_fragment.push_column_slice(rest);
299 ace_fragment.push_column_slice(rest);
300 },
301 5..=9 => {
302 // columns 5 - 9 are relevant to all chiplets
303 let rest = hasher_fragment.push_column_slice(column);
304 let rest = bitwise_fragment.push_column_slice(rest);
305 let rest = memory_fragment.push_column_slice(rest);
306 let rest = ace_fragment.push_column_slice(rest);
307 kernel_rom_fragment.push_column_slice(rest);
308 },
309 15 | 16 => {
310 // columns 15 and 16 are relevant only for the hasher, memory and ace chiplets
311 let rest = hasher_fragment.push_column_slice(column);
312 // skip bitwise chiplet
313 let (_, rest) = rest.split_at_mut(bitwise.trace_len());
314 let rest = memory_fragment.push_column_slice(rest);
315 ace_fragment.push_column_slice(rest);
316 },
317 17 => {
318 // column 17 is relevant only for the memory chiplet
319 // skip the hasher and bitwise chiplets
320 let (_, rest) = column.split_at_mut(hasher.trace_len() + bitwise.trace_len());
321 let rest = memory_fragment.push_column_slice(rest);
322 ace_fragment.push_column_slice(rest);
323 },
324 18 | 19 => {
325 // column 18 and 19 are relevant only for the ACE chiplet
326 // skip the hasher, bitwise and memory chiplets
327 let (_, rest) = column.split_at_mut(
328 hasher.trace_len() + bitwise.trace_len() + memory.trace_len(),
329 );
330 ace_fragment.push_column_slice(rest);
331 },
332 _ => panic!("invalid column index"),
333 }
334 }
335
336 // fill the fragments with the execution trace from each chiplet in parallel
337 // The chiplets are independent and can be processed concurrently
338
339 // Fill independent chiplets in parallel: hasher, bitwise, memory, kernel_rom
340 // Note: ACE must be processed separately since it returns a value
341 // Use ThreadPool::install() to prevent nested parallelism from column operations
342 rayon::scope(|s| {
343 s.spawn(move |_| {
344 hasher.fill_trace(&mut hasher_fragment);
345 });
346 s.spawn(move |_| {
347 bitwise.fill_trace(&mut bitwise_fragment);
348 });
349 s.spawn(move |_| {
350 memory.fill_trace(&mut memory_fragment);
351 });
352 s.spawn(move |_| {
353 kernel_rom.fill_trace(&mut kernel_rom_fragment);
354 });
355 });
356
357 // Process ACE chiplet separately as it returns ace_sections
358 let ace_sections = ace.fill_trace(&mut ace_fragment);
359 AceHints::new(ace_start, ace_sections)
360 }
361}
362
363// HELPER STRUCTS
364// ================================================================================================
365
366/// Result of a Merkle tree node update. The result contains the old Merkle_root, which
367/// corresponding to the old_value, and the new merkle_root, for the updated value. As well as the
368/// row address of the execution trace at which the computation started.
369#[derive(Debug, Copy, Clone)]
370pub struct MerkleRootUpdate {
371 address: Felt,
372 old_root: Word,
373 new_root: Word,
374}
375
376impl MerkleRootUpdate {
377 pub fn get_address(&self) -> Felt {
378 self.address
379 }
380 pub fn get_old_root(&self) -> Word {
381 self.old_root
382 }
383 pub fn get_new_root(&self) -> Word {
384 self.new_root
385 }
386}