1use std::{
2 fs::File,
3 io::{self, Write},
4 path::Path,
5};
6
7use derive_new::new;
8use getset::{Setters, WithSetters};
9use openvm_instructions::{
10 riscv::{RV32_IMM_AS, RV32_MEMORY_AS, RV32_REGISTER_AS},
11 DEFERRAL_AS,
12};
13use openvm_poseidon2_air::Poseidon2Config;
14use openvm_stark_backend::{
15 p3_field::Field, EngineDeviceCtx, StarkEngine, StarkProtocolConfig, Val,
16};
17use serde::{de::DeserializeOwned, Deserialize, Serialize};
18
19use super::{AnyEnum, VmChipComplex, BOUNDARY_AIR_ID, CONNECTOR_AIR_ID, PROGRAM_AIR_ID};
20use crate::{
21 arch::{
22 execution_mode::metered::segment_ctx::DEFAULT_MAX_MEMORY, AirInventory, AirInventoryError,
23 Arena, ChipInventoryError, ExecutorInventory, ExecutorInventoryError,
24 },
25 system::{
26 memory::{merkle::public_values::PUBLIC_VALUES_AS, num_memory_airs, POINTER_MAX_BITS},
27 SystemChipComplex,
28 },
29};
30
31const DEFAULT_POSEIDON2_MAX_CONSTRAINT_DEGREE: usize = 3;
34pub const DEFAULT_MAX_NUM_PUBLIC_VALUES: usize = 32;
35pub const DEFAULT_DEFERRAL_ADDR_SPACE_CELLS: usize = 1 << 14;
37pub const POSEIDON2_WIDTH: usize = 16;
39pub const ADDR_SPACE_OFFSET: u32 = 1;
41
42fn default_segmentation_max_memory() -> usize {
43 DEFAULT_MAX_MEMORY
44}
45pub fn vm_poseidon2_config<F: Field>() -> Poseidon2Config<F> {
47 Poseidon2Config::default()
48}
49
50pub trait VmConfig<SC>:
62 Clone
63 + Serialize
64 + DeserializeOwned
65 + InitFileGenerator
66 + VmExecutionConfig<Val<SC>>
67 + VmCircuitConfig<SC>
68 + AsRef<SystemConfig>
69 + AsMut<SystemConfig>
70where
71 SC: StarkProtocolConfig,
72{
73}
74
75pub trait VmExecutionConfig<F> {
76 type Executor: AnyEnum;
77
78 fn create_executors(&self)
79 -> Result<ExecutorInventory<Self::Executor>, ExecutorInventoryError>;
80}
81
82pub trait VmCircuitConfig<SC: StarkProtocolConfig> {
83 fn create_airs(&self) -> Result<AirInventory<SC>, AirInventoryError>;
84}
85
86pub trait VmBuilder<E: StarkEngine>: Sized {
89 type VmConfig: VmConfig<E::SC>;
90 type RecordArena: Arena;
91 type SystemChipInventory: SystemChipComplex<Self::RecordArena, E::PB>;
92
93 #[allow(clippy::type_complexity)]
96 fn create_chip_complex(
97 &self,
98 config: &Self::VmConfig,
99 circuit: AirInventory<E::SC>,
100 device_ctx: &EngineDeviceCtx<E>,
101 ) -> Result<
102 VmChipComplex<E::SC, Self::RecordArena, E::PB, Self::SystemChipInventory>,
103 ChipInventoryError,
104 >;
105}
106
107impl<SC, VC> VmConfig<SC> for VC
108where
109 SC: StarkProtocolConfig,
110 VC: Clone
111 + Serialize
112 + DeserializeOwned
113 + InitFileGenerator
114 + VmExecutionConfig<Val<SC>>
115 + VmCircuitConfig<SC>
116 + AsRef<SystemConfig>
117 + AsMut<SystemConfig>,
118{
119}
120
121pub const OPENVM_DEFAULT_INIT_FILE_BASENAME: &str = "openvm_init";
122pub const OPENVM_DEFAULT_INIT_FILE_NAME: &str = "openvm_init.rs";
123pub const DEFAULT_BLOCK_SIZE: usize = 4;
126
127pub trait InitFileGenerator {
131 fn generate_init_file_contents(&self) -> Option<String> {
133 None
134 }
135
136 fn write_to_init_file(
139 &self,
140 manifest_dir: &Path,
141 init_file_name: Option<&str>,
142 ) -> io::Result<()> {
143 if let Some(contents) = self.generate_init_file_contents() {
144 let dest_path = Path::new(manifest_dir)
145 .join(init_file_name.unwrap_or(OPENVM_DEFAULT_INIT_FILE_NAME));
146 let mut f = File::create(&dest_path)?;
147 write!(f, "{contents}")?;
148 }
149 Ok(())
150 }
151}
152
153pub trait AddressSpaceHostLayout {
161 fn size(&self) -> usize;
163
164 unsafe fn to_field<F: Field>(&self, value: &[u8]) -> F;
169}
170
171#[derive(Debug, Serialize, Deserialize, Clone, new)]
172pub struct MemoryConfig {
173 pub addr_space_height: usize,
177 pub addr_spaces: Vec<AddressSpaceHostConfig>,
180 pub pointer_max_bits: usize,
181 pub timestamp_max_bits: usize,
183 pub decomp: usize,
185}
186
187impl Default for MemoryConfig {
188 fn default() -> Self {
189 let mut addr_spaces =
190 Self::empty_address_space_configs((1 << 3) + ADDR_SPACE_OFFSET as usize);
191 const MAX_CELLS: usize = 1 << 29;
192 addr_spaces[RV32_REGISTER_AS as usize].num_cells = 32 * size_of::<u32>();
193 addr_spaces[RV32_MEMORY_AS as usize].num_cells = MAX_CELLS;
194 addr_spaces[PUBLIC_VALUES_AS as usize].num_cells = DEFAULT_MAX_NUM_PUBLIC_VALUES;
195 addr_spaces[DEFERRAL_AS as usize].num_cells = DEFAULT_DEFERRAL_ADDR_SPACE_CELLS;
196 Self::new(3, addr_spaces, POINTER_MAX_BITS, 29, 17)
197 }
198}
199
200impl MemoryConfig {
201 pub fn empty_address_space_configs(num_addr_spaces: usize) -> Vec<AddressSpaceHostConfig> {
202 let mut addr_spaces =
204 vec![AddressSpaceHostConfig::new(0, MemoryCellType::field32()); num_addr_spaces];
205 addr_spaces[RV32_IMM_AS as usize] = AddressSpaceHostConfig::new(0, MemoryCellType::Null);
206 addr_spaces[RV32_REGISTER_AS as usize] = AddressSpaceHostConfig::new(0, MemoryCellType::U8);
207
208 addr_spaces[RV32_MEMORY_AS as usize] = AddressSpaceHostConfig::new(0, MemoryCellType::U8);
209
210 addr_spaces[PUBLIC_VALUES_AS as usize] = AddressSpaceHostConfig::new(0, MemoryCellType::U8);
211
212 addr_spaces
213 }
214
215 pub fn aggregation() -> Self {
217 let mut addr_spaces =
218 Self::empty_address_space_configs((1 << 3) + ADDR_SPACE_OFFSET as usize);
219 addr_spaces[openvm_instructions::DEFERRAL_AS as usize].num_cells = 1 << 29;
220 Self::new(3, addr_spaces, POINTER_MAX_BITS, 29, 17)
221 }
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize, Setters, WithSetters)]
227pub struct SystemConfig {
228 #[getset(set_with = "pub")]
230 pub max_constraint_degree: usize,
231 pub memory_config: MemoryConfig,
233 pub num_public_values: usize,
236 #[serde(skip, default = "default_segmentation_max_memory")]
240 #[getset(set = "pub")]
241 pub segmentation_max_memory: usize,
242}
243
244impl SystemConfig {
245 pub fn new(
246 max_constraint_degree: usize,
247 mut memory_config: MemoryConfig,
248 num_public_values: usize,
249 ) -> Self {
250 assert!(
251 memory_config.timestamp_max_bits <= 29,
252 "Timestamp max bits must be <= 29 for LessThan to work in 31-bit field"
253 );
254 memory_config.addr_spaces[PUBLIC_VALUES_AS as usize].num_cells = num_public_values;
255 Self {
256 max_constraint_degree,
257 memory_config,
258 num_public_values,
259 segmentation_max_memory: DEFAULT_MAX_MEMORY,
260 }
261 }
262
263 pub fn default_from_memory(memory_config: MemoryConfig) -> Self {
264 Self::new(
265 DEFAULT_POSEIDON2_MAX_CONSTRAINT_DEGREE,
266 memory_config,
267 DEFAULT_MAX_NUM_PUBLIC_VALUES,
268 )
269 }
270
271 pub fn with_public_values(mut self, num_public_values: usize) -> Self {
272 self.num_public_values = num_public_values;
273 self.memory_config.addr_spaces[PUBLIC_VALUES_AS as usize].num_cells = num_public_values;
274 self
275 }
276
277 pub fn memory_boundary_air_id(&self) -> usize {
279 BOUNDARY_AIR_ID
280 }
281
282 pub fn memory_merkle_air_id(&self) -> usize {
284 self.memory_boundary_air_id() + 1
285 }
286
287 pub fn is_required_air_id(&self, air_id: usize) -> bool {
289 air_id == PROGRAM_AIR_ID
290 || air_id == CONNECTOR_AIR_ID
291 || air_id == self.memory_boundary_air_id()
292 || air_id == self.memory_merkle_air_id()
293 }
294
295 pub fn num_airs(&self) -> usize {
298 self.memory_boundary_air_id() + num_memory_airs()
299 }
300}
301
302impl Default for SystemConfig {
303 fn default() -> Self {
304 Self::default_from_memory(MemoryConfig::default())
305 }
306}
307
308impl AsRef<SystemConfig> for SystemConfig {
309 fn as_ref(&self) -> &SystemConfig {
310 self
311 }
312}
313
314impl AsMut<SystemConfig> for SystemConfig {
315 fn as_mut(&mut self) -> &mut SystemConfig {
316 self
317 }
318}
319
320impl InitFileGenerator for SystemConfig {}
322
323#[derive(Debug, Serialize, Deserialize, Clone, Copy, new)]
324pub struct AddressSpaceHostConfig {
325 pub num_cells: usize,
328 pub layout: MemoryCellType,
329}
330
331impl AddressSpaceHostConfig {
332 pub fn size(&self) -> usize {
334 self.num_cells * self.layout.size()
335 }
336}
337
338#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
339pub enum MemoryCellType {
340 Null,
341 U8,
342 U16,
343 U32,
345 F {
347 size: u8,
348 },
349}
350
351impl MemoryCellType {
352 pub fn field32() -> Self {
353 Self::F {
354 size: size_of::<u32>() as u8,
355 }
356 }
357}
358
359impl AddressSpaceHostLayout for MemoryCellType {
360 fn size(&self) -> usize {
361 match self {
362 Self::Null => 1, Self::U8 => size_of::<u8>(),
364 Self::U16 => size_of::<u16>(),
365 Self::U32 => size_of::<u32>(),
366 Self::F { size } => *size as usize,
367 }
368 }
369
370 unsafe fn to_field<F: Field>(&self, value: &[u8]) -> F {
378 match self {
379 Self::Null => unreachable!(),
380 Self::U8 => F::from_u8(*value.get_unchecked(0)),
381 Self::U16 => F::from_u16(core::ptr::read(value.as_ptr() as *const u16)),
382 Self::U32 => F::from_u32(core::ptr::read(value.as_ptr() as *const u32)),
383 Self::F { .. } => core::ptr::read(value.as_ptr() as *const F),
384 }
385 }
386}