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 with_default(self, default_value: Felt) -> Self {
98 FeltSchema {
99 default_value: Some(default_value),
100 ..self
101 }
102 }
103
104 pub fn with_description(self, description: impl Into<String>) -> Self {
106 FeltSchema {
107 description: Some(description.into()),
108 ..self
109 }
110 }
111
112 pub fn felt_type(&self) -> SchemaType {
114 self.r#type.clone()
115 }
116
117 pub fn name(&self) -> Option<&str> {
118 self.name.as_deref()
119 }
120
121 pub fn description(&self) -> Option<&String> {
122 self.description.as_ref()
123 }
124
125 pub fn default_value(&self) -> Option<Felt> {
126 self.default_value
127 }
128
129 pub(super) fn collect_init_value_requirements(
130 &self,
131 slot_prefix: StorageValueName,
132 requirements: &mut BTreeMap<StorageValueName, SchemaRequirement>,
133 ) -> Result<(), ComponentMetadataError> {
134 if self.r#type == SchemaType::void() {
135 return Ok(());
136 }
137
138 let Some(name) = self.name.as_deref() else {
139 return Err(ComponentMetadataError::InvalidSchema(
140 "non-void felt elements must be named".into(),
141 ));
142 };
143 let value_name =
144 StorageValueName::from_slot_name_with_suffix(slot_prefix.slot_name(), name)
145 .map_err(|err| ComponentMetadataError::InvalidSchema(err.to_string()))?;
146
147 let default_value = self
148 .default_value
149 .map(|felt| SCHEMA_TYPE_REGISTRY.display_felt(&self.r#type, felt));
150
151 if requirements
152 .insert(
153 value_name.clone(),
154 SchemaRequirement {
155 description: self.description.clone(),
156 r#type: self.r#type.clone(),
157 default_value,
158 },
159 )
160 .is_some()
161 {
162 return Err(ComponentMetadataError::DuplicateInitValueName(value_name));
163 }
164
165 Ok(())
166 }
167
168 pub(crate) fn try_build_felt(
173 &self,
174 init_storage_data: &InitStorageData,
175 slot_name: &StorageSlotName,
176 ) -> Result<Felt, ComponentMetadataError> {
177 let value_name = match self.name.as_deref() {
178 Some(name) => Some(
179 StorageValueName::from_slot_name_with_suffix(slot_name, name)
180 .map_err(|err| ComponentMetadataError::InvalidSchema(err.to_string()))?,
181 ),
182 None => None,
183 };
184
185 if let Some(value_name) = value_name.clone()
186 && let Some(raw_value) = init_storage_data.value_entry(&value_name)
187 {
188 match raw_value {
189 WordValue::Atomic(raw) => {
190 let felt = SCHEMA_TYPE_REGISTRY
191 .try_parse_felt(&self.r#type, raw)
192 .map_err(ComponentMetadataError::StorageValueParsingError)?;
193 return Ok(felt);
194 },
195 WordValue::Elements(_) => {
196 return Err(ComponentMetadataError::InvalidInitStorageValue(
197 value_name,
198 "expected an atomic value, got a 4-element array".into(),
199 ));
200 },
201 WordValue::FullyTyped(_) => {
202 return Err(ComponentMetadataError::InvalidInitStorageValue(
203 value_name,
204 "expected an atomic value, got a word".into(),
205 ));
206 },
207 }
208 }
209
210 if self.r#type == SchemaType::void() {
211 return Ok(Felt::ZERO);
212 }
213
214 if let Some(default_value) = self.default_value {
215 return Ok(default_value);
216 }
217
218 let Some(value_name) = value_name else {
219 return Err(ComponentMetadataError::InvalidSchema(
220 "non-void felt elements must be named".into(),
221 ));
222 };
223
224 Err(ComponentMetadataError::InitValueNotProvided(value_name))
225 }
226
227 pub(super) fn validate(&self) -> Result<(), ComponentMetadataError> {
229 if let Some(description) = self.description.as_deref() {
230 validate_description_ascii(description)?;
231 }
232
233 let type_exists = SCHEMA_TYPE_REGISTRY.contains_felt_type(&self.felt_type());
234 if !type_exists {
235 return Err(ComponentMetadataError::InvalidType(
236 self.felt_type().to_string(),
237 "Felt".into(),
238 ));
239 }
240
241 if self.r#type == SchemaType::void() {
242 if self.name.is_some() {
243 return Err(ComponentMetadataError::InvalidSchema(
244 "void felt elements must be unnamed".into(),
245 ));
246 }
247 if self.default_value.is_some() {
248 return Err(ComponentMetadataError::InvalidSchema(
249 "void felt elements cannot define `default-value`".into(),
250 ));
251 }
252 return Ok(());
253 }
254
255 if self.name.is_none() {
256 return Err(ComponentMetadataError::InvalidSchema(
257 "non-void felt elements must be named".into(),
258 ));
259 }
260
261 if let Some(value) = self.default_value {
262 SCHEMA_TYPE_REGISTRY
263 .validate_felt_value(&self.felt_type(), value)
264 .map_err(ComponentMetadataError::StorageValueParsingError)?;
265 }
266 Ok(())
267 }
268
269 pub(super) fn write_into_with_optional_defaults<W: ByteWriter>(
270 &self,
271 target: &mut W,
272 include_defaults: bool,
273 ) {
274 target.write(&self.name);
275 target.write(&self.description);
276 target.write(&self.r#type);
277 let default_value = if include_defaults { self.default_value } else { None };
278 target.write(default_value);
279 }
280}
281
282impl Serializable for FeltSchema {
283 fn write_into<W: ByteWriter>(&self, target: &mut W) {
284 self.write_into_with_optional_defaults(target, true);
285 }
286}
287
288impl Deserializable for FeltSchema {
289 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
290 let name = Option::<String>::read_from(source)?;
291 let description = Option::<String>::read_from(source)?;
292 let r#type = SchemaType::read_from(source)?;
293 let default_value = Option::<Felt>::read_from(source)?;
294 Ok(FeltSchema { name, description, r#type, default_value })
295 }
296}