openvm_circuit/arch/
config.rs

1use std::sync::Arc;
2
3use 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};
9
10use 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;
16
17// 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}
26
27pub trait VmConfig<F: PrimeField32>: Clone + Serialize + DeserializeOwned {
28    type Executor: InstructionExecutor<F> + AnyEnum + ChipUsageGetter;
29    type Periphery: AnyEnum + ChipUsageGetter;
30
31    /// Must contain system config
32    fn system(&self) -> &SystemConfig;
33    fn system_mut(&mut self) -> &mut SystemConfig;
34
35    fn create_chip_complex(
36        &self,
37    ) -> Result<VmChipComplex<F, Self::Executor, Self::Periphery>, VmInventoryError>;
38}
39
40#[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.
43    pub as_height: usize,
44    /// The offset of the address space. Should be fixed to equal `1`.
45    pub as_offset: u32,
46    pub pointer_max_bits: usize,
47    /// All timestamps must be in the range `[0, 2^clk_max_bits)`. Maximum allowed: 29.
48    pub clk_max_bits: usize,
49    /// Limb size used by the range checker
50    pub decomp: usize,
51    /// Maximum N AccessAdapter AIR to support.
52    pub max_access_adapter_n: usize,
53    /// An expected upper bound on the number of memory accesses.
54    pub access_capacity: usize,
55}
56
57impl Default for MemoryConfig {
58    fn default() -> Self {
59        Self::new(3, 1, 29, 29, 17, 32, 1 << 24)
60    }
61}
62
63/// 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.
68    pub 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.
74    pub continuation_enabled: bool,
75    /// Memory configuration
76    pub 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.
84    pub num_public_values: usize,
85    /// Whether to collect detailed profiling metrics.
86    /// **Warning**: this slows down the runtime.
87    pub 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")]
92    pub segmentation_strategy: Arc<dyn SegmentationStrategy>,
93}
94
95pub fn get_default_segmentation_strategy() -> Arc<DefaultSegmentationStrategy> {
96    Arc::new(DefaultSegmentationStrategy::default())
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
100pub struct SystemTraceHeights {
101    pub memory: MemoryTraceHeights,
102    // All other chips have constant heights.
103}
104
105impl SystemConfig {
106    pub fn new(
107        max_constraint_degree: usize,
108        memory_config: MemoryConfig,
109        num_public_values: usize,
110    ) -> Self {
111        let segmentation_strategy = get_default_segmentation_strategy();
112        assert!(
113            memory_config.clk_max_bits <= 29,
114            "Timestamp max bits must be <= 29 for LessThan to work in 31-bit field"
115        );
116        Self {
117            max_constraint_degree,
118            continuation_enabled: false,
119            memory_config,
120            num_public_values,
121            segmentation_strategy,
122            profiling: false,
123        }
124    }
125
126    pub fn with_max_constraint_degree(mut self, max_constraint_degree: usize) -> Self {
127        self.max_constraint_degree = max_constraint_degree;
128        self
129    }
130
131    pub fn with_continuations(mut self) -> Self {
132        self.continuation_enabled = true;
133        self
134    }
135
136    pub fn without_continuations(mut self) -> Self {
137        self.continuation_enabled = false;
138        self
139    }
140
141    pub fn with_public_values(mut self, num_public_values: usize) -> Self {
142        self.num_public_values = num_public_values;
143        self
144    }
145
146    pub fn with_max_segment_len(mut self, max_segment_len: usize) -> Self {
147        self.segmentation_strategy = Arc::new(
148            DefaultSegmentationStrategy::new_with_max_segment_len(max_segment_len),
149        );
150        self
151    }
152
153    pub fn set_segmentation_strategy(&mut self, strategy: Arc<dyn SegmentationStrategy>) {
154        self.segmentation_strategy = strategy;
155    }
156
157    pub fn with_profiling(mut self) -> Self {
158        self.profiling = true;
159        self
160    }
161
162    pub fn without_profiling(mut self) -> Self {
163        self.profiling = false;
164        self
165    }
166
167    pub fn has_public_values_chip(&self) -> bool {
168        !self.continuation_enabled && self.num_public_values > 0
169    }
170
171    /// Returns the AIR ID of the memory boundary AIR. Panic if the boundary AIR is not enabled.
172    pub fn memory_boundary_air_id(&self) -> usize {
173        let mut ret = PUBLIC_VALUES_AIR_ID;
174        if self.has_public_values_chip() {
175            ret += 1;
176        }
177        ret += BOUNDARY_AIR_OFFSET;
178        ret
179    }
180}
181
182impl Default for SystemConfig {
183    fn default() -> Self {
184        Self::new(
185            DEFAULT_POSEIDON2_MAX_CONSTRAINT_DEGREE,
186            Default::default(),
187            DEFAULT_MAX_NUM_PUBLIC_VALUES,
188        )
189    }
190}
191
192impl SystemTraceHeights {
193    /// Round all trace heights to the next power of two. This will round trace heights of 0 to 1.
194    pub fn round_to_next_power_of_two(&mut self) {
195        self.memory.round_to_next_power_of_two();
196    }
197
198    /// Round all trace heights to the next power of two, except 0 stays 0.
199    pub fn round_to_next_power_of_two_or_zero(&mut self) {
200        self.memory.round_to_next_power_of_two_or_zero();
201    }
202}
203
204impl<F: PrimeField32> VmConfig<F> for SystemConfig {
205    type Executor = SystemExecutor<F>;
206    type Periphery = SystemPeriphery<F>;
207
208    fn system(&self) -> &SystemConfig {
209        self
210    }
211    fn system_mut(&mut self) -> &mut SystemConfig {
212        self
213    }
214
215    fn create_chip_complex(
216        &self,
217    ) -> Result<VmChipComplex<F, Self::Executor, Self::Periphery>, VmInventoryError> {
218        let complex = SystemComplex::new(self.clone());
219        Ok(complex)
220    }
221}