use super::{
BTreeMap, ChipletsTrace, Felt, FieldElement, RangeChecker, StarkField, TraceFragment, Vec,
Word, CHIPLETS_WIDTH, ONE, ZERO,
};
use crate::{trace::LookupTableRow, ExecutionError};
use vm_core::{
chiplets::bitwise::{BITWISE_AND_LABEL, BITWISE_XOR_LABEL},
chiplets::{
hasher::{Digest, HasherState},
memory::{MEMORY_READ_LABEL, MEMORY_WRITE_LABEL},
},
code_blocks::OpBatch,
Kernel,
};
mod bitwise;
use bitwise::{Bitwise, BitwiseLookup};
mod hasher;
use hasher::Hasher;
pub use hasher::{AuxTraceBuilder as HasherAuxTraceBuilder, SiblingTableRow};
mod memory;
use memory::{Memory, MemoryLookup};
mod kernel_rom;
use kernel_rom::KernelRom;
mod bus;
pub use bus::{AuxTraceBuilder, ChipletsBus};
#[cfg(test)]
mod tests;
pub struct Chiplets {
clk: u32,
hasher: Hasher,
bitwise: Bitwise,
memory: Memory,
kernel_rom: KernelRom,
bus: ChipletsBus,
}
impl Chiplets {
pub fn new(kernel: &Kernel) -> Self {
Self {
clk: 0,
hasher: Hasher::default(),
bitwise: Bitwise::default(),
memory: Memory::default(),
kernel_rom: KernelRom::new(kernel),
bus: ChipletsBus::default(),
}
}
pub fn trace_len(&self) -> usize {
self.hasher.trace_len()
+ self.bitwise.trace_len()
+ self.memory.trace_len()
+ self.kernel_rom.trace_len()
+ 1
}
pub fn bitwise_start(&self) -> usize {
self.hasher.trace_len()
}
pub fn memory_start(&self) -> usize {
self.bitwise_start() + self.bitwise.trace_len()
}
pub fn kernel_rom_start(&self) -> usize {
self.memory_start() + self.memory.trace_len()
}
pub fn padding_start(&self) -> usize {
self.kernel_rom_start() + self.kernel_rom.trace_len()
}
pub fn permute(&mut self, state: HasherState) -> (Felt, HasherState) {
let mut lookups = Vec::new();
let (addr, return_state) = self.hasher.permute(state, &mut lookups);
self.bus.request_hasher_operation(&lookups, self.clk);
self.bus.provide_hasher_lookups(&lookups);
(addr, return_state)
}
pub fn build_merkle_root(&mut self, value: Word, path: &[Word], index: Felt) -> (Felt, Word) {
let mut lookups = Vec::new();
let (addr, root) = self
.hasher
.build_merkle_root(value, path, index, &mut lookups);
self.bus.request_hasher_operation(&lookups, self.clk);
self.bus.provide_hasher_lookups(&lookups);
(addr, root)
}
pub fn update_merkle_root(
&mut self,
old_value: Word,
new_value: Word,
path: &[Word],
index: Felt,
) -> (Felt, Word, Word) {
let mut lookups = Vec::new();
let (addr, old_root, new_root) =
self.hasher
.update_merkle_root(old_value, new_value, path, index, &mut lookups);
self.bus.request_hasher_operation(&lookups, self.clk);
self.bus.provide_hasher_lookups(&lookups);
(addr, old_root, new_root)
}
pub fn hash_control_block(&mut self, h1: Word, h2: Word, expected_hash: Digest) -> Felt {
let mut lookups = Vec::new();
let (addr, result) = self
.hasher
.hash_control_block(h1, h2, expected_hash, &mut lookups);
debug_assert_eq!(expected_hash, result.into());
self.bus.request_hasher_lookup(lookups[0], self.clk);
self.bus.enqueue_hasher_request(lookups[1]);
self.bus.provide_hasher_lookups(&lookups);
addr
}
pub fn hash_span_block(
&mut self,
op_batches: &[OpBatch],
num_op_groups: usize,
expected_hash: Digest,
) -> Felt {
let mut lookups = Vec::new();
let (addr, result) =
self.hasher
.hash_span_block(op_batches, num_op_groups, expected_hash, &mut lookups);
debug_assert_eq!(expected_hash, result.into());
self.bus.request_hasher_lookup(lookups[0], self.clk);
for lookup in lookups.iter().skip(1).rev() {
self.bus.enqueue_hasher_request(*lookup);
}
self.bus.provide_hasher_lookups(&lookups);
addr
}
pub fn absorb_span_batch(&mut self) {
self.bus.send_queued_hasher_request(self.clk);
}
pub fn read_hash_result(&mut self) {
self.bus.send_queued_hasher_request(self.clk);
}
pub fn u32and(&mut self, a: Felt, b: Felt) -> Result<Felt, ExecutionError> {
let result = self.bitwise.u32and(a, b)?;
let bitwise_lookup = BitwiseLookup::new(BITWISE_AND_LABEL, a, b, result);
self.bus.request_bitwise_operation(bitwise_lookup, self.clk);
Ok(result)
}
pub fn u32xor(&mut self, a: Felt, b: Felt) -> Result<Felt, ExecutionError> {
let result = self.bitwise.u32xor(a, b)?;
let bitwise_lookup = BitwiseLookup::new(BITWISE_XOR_LABEL, a, b, result);
self.bus.request_bitwise_operation(bitwise_lookup, self.clk);
Ok(result)
}
pub fn read_mem(&mut self, ctx: u32, addr: Felt) -> Word {
let value = self.memory.read(ctx, addr, self.clk);
let lookup = MemoryLookup::from_ints(MEMORY_READ_LABEL, ctx, addr, self.clk, value);
self.bus.request_memory_operation(&[lookup], self.clk);
value
}
pub fn read_mem_double(&mut self, ctx: u32, addr: Felt) -> [Word; 2] {
let addr2 = addr + ONE;
let words = [
self.memory.read(ctx, addr, self.clk),
self.memory.read(ctx, addr2, self.clk),
];
let lookups = [
MemoryLookup::from_ints(MEMORY_READ_LABEL, ctx, addr, self.clk, words[0]),
MemoryLookup::from_ints(MEMORY_READ_LABEL, ctx, addr2, self.clk, words[1]),
];
self.bus.request_memory_operation(&lookups, self.clk);
words
}
pub fn write_mem(&mut self, ctx: u32, addr: Felt, word: Word) {
self.memory.write(ctx, addr, self.clk, word);
let lookup = MemoryLookup::from_ints(MEMORY_WRITE_LABEL, ctx, addr, self.clk, word);
self.bus.request_memory_operation(&[lookup], self.clk);
}
pub fn write_mem_element(&mut self, ctx: u32, addr: Felt, value: Felt) -> Word {
let old_word = self.memory.get_old_value(ctx, addr.as_int());
let new_word = [value, old_word[1], old_word[2], old_word[3]];
self.memory.write(ctx, addr, self.clk, new_word);
let lookup = MemoryLookup::from_ints(MEMORY_WRITE_LABEL, ctx, addr, self.clk, new_word);
self.bus.request_memory_operation(&[lookup], self.clk);
old_word
}
pub fn write_mem_double(&mut self, ctx: u32, addr: Felt, words: [Word; 2]) {
let addr2 = addr + ONE;
self.memory.write(ctx, addr, self.clk, words[0]);
self.memory.write(ctx, addr2, self.clk, words[1]);
let lookups = [
MemoryLookup::from_ints(MEMORY_WRITE_LABEL, ctx, addr, self.clk, words[0]),
MemoryLookup::from_ints(MEMORY_WRITE_LABEL, ctx, addr2, self.clk, words[1]),
];
self.bus.request_memory_operation(&lookups, self.clk);
}
pub fn get_mem_value(&self, ctx: u32, addr: u64) -> Option<Word> {
self.memory.get_value(ctx, addr)
}
pub fn get_mem_state_at(&self, ctx: u32, clk: u32) -> Vec<(u64, Word)> {
self.memory.get_state_at(ctx, clk)
}
#[cfg(test)]
pub fn get_mem_size(&self) -> usize {
self.memory.size()
}
pub fn access_kernel_proc(&mut self, proc_hash: Digest) -> Result<(), ExecutionError> {
self.kernel_rom.access_proc(proc_hash)
}
pub fn advance_clock(&mut self) {
self.clk += 1;
}
pub fn append_range_checks(&self, range_checker: &mut RangeChecker) {
self.memory
.append_range_checks(self.memory_start(), range_checker);
}
pub fn into_trace(self, trace_len: usize, num_rand_rows: usize) -> ChipletsTrace {
assert!(
self.trace_len() + num_rand_rows <= trace_len,
"target trace length too small"
);
let mut trace = (0..CHIPLETS_WIDTH)
.map(|_| Felt::zeroed_vector(trace_len))
.collect::<Vec<_>>()
.try_into()
.expect("failed to convert vector to array");
let (hasher_aux_builder, aux_builder) = self.fill_trace(&mut trace);
ChipletsTrace {
trace,
hasher_aux_builder,
aux_builder,
}
}
fn fill_trace(
self,
trace: &mut [Vec<Felt>; CHIPLETS_WIDTH],
) -> (HasherAuxTraceBuilder, AuxTraceBuilder) {
let bitwise_start = self.bitwise_start();
let memory_start = self.memory_start();
let kernel_rom_start = self.kernel_rom_start();
let padding_start = self.padding_start();
let Chiplets {
clk: _,
hasher,
bitwise,
memory,
kernel_rom,
mut bus,
} = self;
trace[0][bitwise_start..].fill(ONE);
trace[1][memory_start..].fill(ONE);
trace[2][kernel_rom_start..].fill(ONE);
trace[3][padding_start..].fill(ONE);
let mut hasher_fragment = TraceFragment::new(CHIPLETS_WIDTH);
let mut bitwise_fragment = TraceFragment::new(CHIPLETS_WIDTH);
let mut memory_fragment = TraceFragment::new(CHIPLETS_WIDTH);
let mut kernel_rom_fragment = TraceFragment::new(CHIPLETS_WIDTH);
for (column_num, column) in trace.iter_mut().enumerate().skip(1) {
match column_num {
1 | 15..=17 => {
hasher_fragment.push_column_slice(column, hasher.trace_len());
}
2 => {
let rest = hasher_fragment.push_column_slice(column, hasher.trace_len());
bitwise_fragment.push_column_slice(rest, bitwise.trace_len());
}
3 | 10..=14 => {
let rest = hasher_fragment.push_column_slice(column, hasher.trace_len());
let rest = bitwise_fragment.push_column_slice(rest, bitwise.trace_len());
memory_fragment.push_column_slice(rest, memory.trace_len());
}
4..=9 => {
let rest = hasher_fragment.push_column_slice(column, hasher.trace_len());
let rest = bitwise_fragment.push_column_slice(rest, bitwise.trace_len());
let rest = memory_fragment.push_column_slice(rest, memory.trace_len());
kernel_rom_fragment.push_column_slice(rest, kernel_rom.trace_len());
}
_ => panic!("invalid column index"),
}
}
let hasher_aux_builder = hasher.fill_trace(&mut hasher_fragment);
bitwise.fill_trace(&mut bitwise_fragment, &mut bus, bitwise_start);
memory.fill_trace(&mut memory_fragment, &mut bus, memory_start);
kernel_rom.fill_trace(&mut kernel_rom_fragment);
(hasher_aux_builder, bus.into_aux_builder())
}
}