Skip to main content

miden_protocol/account/component/storage/schema/
felt.rs

1use 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// FELT SCHEMA
19// ================================================================================================
20
21/// Supported element schema descriptors for a component's storage entries.
22///
23/// Each felt element in a composed word slot is typed, can have an optional default value, and can
24/// optionally be named to allow overriding at instantiation time.
25///
26/// To avoid non-overridable constants, unnamed elements are allowed only when `type = "void"`,
27/// which always evaluates to `0` and does not require init data.
28#[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    /// Creates a new required typed felt field.
38    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    /// Creates a new typed felt field with a default value.
48    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    /// Creates an unnamed `void` felt element.
62    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    /// Creates a new required felt field typed as [`SchemaType::native_felt()`].
72    pub fn felt(name: impl Into<String>) -> Self {
73        Self::new_typed(SchemaType::native_felt(), name)
74    }
75
76    /// Creates a new required felt field typed as [`SchemaType::native_word()`].
77    pub fn word(name: impl Into<String>) -> Self {
78        Self::new_typed(SchemaType::native_word(), name)
79    }
80
81    /// Creates a new required felt field typed as [`SchemaType::u8()`].
82    pub fn u8(name: impl Into<String>) -> Self {
83        Self::new_typed(SchemaType::u8(), name)
84    }
85
86    /// Creates a new required felt field typed as [`SchemaType::u16()`].
87    pub fn u16(name: impl Into<String>) -> Self {
88        Self::new_typed(SchemaType::u16(), name)
89    }
90
91    /// Creates a new required felt field typed as [`SchemaType::u32()`].
92    pub fn u32(name: impl Into<String>) -> Self {
93        Self::new_typed(SchemaType::u32(), name)
94    }
95
96    /// Creates a new required felt field typed as [`SchemaType::bool()`].
97    pub fn bool(name: impl Into<String>) -> Self {
98        Self::new_typed(SchemaType::bool(), name)
99    }
100
101    /// Sets the default value of the [`FeltSchema`] and returns `self`.
102    pub fn with_default(self, default_value: Felt) -> Self {
103        FeltSchema {
104            default_value: Some(default_value),
105            ..self
106        }
107    }
108
109    /// Sets the description of the [`FeltSchema`] and returns `self`.
110    pub fn with_description(self, description: impl Into<String>) -> Self {
111        FeltSchema {
112            description: Some(description.into()),
113            ..self
114        }
115    }
116
117    /// Returns the felt type.
118    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    /// Attempts to convert the [`FeltSchema`] into a [`Felt`].
174    ///
175    /// If the schema variant is typed, the value is retrieved from `init_storage_data`,
176    /// identified by its key. Otherwise, the returned value is just the inner element.
177    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    /// Validates that the defined felt type exists.
233    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}