1use miden_air::trace::MIN_TRACE_LEN;
2use miden_core::program::MIN_STACK_DEPTH;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct ExecutionOptions {
13 max_cycles: u32,
14 expected_cycles: u32,
15 core_trace_fragment_size: usize,
16 enable_tracing: bool,
17 enable_debugging: bool,
18 max_adv_map_value_size: usize,
21 max_adv_map_elements: usize,
23 max_hash_len_bytes: usize,
25 max_num_continuations: usize,
28 max_stack_depth: usize,
31}
32
33impl Default for ExecutionOptions {
34 fn default() -> Self {
35 ExecutionOptions {
36 max_cycles: Self::MAX_CYCLES,
37 expected_cycles: MIN_TRACE_LEN as u32,
38 core_trace_fragment_size: Self::DEFAULT_CORE_TRACE_FRAGMENT_SIZE,
39 enable_tracing: false,
40 enable_debugging: false,
41 max_adv_map_value_size: Self::DEFAULT_MAX_ADV_MAP_VALUE_SIZE,
42 max_adv_map_elements: Self::DEFAULT_MAX_ADV_MAP_ELEMENTS,
43 max_hash_len_bytes: Self::DEFAULT_MAX_HASH_LEN_BYTES,
44 max_num_continuations: Self::DEFAULT_MAX_NUM_CONTINUATIONS,
45 max_stack_depth: Self::DEFAULT_MAX_STACK_DEPTH,
46 }
47 }
48}
49
50impl ExecutionOptions {
51 pub const MAX_CYCLES: u32 = 1 << 29;
56
57 pub const DEFAULT_CORE_TRACE_FRAGMENT_SIZE: usize = 4096; pub const DEFAULT_MAX_ADV_MAP_VALUE_SIZE: usize = 1 << 17;
63
64 pub const DEFAULT_MAX_ADV_MAP_ELEMENTS: usize = 1 << 20;
69
70 pub const DEFAULT_MAX_HASH_LEN_BYTES: usize = 1 << 20;
73
74 pub const DEFAULT_MAX_NUM_CONTINUATIONS: usize = 1 << 16;
77
78 pub const DEFAULT_MAX_STACK_DEPTH: usize = 6615;
83
84 pub fn new(
97 max_cycles: Option<u32>,
98 expected_cycles: u32,
99 core_trace_fragment_size: usize,
100 enable_tracing: bool,
101 enable_debugging: bool,
102 ) -> Result<Self, ExecutionOptionsError> {
103 let max_cycles = if let Some(max_cycles) = max_cycles {
105 if max_cycles > Self::MAX_CYCLES {
106 return Err(ExecutionOptionsError::MaxCycleNumTooBig {
107 max_cycles,
108 max_cycles_limit: Self::MAX_CYCLES,
109 });
110 }
111 if max_cycles < MIN_TRACE_LEN as u32 {
112 return Err(ExecutionOptionsError::MaxCycleNumTooSmall {
113 max_cycles,
114 min_cycles_limit: MIN_TRACE_LEN,
115 });
116 }
117 max_cycles
118 } else {
119 Self::MAX_CYCLES
120 };
121 let expected_cycles = expected_cycles.next_power_of_two().max(MIN_TRACE_LEN as u32);
124 if max_cycles < expected_cycles {
126 return Err(ExecutionOptionsError::ExpectedCyclesTooBig {
127 max_cycles,
128 expected_cycles,
129 });
130 }
131
132 if core_trace_fragment_size == 0 {
134 return Err(ExecutionOptionsError::CoreTraceFragmentSizeTooSmall);
135 }
136
137 Ok(ExecutionOptions {
138 max_cycles,
139 expected_cycles,
140 core_trace_fragment_size,
141 enable_tracing,
142 enable_debugging,
143 max_adv_map_value_size: Self::DEFAULT_MAX_ADV_MAP_VALUE_SIZE,
144 max_adv_map_elements: Self::DEFAULT_MAX_ADV_MAP_ELEMENTS,
145 max_hash_len_bytes: Self::DEFAULT_MAX_HASH_LEN_BYTES,
146 max_num_continuations: Self::DEFAULT_MAX_NUM_CONTINUATIONS,
147 max_stack_depth: Self::DEFAULT_MAX_STACK_DEPTH,
148 })
149 }
150
151 pub fn with_core_trace_fragment_size(
155 mut self,
156 size: usize,
157 ) -> Result<Self, ExecutionOptionsError> {
158 if size == 0 {
159 return Err(ExecutionOptionsError::CoreTraceFragmentSizeTooSmall);
160 }
161 self.core_trace_fragment_size = size;
162 Ok(self)
163 }
164
165 pub fn with_tracing(mut self, enable_tracing: bool) -> Self {
167 self.enable_tracing = enable_tracing;
168 self
169 }
170
171 pub fn with_debugging(mut self, enable_debugging: bool) -> Self {
179 self.enable_debugging = enable_debugging;
180 self
181 }
182
183 #[inline(always)]
188 pub fn max_cycles(&self) -> u32 {
189 self.max_cycles
190 }
191
192 pub fn expected_cycles(&self) -> u32 {
198 self.expected_cycles
199 }
200
201 pub fn core_trace_fragment_size(&self) -> usize {
203 self.core_trace_fragment_size
204 }
205
206 #[inline]
208 pub fn enable_tracing(&self) -> bool {
209 self.enable_tracing
210 }
211
212 #[inline]
214 pub fn enable_debugging(&self) -> bool {
215 self.enable_debugging
216 }
217
218 #[inline]
220 pub fn max_adv_map_value_size(&self) -> usize {
221 self.max_adv_map_value_size
222 }
223
224 #[inline]
227 pub fn max_adv_map_elements(&self) -> usize {
228 self.max_adv_map_elements
229 }
230
231 #[inline]
233 pub fn max_hash_len_bytes(&self) -> usize {
234 self.max_hash_len_bytes
235 }
236
237 pub fn with_max_adv_map_value_size(mut self, size: usize) -> Self {
239 self.max_adv_map_value_size = size;
240 self
241 }
242
243 pub fn with_max_adv_map_elements(mut self, size: usize) -> Self {
245 self.max_adv_map_elements = size;
246 self
247 }
248
249 pub fn with_max_hash_len_bytes(mut self, size: usize) -> Self {
251 self.max_hash_len_bytes = size;
252 self
253 }
254
255 #[inline]
257 pub fn max_num_continuations(&self) -> usize {
258 self.max_num_continuations
259 }
260
261 #[inline]
264 pub fn max_stack_depth(&self) -> usize {
265 self.max_stack_depth
266 }
267
268 pub fn with_max_num_continuations(mut self, max_num_continuations: usize) -> Self {
270 self.max_num_continuations = max_num_continuations;
271 self
272 }
273
274 pub fn with_max_stack_depth(
277 mut self,
278 max_stack_depth: usize,
279 ) -> Result<Self, ExecutionOptionsError> {
280 if max_stack_depth < MIN_STACK_DEPTH {
281 return Err(ExecutionOptionsError::MaxStackDepthTooSmall {
282 max_stack_depth,
283 min_stack_depth: MIN_STACK_DEPTH,
284 });
285 }
286 self.max_stack_depth = max_stack_depth;
287 Ok(self)
288 }
289}
290
291#[derive(Debug, thiserror::Error)]
295pub enum ExecutionOptionsError {
296 #[error(
297 "expected number of cycles {expected_cycles} must be smaller than the maximum number of cycles {max_cycles}"
298 )]
299 ExpectedCyclesTooBig { max_cycles: u32, expected_cycles: u32 },
300 #[error("maximum number of cycles {max_cycles} must be greater than {min_cycles_limit}")]
301 MaxCycleNumTooSmall { max_cycles: u32, min_cycles_limit: usize },
302 #[error("maximum number of cycles {max_cycles} must be less than {max_cycles_limit}")]
303 MaxCycleNumTooBig { max_cycles: u32, max_cycles_limit: u32 },
304 #[error("core trace fragment size must be greater than 0")]
305 CoreTraceFragmentSizeTooSmall,
306 #[error("maximum stack depth {max_stack_depth} must be at least {min_stack_depth}")]
307 MaxStackDepthTooSmall {
308 max_stack_depth: usize,
309 min_stack_depth: usize,
310 },
311}
312
313#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[test]
321 fn valid_fragment_size() {
322 let opts = ExecutionOptions::new(None, 64, 1024, false, false);
324 assert!(opts.is_ok());
325 assert_eq!(opts.unwrap().core_trace_fragment_size(), 1024);
326
327 let opts = ExecutionOptions::new(None, 64, 4096, false, false);
328 assert!(opts.is_ok());
329
330 let opts = ExecutionOptions::new(None, 64, 1, false, false);
331 assert!(opts.is_ok());
332 }
333
334 #[test]
335 fn zero_fragment_size_fails() {
336 let opts = ExecutionOptions::new(None, 64, 0, false, false);
337 assert!(matches!(opts, Err(ExecutionOptionsError::CoreTraceFragmentSizeTooSmall)));
338 }
339
340 #[test]
341 fn with_core_trace_fragment_size_validates() {
342 let result = ExecutionOptions::default().with_core_trace_fragment_size(2048);
344 assert!(result.is_ok());
345 assert_eq!(result.unwrap().core_trace_fragment_size(), 2048);
346
347 let result = ExecutionOptions::default().with_core_trace_fragment_size(0);
349 assert!(matches!(result, Err(ExecutionOptionsError::CoreTraceFragmentSizeTooSmall)));
350 }
351
352 #[test]
353 fn expected_cycles_validated_after_rounding() {
354 let opts = ExecutionOptions::new(Some(100), 65, 1024, false, false);
356 assert!(matches!(
357 opts,
358 Err(ExecutionOptionsError::ExpectedCyclesTooBig {
359 max_cycles: 100,
360 expected_cycles: 128
361 })
362 ));
363
364 let opts = ExecutionOptions::new(Some(100), 64, 1024, false, false);
366 assert!(opts.is_ok());
367 assert_eq!(opts.unwrap().expected_cycles(), 64);
368 }
369
370 #[test]
371 fn max_stack_depth_validates_minimum_depth() {
372 let result = ExecutionOptions::default().with_max_stack_depth(MIN_STACK_DEPTH - 1);
373 assert!(matches!(
374 result,
375 Err(ExecutionOptionsError::MaxStackDepthTooSmall {
376 max_stack_depth,
377 min_stack_depth: MIN_STACK_DEPTH,
378 }) if max_stack_depth == MIN_STACK_DEPTH - 1
379 ));
380
381 let result = ExecutionOptions::default().with_max_stack_depth(MIN_STACK_DEPTH);
382 assert!(result.is_ok());
383 assert_eq!(result.unwrap().max_stack_depth(), MIN_STACK_DEPTH);
384 }
385}