miden_debug_engine/exec/
config.rs1use alloc::{
2 string::{String, ToString},
3 vec::Vec,
4};
5#[cfg(feature = "std")]
6use std::{ffi::OsStr, path::Path};
7
8use miden_processor::{
9 ExecutionOptions, StackInputs,
10 advice::{AdviceInputs, AdviceStack},
11};
12use serde::Deserialize;
13
14use crate::felt::Felt;
15
16#[derive(Debug, Clone, Default, Deserialize)]
17#[serde(try_from = "ExecutionConfigFile")]
18pub struct ExecutionConfig {
19 pub inputs: StackInputs,
20 pub advice_inputs: AdviceInputs,
21 pub options: ExecutionOptions,
22}
23
24impl TryFrom<ExecutionConfigFile> for ExecutionConfig {
25 type Error = String;
26
27 #[inline]
28 fn try_from(file: ExecutionConfigFile) -> Result<Self, Self::Error> {
29 Self::from_inputs_file(file)
30 }
31}
32
33impl ExecutionConfig {
34 #[cfg(feature = "std")]
35 pub fn parse_file<P>(path: P) -> std::io::Result<Self>
36 where
37 P: AsRef<std::path::Path>,
38 {
39 let path = path.as_ref();
40 let content = std::fs::read_to_string(path)?;
41
42 let file =
43 toml::from_str::<ExecutionConfigFile>(&content).map_err(std::io::Error::other)?;
44 Self::from_inputs_file(file).map_err(std::io::Error::other)
45 }
46
47 pub fn parse_str(content: &str) -> Result<Self, String> {
48 let file = toml::from_str::<ExecutionConfigFile>(content).map_err(|err| err.to_string())?;
49
50 Self::from_inputs_file(file)
51 }
52
53 fn from_inputs_file(file: ExecutionConfigFile) -> Result<Self, String> {
54 let felts: Vec<_> = file.inputs.stack.into_iter().map(|felt| felt.0).collect();
55 let inputs =
56 StackInputs::new(&felts).map_err(|err| format!("invalid value for 'stack': {err}"))?;
57 let advice_inputs = AdviceInputs::default()
58 .with_stack(
59 file.inputs.advice.stack.into_iter().map(|felt| felt.0).collect::<AdviceStack>(),
60 )
61 .with_map(file.inputs.advice.map.into_iter().map(|entry| {
62 (entry.digest.0, entry.values.into_iter().map(|felt| felt.0).collect::<Vec<_>>())
63 }));
64
65 Ok(Self {
66 inputs,
67 advice_inputs,
68 options: file.options,
69 })
70 }
71}
72
73#[derive(Debug, Clone, Default, Deserialize)]
74#[serde(default)]
75struct ExecutionConfigFile {
76 inputs: Inputs,
77 #[serde(deserialize_with = "deserialize_execution_options")]
78 options: ExecutionOptions,
79}
80
81#[derive(Debug, Clone, Default, Deserialize)]
82#[serde(default)]
83struct Inputs {
84 stack: Vec<Felt>,
86 advice: Advice,
88}
89
90#[derive(Debug, Clone, Default, Deserialize)]
91#[serde(default)]
92struct Advice {
93 stack: Vec<Felt>,
95 map: Vec<AdviceMapEntry>,
97}
98
99#[derive(Debug, Clone, Deserialize)]
100struct AdviceMapEntry {
101 digest: Word,
102 values: Vec<Felt>,
104}
105
106#[cfg(feature = "std")]
107impl clap::builder::ValueParserFactory for ExecutionConfig {
108 type Parser = ExecutionConfigParser;
109
110 fn value_parser() -> Self::Parser {
111 ExecutionConfigParser
112 }
113}
114
115#[cfg(feature = "std")]
116#[doc(hidden)]
117#[derive(Clone)]
118pub struct ExecutionConfigParser;
119
120#[cfg(feature = "std")]
121impl clap::builder::TypedValueParser for ExecutionConfigParser {
122 type Value = ExecutionConfig;
123
124 fn parse_ref(
125 &self,
126 _cmd: &clap::Command,
127 _arg: Option<&clap::Arg>,
128 value: &OsStr,
129 ) -> Result<Self::Value, clap::error::Error> {
130 use clap::error::{Error, ErrorKind};
131
132 let inputs_path = Path::new(value);
133 if !inputs_path.is_file() {
134 return Err(Error::raw(
135 ErrorKind::InvalidValue,
136 format!("invalid inputs file: '{}' is not a file", inputs_path.display()),
137 ));
138 }
139
140 let content = std::fs::read_to_string(inputs_path).map_err(|err| {
141 Error::raw(ErrorKind::ValueValidation, format!("failed to read inputs file: {err}"))
142 })?;
143 let inputs_file = toml::from_str::<ExecutionConfigFile>(&content).map_err(|err| {
144 Error::raw(ErrorKind::ValueValidation, format!("invalid inputs file: {err}"))
145 })?;
146
147 ExecutionConfig::from_inputs_file(inputs_file).map_err(|err| {
148 Error::raw(ErrorKind::ValueValidation, format!("invalid inputs file: {err}"))
149 })
150 }
151}
152
153#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
154struct Word(miden_core::Word);
155impl<'de> Deserialize<'de> for Word {
156 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
157 where
158 D: serde::Deserializer<'de>,
159 {
160 let digest = String::deserialize(deserializer)?;
161 miden_core::Word::try_from(&digest)
162 .map_err(|err| serde::de::Error::custom(format!("invalid digest: {err}")))
163 .map(Self)
164 }
165}
166
167fn deserialize_execution_options<'de, D>(deserializer: D) -> Result<ExecutionOptions, D::Error>
168where
169 D: serde::Deserializer<'de>,
170{
171 #[derive(Default, Deserialize)]
172 #[serde(default)]
173 struct ExecOptions {
174 max_cycles: Option<u32>,
175 expected_cycles: u32,
176 }
177
178 ExecOptions::deserialize(deserializer).and_then(|opts| {
179 ExecutionOptions::new(
180 opts.max_cycles,
181 opts.expected_cycles,
182 ExecutionOptions::DEFAULT_CORE_TRACE_FRAGMENT_SIZE,
183 )
184 .map_err(|err| serde::de::Error::custom(format!("invalid execution options: {err}")))
185 })
186}
187
188#[cfg(test)]
189mod tests;