1use std::sync::Arc;
23use derive_new::new;
4use openvm_circuit::system::memory::MemoryTraceHeights;
5use openvm_instructions::program::DEFAULT_MAX_NUM_PUBLIC_VALUES;
6use openvm_poseidon2_air::Poseidon2Config;
7use openvm_stark_backend::{p3_field::PrimeField32, ChipUsageGetter};
8use serde::{de::DeserializeOwned, Deserialize, Serialize};
910use super::{
11 segment::DefaultSegmentationStrategy, AnyEnum, InstructionExecutor, SegmentationStrategy,
12 SystemComplex, SystemExecutor, SystemPeriphery, VmChipComplex, VmInventoryError,
13 PUBLIC_VALUES_AIR_ID,
14};
15use crate::system::memory::BOUNDARY_AIR_OFFSET;
1617// sbox is decomposed to have this max degree for Poseidon2. We set to 3 so quotient_degree = 2
18// allows log_blowup = 1
19const DEFAULT_POSEIDON2_MAX_CONSTRAINT_DEGREE: usize = 3;
20/// Width of Poseidon2 VM uses.
21pub const POSEIDON2_WIDTH: usize = 16;
22/// Returns a Poseidon2 config for the VM.
23pub fn vm_poseidon2_config<F: PrimeField32>() -> Poseidon2Config<F> {
24 Poseidon2Config::default()
25}
2627pub trait VmConfig<F: PrimeField32>: Clone + Serialize + DeserializeOwned {
28type Executor: InstructionExecutor<F> + AnyEnum + ChipUsageGetter;
29type Periphery: AnyEnum + ChipUsageGetter;
3031/// Must contain system config
32fn system(&self) -> &SystemConfig;
33fn system_mut(&mut self) -> &mut SystemConfig;
3435fn create_chip_complex(
36&self,
37 ) -> Result<VmChipComplex<F, Self::Executor, Self::Periphery>, VmInventoryError>;
38}
3940#[derive(Debug, Serialize, Deserialize, Clone, new, Copy)]
41pub struct MemoryConfig {
42/// The maximum height of the address space. This means the trie has `as_height` layers for searching the address space. The allowed address spaces are those in the range `[as_offset, as_offset + 2^as_height)` where `as_offset` is currently fixed to `1` to not allow address space `0` in memory.
43pub as_height: usize,
44/// The offset of the address space. Should be fixed to equal `1`.
45pub as_offset: u32,
46pub pointer_max_bits: usize,
47/// All timestamps must be in the range `[0, 2^clk_max_bits)`. Maximum allowed: 29.
48pub clk_max_bits: usize,
49/// Limb size used by the range checker
50pub decomp: usize,
51/// Maximum N AccessAdapter AIR to support.
52pub max_access_adapter_n: usize,
53/// An expected upper bound on the number of memory accesses.
54pub access_capacity: usize,
55}
5657impl Default for MemoryConfig {
58fn default() -> Self {
59Self::new(3, 1, 29, 29, 17, 32, 1 << 24)
60 }
61}
6263/// System-level configuration for the virtual machine. Contains all configuration parameters that
64/// are managed by the architecture, including configuration for continuations support.
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct SystemConfig {
67/// The maximum constraint degree any chip is allowed to use.
68pub max_constraint_degree: usize,
69/// True if the VM is in continuation mode. In this mode, an execution could be segmented and
70 /// each segment is proved by a proof. Each proof commits the before and after state of the
71 /// corresponding segment.
72 /// False if the VM is in single segment mode. In this mode, an execution is proved by a single
73 /// proof.
74pub continuation_enabled: bool,
75/// Memory configuration
76pub memory_config: MemoryConfig,
77/// `num_public_values` has different meanings in single segment mode and continuation mode.
78 /// In single segment mode, `num_public_values` is the number of public values of
79 /// `PublicValuesChip`. In this case, verifier can read public values directly.
80 /// In continuation mode, public values are stored in a special address space.
81 /// `num_public_values` indicates the number of allowed addresses in that address space. The verifier
82 /// cannot read public values directly, but they can decommit the public values from the memory
83 /// merkle root.
84pub num_public_values: usize,
85/// Whether to collect detailed profiling metrics.
86 /// **Warning**: this slows down the runtime.
87pub profiling: bool,
88/// Segmentation strategy
89 /// This field is skipped in serde as it's only used in execution and
90 /// not needed after any serialize/deserialize.
91#[serde(skip, default = "get_default_segmentation_strategy")]
92pub segmentation_strategy: Arc<dyn SegmentationStrategy>,
93}
9495pub fn get_default_segmentation_strategy() -> Arc<DefaultSegmentationStrategy> {
96 Arc::new(DefaultSegmentationStrategy::default())
97}
9899#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
100pub struct SystemTraceHeights {
101pub memory: MemoryTraceHeights,
102// All other chips have constant heights.
103}
104105impl SystemConfig {
106pub fn new(
107 max_constraint_degree: usize,
108 memory_config: MemoryConfig,
109 num_public_values: usize,
110 ) -> Self {
111let segmentation_strategy = get_default_segmentation_strategy();
112assert!(
113 memory_config.clk_max_bits <= 29,
114"Timestamp max bits must be <= 29 for LessThan to work in 31-bit field"
115);
116Self {
117 max_constraint_degree,
118 continuation_enabled: false,
119 memory_config,
120 num_public_values,
121 segmentation_strategy,
122 profiling: false,
123 }
124 }
125126pub fn with_max_constraint_degree(mut self, max_constraint_degree: usize) -> Self {
127self.max_constraint_degree = max_constraint_degree;
128self
129}
130131pub fn with_continuations(mut self) -> Self {
132self.continuation_enabled = true;
133self
134}
135136pub fn without_continuations(mut self) -> Self {
137self.continuation_enabled = false;
138self
139}
140141pub fn with_public_values(mut self, num_public_values: usize) -> Self {
142self.num_public_values = num_public_values;
143self
144}
145146pub fn with_max_segment_len(mut self, max_segment_len: usize) -> Self {
147self.segmentation_strategy = Arc::new(
148 DefaultSegmentationStrategy::new_with_max_segment_len(max_segment_len),
149 );
150self
151}
152153pub fn set_segmentation_strategy(&mut self, strategy: Arc<dyn SegmentationStrategy>) {
154self.segmentation_strategy = strategy;
155 }
156157pub fn with_profiling(mut self) -> Self {
158self.profiling = true;
159self
160}
161162pub fn without_profiling(mut self) -> Self {
163self.profiling = false;
164self
165}
166167pub fn has_public_values_chip(&self) -> bool {
168 !self.continuation_enabled && self.num_public_values > 0
169}
170171/// Returns the AIR ID of the memory boundary AIR. Panic if the boundary AIR is not enabled.
172pub fn memory_boundary_air_id(&self) -> usize {
173let mut ret = PUBLIC_VALUES_AIR_ID;
174if self.has_public_values_chip() {
175 ret += 1;
176 }
177 ret += BOUNDARY_AIR_OFFSET;
178 ret
179 }
180}
181182impl Default for SystemConfig {
183fn default() -> Self {
184Self::new(
185 DEFAULT_POSEIDON2_MAX_CONSTRAINT_DEGREE,
186 Default::default(),
187 DEFAULT_MAX_NUM_PUBLIC_VALUES,
188 )
189 }
190}
191192impl SystemTraceHeights {
193/// Round all trace heights to the next power of two. This will round trace heights of 0 to 1.
194pub fn round_to_next_power_of_two(&mut self) {
195self.memory.round_to_next_power_of_two();
196 }
197198/// Round all trace heights to the next power of two, except 0 stays 0.
199pub fn round_to_next_power_of_two_or_zero(&mut self) {
200self.memory.round_to_next_power_of_two_or_zero();
201 }
202}
203204impl<F: PrimeField32> VmConfig<F> for SystemConfig {
205type Executor = SystemExecutor<F>;
206type Periphery = SystemPeriphery<F>;
207208fn system(&self) -> &SystemConfig {
209self
210}
211fn system_mut(&mut self) -> &mut SystemConfig {
212self
213}
214215fn create_chip_complex(
216&self,
217 ) -> Result<VmChipComplex<F, Self::Executor, Self::Periphery>, VmInventoryError> {
218let complex = SystemComplex::new(self.clone());
219Ok(complex)
220 }
221}