openvm_circuit/system/memory/merkle/
trace.rs1use std::{borrow::BorrowMut, sync::atomic::AtomicU32};
2
3use openvm_cpu_backend::CpuBackend;
4use openvm_stark_backend::{
5 p3_field::PrimeField32, p3_matrix::dense::RowMajorMatrix, prover::AirProvingContext,
6 StarkProtocolConfig, Val,
7};
8use tracing::instrument;
9
10use crate::{
11 arch::{hasher::HasherChip, VmField},
12 system::{
13 memory::{
14 merkle::{tree::MerkleTree, FinalState, MemoryMerkleChip, MemoryMerkleCols},
15 Equipartition, MemoryImage,
16 },
17 poseidon2::{
18 Poseidon2PeripheryBaseChip, Poseidon2PeripheryChip, PERIPHERY_POSEIDON2_WIDTH,
19 },
20 },
21};
22
23impl<const CHUNK: usize, F: PrimeField32> MemoryMerkleChip<CHUNK, F> {
24 #[instrument(name = "merkle_finalize", level = "debug", skip_all)]
25 pub(crate) fn finalize(
26 &mut self,
27 initial_memory: &MemoryImage,
28 final_memory: &Equipartition<F, CHUNK>,
29 hasher: &impl HasherChip<CHUNK, F>,
30 ) {
31 assert!(self.final_state.is_none(), "Merkle chip already finalized");
32 let memory_dimensions = &self.air.memory_dimensions;
33 let mut tree = MerkleTree::from_memory(initial_memory, memory_dimensions, hasher);
34 self.final_state = Some(tree.finalize(hasher, final_memory, memory_dimensions));
35 self.top_tree = tree.top_tree(memory_dimensions.addr_space_height);
36 }
37}
38
39impl<const CHUNK: usize, F> MemoryMerkleChip<CHUNK, F>
40where
41 F: PrimeField32,
42{
43 pub fn generate_proving_ctx<SC>(&mut self) -> AirProvingContext<CpuBackend<SC>>
44 where
45 SC: StarkProtocolConfig<F = F>,
46 {
47 assert!(
48 self.final_state.is_some(),
49 "Merkle chip must finalize before trace generation"
50 );
51 let FinalState {
52 mut rows,
53 init_root,
54 final_root,
55 } = self.final_state.take().unwrap();
56 rows.reverse();
59 rows.swap(0, 1);
60
61 #[cfg(feature = "metrics")]
62 {
63 self.current_height = rows.len();
64 }
65 let width = MemoryMerkleCols::<Val<SC>, CHUNK>::width();
66 let mut height = rows.len().next_power_of_two();
67 if let Some(mut oh) = self.overridden_height {
68 oh = oh.next_power_of_two();
69 assert!(
70 oh >= height,
71 "Overridden height {oh} is less than the required height {height}"
72 );
73 height = oh;
74 }
75 let mut trace = Val::<SC>::zero_vec(width * height);
76
77 for (trace_row, row) in trace.chunks_exact_mut(width).zip(rows) {
78 *trace_row.borrow_mut() = row;
79 }
80
81 let trace = RowMajorMatrix::new(trace, width);
82 let pvs = init_root.into_iter().chain(final_root).collect();
83 AirProvingContext::simple(trace, pvs)
84 }
85}
86pub trait SerialReceiver<T> {
87 fn receive(&self, msg: T);
88}
89
90impl<'a, F: VmField, const SBOX_REGISTERS: usize> SerialReceiver<&'a [F]>
91 for Poseidon2PeripheryBaseChip<F, SBOX_REGISTERS>
92{
93 fn receive(&self, perm_preimage: &'a [F]) {
96 assert!(perm_preimage.len() <= PERIPHERY_POSEIDON2_WIDTH);
97 let mut state = [F::ZERO; PERIPHERY_POSEIDON2_WIDTH];
98 state[..perm_preimage.len()].copy_from_slice(perm_preimage);
99 let count = self.records.entry(state).or_insert(AtomicU32::new(0));
100 count.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
101 self.nonempty
102 .store(true, std::sync::atomic::Ordering::Relaxed);
103 }
104}
105
106impl<'a, F: VmField> SerialReceiver<&'a [F]> for Poseidon2PeripheryChip<F> {
107 fn receive(&self, perm_preimage: &'a [F]) {
108 match self {
109 Poseidon2PeripheryChip::Register0(chip) => chip.receive(perm_preimage),
110 Poseidon2PeripheryChip::Register1(chip) => chip.receive(perm_preimage),
111 }
112 }
113}