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