1use miden_air::trace::MIN_TRACE_LEN;
2use miden_core::{deferred::PrecompileLimits, program::MIN_STACK_DEPTH};
3
4#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct ExecutionOptions {
13 max_cycles: u32,
14 expected_cycles: u32,
15 core_trace_fragment_size: usize,
16 max_advice_size_bytes: usize,
18 overlapped_trace_build: bool,
21 max_hash_len_bytes: usize,
23 max_num_continuations: usize,
26 max_stack_depth: usize,
35 max_memory_elements: usize,
38 execution_precompile_limits: PrecompileLimits,
40 verification_precompile_limits: PrecompileLimits,
42}
43
44impl Default for ExecutionOptions {
45 fn default() -> Self {
46 ExecutionOptions {
47 max_cycles: Self::MAX_CYCLES,
48 expected_cycles: MIN_TRACE_LEN as u32,
49 core_trace_fragment_size: Self::DEFAULT_CORE_TRACE_FRAGMENT_SIZE,
50 max_advice_size_bytes: Self::DEFAULT_MAX_ADVICE_SIZE_BYTES,
51 max_hash_len_bytes: Self::DEFAULT_MAX_HASH_LEN_BYTES,
52 max_num_continuations: Self::DEFAULT_MAX_NUM_CONTINUATIONS,
53 max_stack_depth: Self::DEFAULT_MAX_STACK_DEPTH,
54 max_memory_elements: Self::DEFAULT_MAX_MEMORY_ELEMENTS,
55 overlapped_trace_build: true,
56 execution_precompile_limits: miden_precompiles::default_execution_precompile_limits(),
57 verification_precompile_limits:
58 miden_precompiles::default_verification_precompile_limits(),
59 }
60 }
61}
62
63impl ExecutionOptions {
64 pub const MAX_CYCLES: u32 = 1 << 29;
69
70 pub const DEFAULT_CORE_TRACE_FRAGMENT_SIZE: usize = 4096; pub const DEFAULT_MAX_ADVICE_SIZE_BYTES: usize = 16 * 1024 * 1024;
75
76 pub const DEFAULT_MAX_HASH_LEN_BYTES: usize = 1 << 20;
79
80 pub const DEFAULT_MAX_NUM_CONTINUATIONS: usize = 1 << 16;
83
84 pub const DEFAULT_MAX_STACK_DEPTH: usize = 6615;
89
90 pub const DEFAULT_MAX_MEMORY_ELEMENTS: usize = 1 << 28;
98
99 pub fn new(
112 max_cycles: Option<u32>,
113 expected_cycles: u32,
114 core_trace_fragment_size: usize,
115 ) -> Result<Self, ExecutionOptionsError> {
116 let max_cycles = if let Some(max_cycles) = max_cycles {
118 if max_cycles > Self::MAX_CYCLES {
119 return Err(ExecutionOptionsError::MaxCycleNumTooBig {
120 max_cycles,
121 max_cycles_limit: Self::MAX_CYCLES,
122 });
123 }
124 if max_cycles < MIN_TRACE_LEN as u32 {
125 return Err(ExecutionOptionsError::MaxCycleNumTooSmall {
126 max_cycles,
127 min_cycles_limit: MIN_TRACE_LEN,
128 });
129 }
130 max_cycles
131 } else {
132 Self::MAX_CYCLES
133 };
134 let expected_cycles = expected_cycles.next_power_of_two().max(MIN_TRACE_LEN as u32);
137 if max_cycles < expected_cycles {
139 return Err(ExecutionOptionsError::ExpectedCyclesTooBig {
140 max_cycles,
141 expected_cycles,
142 });
143 }
144
145 if core_trace_fragment_size == 0 {
147 return Err(ExecutionOptionsError::CoreTraceFragmentSizeTooSmall);
148 }
149
150 Ok(ExecutionOptions {
151 max_cycles,
152 expected_cycles,
153 core_trace_fragment_size,
154 max_advice_size_bytes: Self::DEFAULT_MAX_ADVICE_SIZE_BYTES,
155 max_hash_len_bytes: Self::DEFAULT_MAX_HASH_LEN_BYTES,
156 max_num_continuations: Self::DEFAULT_MAX_NUM_CONTINUATIONS,
157 max_stack_depth: Self::DEFAULT_MAX_STACK_DEPTH,
158 max_memory_elements: Self::DEFAULT_MAX_MEMORY_ELEMENTS,
159 overlapped_trace_build: true,
160 execution_precompile_limits: miden_precompiles::default_execution_precompile_limits(),
161 verification_precompile_limits:
162 miden_precompiles::default_verification_precompile_limits(),
163 })
164 }
165
166 pub fn with_core_trace_fragment_size(
170 mut self,
171 size: usize,
172 ) -> Result<Self, ExecutionOptionsError> {
173 if size == 0 {
174 return Err(ExecutionOptionsError::CoreTraceFragmentSizeTooSmall);
175 }
176 self.core_trace_fragment_size = size;
177 Ok(self)
178 }
179
180 #[inline(always)]
185 pub fn max_cycles(&self) -> u32 {
186 self.max_cycles
187 }
188
189 pub fn expected_cycles(&self) -> u32 {
195 self.expected_cycles
196 }
197
198 pub fn core_trace_fragment_size(&self) -> usize {
200 self.core_trace_fragment_size
201 }
202
203 #[inline]
205 pub fn max_advice_size_bytes(&self) -> usize {
206 self.max_advice_size_bytes
207 }
208
209 #[inline]
211 pub fn max_hash_len_bytes(&self) -> usize {
212 self.max_hash_len_bytes
213 }
214
215 pub fn with_overlapped_trace_build(mut self, overlapped: bool) -> Self {
220 self.overlapped_trace_build = overlapped;
221 self
222 }
223
224 pub fn overlapped_trace_build(&self) -> bool {
226 self.overlapped_trace_build
227 }
228
229 pub fn with_max_advice_size_bytes(mut self, size: usize) -> Self {
231 self.max_advice_size_bytes = size;
232 self
233 }
234
235 pub fn with_max_hash_len_bytes(mut self, size: usize) -> Self {
237 self.max_hash_len_bytes = size;
238 self
239 }
240
241 #[inline]
243 pub fn max_num_continuations(&self) -> usize {
244 self.max_num_continuations
245 }
246
247 #[inline]
250 pub fn max_stack_depth(&self) -> usize {
251 self.max_stack_depth
252 }
253
254 #[inline]
259 pub fn max_memory_elements(&self) -> usize {
260 self.max_memory_elements
261 }
262
263 pub fn with_max_num_continuations(mut self, max_num_continuations: usize) -> Self {
265 self.max_num_continuations = max_num_continuations;
266 self
267 }
268
269 pub fn with_max_stack_depth(
272 mut self,
273 max_stack_depth: usize,
274 ) -> Result<Self, ExecutionOptionsError> {
275 if max_stack_depth < MIN_STACK_DEPTH {
276 return Err(ExecutionOptionsError::MaxStackDepthTooSmall {
277 max_stack_depth,
278 min_stack_depth: MIN_STACK_DEPTH,
279 });
280 }
281 self.max_stack_depth = max_stack_depth;
282 Ok(self)
283 }
284
285 pub fn with_max_memory_elements(mut self, max_memory_elements: usize) -> Self {
287 self.max_memory_elements = max_memory_elements;
288 self
289 }
290
291 pub const fn execution_precompile_limits(&self) -> &PrecompileLimits {
293 &self.execution_precompile_limits
294 }
295
296 pub fn with_execution_precompile_limits(mut self, limits: PrecompileLimits) -> Self {
298 self.execution_precompile_limits = limits;
299 self
300 }
301
302 pub const fn verification_precompile_limits(&self) -> &PrecompileLimits {
304 &self.verification_precompile_limits
305 }
306
307 pub fn with_verification_precompile_limits(mut self, limits: PrecompileLimits) -> Self {
309 self.verification_precompile_limits = limits;
310 self
311 }
312}
313
314#[derive(Debug, thiserror::Error)]
318pub enum ExecutionOptionsError {
319 #[error(
320 "expected number of cycles {expected_cycles} must be smaller than the maximum number of cycles {max_cycles}"
321 )]
322 ExpectedCyclesTooBig { max_cycles: u32, expected_cycles: u32 },
323 #[error("maximum number of cycles {max_cycles} must be greater than {min_cycles_limit}")]
324 MaxCycleNumTooSmall { max_cycles: u32, min_cycles_limit: usize },
325 #[error("maximum number of cycles {max_cycles} must be less than {max_cycles_limit}")]
326 MaxCycleNumTooBig { max_cycles: u32, max_cycles_limit: u32 },
327 #[error("core trace fragment size must be greater than 0")]
328 CoreTraceFragmentSizeTooSmall,
329 #[error("maximum stack depth {max_stack_depth} must be at least {min_stack_depth}")]
330 MaxStackDepthTooSmall {
331 max_stack_depth: usize,
332 min_stack_depth: usize,
333 },
334}
335
336#[cfg(test)]
340mod tests {
341 use super::*;
342
343 #[test]
344 fn valid_fragment_size() {
345 let opts = ExecutionOptions::new(None, 64, 1024);
347 assert!(opts.is_ok());
348 assert_eq!(opts.unwrap().core_trace_fragment_size(), 1024);
349
350 let opts = ExecutionOptions::new(None, 64, 4096);
351 assert!(opts.is_ok());
352
353 let opts = ExecutionOptions::new(None, 64, 1);
354 assert!(opts.is_ok());
355 }
356
357 #[test]
358 fn zero_fragment_size_fails() {
359 let opts = ExecutionOptions::new(None, 64, 0);
360 assert!(matches!(opts, Err(ExecutionOptionsError::CoreTraceFragmentSizeTooSmall)));
361 }
362
363 #[test]
364 fn with_core_trace_fragment_size_validates() {
365 let result = ExecutionOptions::default().with_core_trace_fragment_size(2048);
367 assert!(result.is_ok());
368 assert_eq!(result.unwrap().core_trace_fragment_size(), 2048);
369
370 let result = ExecutionOptions::default().with_core_trace_fragment_size(0);
372 assert!(matches!(result, Err(ExecutionOptionsError::CoreTraceFragmentSizeTooSmall)));
373 }
374
375 #[test]
376 fn expected_cycles_validated_after_rounding() {
377 let opts = ExecutionOptions::new(Some(100), 65, 1024);
379 assert!(matches!(
380 opts,
381 Err(ExecutionOptionsError::ExpectedCyclesTooBig {
382 max_cycles: 100,
383 expected_cycles: 128
384 })
385 ));
386
387 let opts = ExecutionOptions::new(Some(100), 64, 1024);
389 assert!(opts.is_ok());
390 assert_eq!(opts.unwrap().expected_cycles(), 64);
391 }
392
393 #[test]
394 fn max_stack_depth_validates_minimum_depth() {
395 let result = ExecutionOptions::default().with_max_stack_depth(MIN_STACK_DEPTH - 1);
396 assert!(matches!(
397 result,
398 Err(ExecutionOptionsError::MaxStackDepthTooSmall {
399 max_stack_depth,
400 min_stack_depth: MIN_STACK_DEPTH,
401 }) if max_stack_depth == MIN_STACK_DEPTH - 1
402 ));
403
404 let result = ExecutionOptions::default().with_max_stack_depth(MIN_STACK_DEPTH);
405 assert!(result.is_ok());
406 assert_eq!(result.unwrap().max_stack_depth(), MIN_STACK_DEPTH);
407 }
408}