Skip to main content

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}