miden_protocol/account/component/storage/schema/
felt.rs1use alloc::collections::BTreeMap;
2use alloc::string::{String, ToString};
3
4use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable};
5use miden_processor::DeserializationError;
6
7use super::super::type_registry::{SCHEMA_TYPE_REGISTRY, SchemaRequirement, SchemaType};
8use super::super::{InitStorageData, StorageValueName, WordValue};
9use super::validate_description_ascii;
10use crate::account::StorageSlotName;
11use crate::errors::ComponentMetadataError;
12use crate::{Felt, FieldElement};
13
14#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct FeltSchema {
26 name: Option<String>,
27 description: Option<String>,
28 r#type: SchemaType,
29 default_value: Option<Felt>,
30}
31
32impl FeltSchema {
33 pub fn new_typed(r#type: SchemaType, name: impl Into<String>) -> Self {
35 FeltSchema {
36 name: Some(name.into()),
37 description: None,
38 r#type,
39 default_value: None,
40 }
41 }
42
43 pub fn new_typed_with_default(
45 r#type: SchemaType,
46 name: impl Into<String>,
47 default_value: Felt,
48 ) -> Self {
49 FeltSchema {
50 name: Some(name.into()),
51 description: None,
52 r#type,
53 default_value: Some(default_value),
54 }
55 }
56
57 pub fn new_void() -> Self {
59 FeltSchema {
60 name: None,
61 description: None,
62 r#type: SchemaType::void(),
63 default_value: None,
64 }
65 }
66
67 pub fn felt(name: impl Into<String>) -> Self {
69 Self::new_typed(SchemaType::native_felt(), name)
70 }
71
72 pub fn word(name: impl Into<String>) -> Self {
74 Self::new_typed(SchemaType::native_word(), name)
75 }
76
77 pub fn u8(name: impl Into<String>) -> Self {
79 Self::new_typed(SchemaType::u8(), name)
80 }
81
82 pub fn u16(name: impl Into<String>) -> Self {
84 Self::new_typed(SchemaType::u16(), name)
85 }
86
87 pub fn u32(name: impl Into<String>) -> Self {
89 Self::new_typed(SchemaType::u32(), name)
90 }
91
92 pub fn with_default(self, default_value: Felt) -> Self {
94 FeltSchema {
95 default_value: Some(default_value),
96 ..self
97 }
98 }
99
100 pub fn with_description(self, description: impl Into<String>) -> Self {
102 FeltSchema {
103 description: Some(description.into()),
104 ..self
105 }
106 }
107
108 pub fn felt_type(&self) -> SchemaType {
110 self.r#type.clone()
111 }
112
113 pub fn name(&self) -> Option<&str> {
114 self.name.as_deref()
115 }
116
117 pub fn description(&self) -> Option<&String> {
118 self.description.as_ref()
119 }
120
121 pub fn default_value(&self) -> Option<Felt> {
122 self.default_value
123 }
124
125 pub(super) fn collect_init_value_requirements(
126 &self,
127 slot_prefix: StorageValueName,
128 requirements: &mut BTreeMap<StorageValueName, SchemaRequirement>,
129 ) -> Result<(), ComponentMetadataError> {
130 if self.r#type == SchemaType::void() {
131 return Ok(());
132 }
133
134 let Some(name) = self.name.as_deref() else {
135 return Err(ComponentMetadataError::InvalidSchema(
136 "non-void felt elements must be named".into(),
137 ));
138 };
139 let value_name =
140 StorageValueName::from_slot_name_with_suffix(slot_prefix.slot_name(), name)
141 .map_err(|err| ComponentMetadataError::InvalidSchema(err.to_string()))?;
142
143 let default_value = self
144 .default_value
145 .map(|felt| SCHEMA_TYPE_REGISTRY.display_felt(&self.r#type, felt));
146
147 if requirements
148 .insert(
149 value_name.clone(),
150 SchemaRequirement {
151 description: self.description.clone(),
152 r#type: self.r#type.clone(),
153 default_value,
154 },
155 )
156 .is_some()
157 {
158 return Err(ComponentMetadataError::DuplicateInitValueName(value_name));
159 }
160
161 Ok(())
162 }
163
164 pub(crate) fn try_build_felt(
169 &self,
170 init_storage_data: &InitStorageData,
171 slot_name: &StorageSlotName,
172 ) -> Result<Felt, ComponentMetadataError> {
173 let value_name = match self.name.as_deref() {
174 Some(name) => Some(
175 StorageValueName::from_slot_name_with_suffix(slot_name, name)
176 .map_err(|err| ComponentMetadataError::InvalidSchema(err.to_string()))?,
177 ),
178 None => None,
179 };
180
181 if let Some(value_name) = value_name.clone()
182 && let Some(raw_value) = init_storage_data.value_entry(&value_name)
183 {
184 match raw_value {
185 WordValue::Atomic(raw) => {
186 let felt = SCHEMA_TYPE_REGISTRY
187 .try_parse_felt(&self.r#type, raw)
188 .map_err(ComponentMetadataError::StorageValueParsingError)?;
189 return Ok(felt);
190 },
191 WordValue::Elements(_) => {
192 return Err(ComponentMetadataError::InvalidInitStorageValue(
193 value_name,
194 "expected an atomic value, got a 4-element array".into(),
195 ));
196 },
197 WordValue::FullyTyped(_) => {
198 return Err(ComponentMetadataError::InvalidInitStorageValue(
199 value_name,
200 "expected an atomic value, got a word".into(),
201 ));
202 },
203 }
204 }
205
206 if self.r#type == SchemaType::void() {
207 return Ok(Felt::ZERO);
208 }
209
210 if let Some(default_value) = self.default_value {
211 return Ok(default_value);
212 }
213
214 let Some(value_name) = value_name else {
215 return Err(ComponentMetadataError::InvalidSchema(
216 "non-void felt elements must be named".into(),
217 ));
218 };
219
220 Err(ComponentMetadataError::InitValueNotProvided(value_name))
221 }
222
223 pub(super) fn validate(&self) -> Result<(), ComponentMetadataError> {
225 if let Some(description) = self.description.as_deref() {
226 validate_description_ascii(description)?;
227 }
228
229 let type_exists = SCHEMA_TYPE_REGISTRY.contains_felt_type(&self.felt_type());
230 if !type_exists {
231 return Err(ComponentMetadataError::InvalidType(
232 self.felt_type().to_string(),
233 "Felt".into(),
234 ));
235 }
236
237 if self.r#type == SchemaType::void() {
238 if self.name.is_some() {
239 return Err(ComponentMetadataError::InvalidSchema(
240 "void felt elements must be unnamed".into(),
241 ));
242 }
243 if self.default_value.is_some() {
244 return Err(ComponentMetadataError::InvalidSchema(
245 "void felt elements cannot define `default-value`".into(),
246 ));
247 }
248 return Ok(());
249 }
250
251 if self.name.is_none() {
252 return Err(ComponentMetadataError::InvalidSchema(
253 "non-void felt elements must be named".into(),
254 ));
255 }
256
257 if let Some(value) = self.default_value {
258 SCHEMA_TYPE_REGISTRY
259 .validate_felt_value(&self.felt_type(), value)
260 .map_err(ComponentMetadataError::StorageValueParsingError)?;
261 }
262 Ok(())
263 }
264
265 pub(super) fn write_into_with_optional_defaults<W: ByteWriter>(
266 &self,
267 target: &mut W,
268 include_defaults: bool,
269 ) {
270 target.write(&self.name);
271 target.write(&self.description);
272 target.write(&self.r#type);
273 let default_value = if include_defaults { self.default_value } else { None };
274 target.write(default_value);
275 }
276}
277
278impl Serializable for FeltSchema {
279 fn write_into<W: ByteWriter>(&self, target: &mut W) {
280 self.write_into_with_optional_defaults(target, true);
281 }
282}
283
284impl Deserializable for FeltSchema {
285 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
286 let name = Option::<String>::read_from(source)?;
287 let description = Option::<String>::read_from(source)?;
288 let r#type = SchemaType::read_from(source)?;
289 let default_value = Option::<Felt>::read_from(source)?;
290 Ok(FeltSchema { name, description, r#type, default_value })
291 }
292}