use alloc::vec::Vec;
use miden_air::trace::{
CHIPLETS_WIDTH,
chiplets::{
KERNEL_ROM_TRACE_WIDTH,
ace::ACE_CHIPLET_NUM_COLS,
bitwise::TRACE_WIDTH as BITWISE_WIDTH,
hasher::{HasherState, TRACE_WIDTH as HASHER_WIDTH},
memory::TRACE_WIDTH as MEMORY_WIDTH,
},
poseidon2_permutation::NUM_POSEIDON2_PERMUTATION_COLS,
};
use miden_core::{field::PrimeCharacteristicRing, program::KernelDescriptor};
use crate::{
Felt, ONE, Word, ZERO,
crypto::merkle::MerklePath,
trace::{ChipletTraceFragment, RowIndex, range::RangeChecker},
};
mod bitwise;
pub(crate) use bitwise::Bitwise;
mod hasher;
pub(crate) use hasher::Hasher;
mod memory;
pub(crate) use memory::Memory;
mod ace;
pub use ace::{Ace, CircuitEvaluation, MAX_NUM_ACE_WIRES, PTR_OFFSET_ELEM, PTR_OFFSET_WORD};
mod kernel_rom;
pub(crate) use kernel_rom::KernelRom;
#[cfg(test)]
mod tests;
pub struct ChipletsTrace {
pub(crate) trace: Vec<Felt>,
}
pub struct Poseidon2PermutationTrace {
pub(crate) trace: Vec<Felt>,
}
#[derive(Debug)]
pub struct Chiplets {
pub hasher: Hasher,
pub bitwise: Bitwise,
pub memory: Memory,
pub ace: Ace,
pub kernel_rom: KernelRom,
}
impl Chiplets {
pub fn trace_len(&self) -> usize {
self.hasher.trace_len()
+ self.bitwise.trace_len()
+ self.memory.trace_len()
+ self.ace.trace_len()
+ self.kernel_rom.trace_len()
+ 1
}
pub fn poseidon2_permutation_trace_len(&self) -> usize {
self.hasher.poseidon2_permutation_trace_len()
}
pub fn bitwise_start(&self) -> RowIndex {
self.hasher.trace_len().into()
}
pub fn memory_start(&self) -> RowIndex {
self.bitwise_start() + self.bitwise.trace_len()
}
pub fn ace_start(&self) -> RowIndex {
self.memory_start() + self.memory.trace_len()
}
pub fn kernel_rom_start(&self) -> RowIndex {
self.ace_start() + self.ace.trace_len()
}
pub fn padding_start(&self) -> RowIndex {
self.kernel_rom_start() + self.kernel_rom.trace_len()
}
pub fn append_range_checks(&self, range_checker: &mut RangeChecker) {
self.memory.append_range_checks(self.memory_start(), range_checker);
}
pub fn into_traces(
self,
trace_len: usize,
poseidon2_trace_len: usize,
) -> (ChipletsTrace, Poseidon2PermutationTrace) {
assert!(self.trace_len() <= trace_len, "target trace length too small");
assert!(
self.poseidon2_permutation_trace_len() <= poseidon2_trace_len,
"target Poseidon2 trace length too small"
);
let mut trace = vec![Felt::ZERO; CHIPLETS_WIDTH * trace_len];
let mut poseidon2_trace =
Felt::zero_vec(NUM_POSEIDON2_PERMUTATION_COLS * poseidon2_trace_len);
self.fill_trace(&mut trace, trace_len, &mut poseidon2_trace);
(ChipletsTrace { trace }, Poseidon2PermutationTrace { trace: poseidon2_trace })
}
fn fill_trace(self, trace: &mut [Felt], trace_len: usize, poseidon2_trace: &mut [Felt]) {
const W: usize = CHIPLETS_WIDTH;
debug_assert_eq!(trace.len(), W * trace_len);
let memory_start: usize = self.memory_start().into();
let ace_start: usize = self.ace_start().into();
let kernel_rom_start: usize = self.kernel_rom_start().into();
let padding_start: usize = self.padding_start().into();
let Chiplets { hasher, bitwise, memory, ace, kernel_rom } = self;
let hasher_len = hasher.trace_len();
let bitwise_len = bitwise.trace_len();
let memory_len = memory.trace_len();
let ace_len = ace.trace_len();
let kernel_rom_len = kernel_rom.trace_len();
const _: () = assert!(1 + HASHER_WIDTH == CHIPLETS_WIDTH - 1);
let (hasher_band, rest) = trace.split_at_mut(hasher_len * W);
let (bitwise_band, rest) = rest.split_at_mut(bitwise_len * W);
let (memory_band, rest) = rest.split_at_mut(memory_len * W);
let (ace_band, rest) = rest.split_at_mut(ace_len * W);
let (kernel_band, padding_band) = rest.split_at_mut(kernel_rom_len * W);
let mut hasher_fragment =
ChipletTraceFragment::with_overheads(hasher_band, W, 1, HASHER_WIDTH, 0, &[]);
let mut bitwise_fragment = ChipletTraceFragment::with_overheads(
bitwise_band,
W,
2,
BITWISE_WIDTH,
hasher_len,
&[0],
);
let mut memory_fragment = ChipletTraceFragment::with_overheads(
memory_band,
W,
3,
MEMORY_WIDTH,
memory_start,
&[0, 1],
);
let mut ace_fragment = ChipletTraceFragment::with_overheads(
ace_band,
W,
4,
ACE_CHIPLET_NUM_COLS,
ace_start,
&[0, 1, 2],
);
let mut kernel_rom_fragment = ChipletTraceFragment::with_overheads(
kernel_band,
W,
5,
KERNEL_ROM_TRACE_WIDTH,
kernel_rom_start,
&[0, 1, 2, 3],
);
rayon::scope(|s| {
s.spawn(move |_| {
hasher.fill_trace(&mut hasher_fragment, poseidon2_trace);
});
s.spawn(move |_| {
bitwise.fill_trace(&mut bitwise_fragment);
});
s.spawn(move |_| {
memory.fill_trace(&mut memory_fragment);
});
s.spawn(move |_| {
ace.fill_trace(&mut ace_fragment);
});
s.spawn(move |_| {
kernel_rom.fill_trace(&mut kernel_rom_fragment);
});
s.spawn(move |_| {
fill_padding_rows(padding_band, padding_start);
});
});
}
}
fn fill_padding_rows(band: &mut [Felt], row_offset: usize) {
const W: usize = CHIPLETS_WIDTH;
let (rows, _) = band.as_chunks_mut::<W>();
for (i, row) in rows.iter_mut().enumerate() {
row[..5].fill(ONE);
row[W - 1] = Felt::from_u32((row_offset + i + 1) as u32);
}
}
#[derive(Debug, Copy, Clone)]
pub struct MerkleRootUpdate {
address: Felt,
old_root: Word,
new_root: Word,
}
impl MerkleRootUpdate {
pub fn get_address(&self) -> Felt {
self.address
}
pub fn get_old_root(&self) -> Word {
self.old_root
}
pub fn get_new_root(&self) -> Word {
self.new_root
}
}