miden_objects/account/component/template/storage/
entry_content.rs

1use alloc::{
2    boxed::Box,
3    collections::BTreeSet,
4    string::{String, ToString},
5    vec::Vec,
6};
7use core::iter;
8
9use vm_core::{
10    Felt, FieldElement, Word,
11    utils::{ByteReader, ByteWriter, Deserializable, Serializable},
12};
13use vm_processor::{DeserializationError, Digest};
14
15use super::{
16    InitStorageData, MapEntry, StorageValueName, TemplateRequirementsIter,
17    placeholder::{PlaceholderTypeRequirement, TEMPLATE_REGISTRY, TemplateType},
18};
19use crate::account::{StorageMap, component::template::AccountComponentTemplateError};
20
21// WORDS
22// ================================================================================================
23
24/// Defines how a word is represented within the component's storage description.
25///
26/// Each word representation can be:
27/// - A template that defines a type but does not carry a value.
28/// - A predefined value that may contain a hardcoded word or a mix of fixed and templated felts.
29#[derive(Debug, Clone, PartialEq, Eq)]
30#[allow(clippy::large_enum_variant)]
31pub enum WordRepresentation {
32    /// A templated value that serves as a placeholder for instantiation.
33    ///
34    /// This variant defines a type but does not store a value. The actual value is provided at the
35    /// time of instantiation. The name is required to identify this template externally.
36    Template {
37        /// The type associated with this templated word.
38        r#type: TemplateType,
39        /// A human-readable identifier for the template.
40        name: StorageValueName,
41        /// An optional description explaining the purpose of this template.
42        description: Option<String>,
43    },
44
45    /// A predefined value that can be used directly within storage.
46    ///
47    /// This variant may contain either a fully hardcoded word or a structured set of felts, some
48    /// of which may themselves be templates.
49    Value {
50        /// An optional name to identify this value externally.
51        name: Option<StorageValueName>,
52        /// An optional description explaining the role of this value.
53        description: Option<String>,
54        /// The 4-felt representation of the stored word.
55        value: [FeltRepresentation; 4],
56    },
57}
58
59impl WordRepresentation {
60    /// Constructs a new `Template` variant.
61    pub fn new_template(r#type: TemplateType, name: StorageValueName) -> Self {
62        WordRepresentation::Template { name, description: None, r#type }
63    }
64
65    /// Constructs a new `Value` variant.
66    pub fn new_value(
67        value: impl Into<[FeltRepresentation; 4]>,
68        name: Option<StorageValueName>,
69    ) -> Self {
70        WordRepresentation::Value {
71            name,
72            description: None,
73            value: value.into(),
74        }
75    }
76
77    /// Sets the description of the [`WordRepresentation`] and returns `self`.
78    pub fn with_description(self, description: impl Into<String>) -> Self {
79        match self {
80            WordRepresentation::Template { r#type, name, .. } => WordRepresentation::Template {
81                r#type,
82                name,
83                description: Some(description.into()),
84            },
85            WordRepresentation::Value { name, value, .. } => WordRepresentation::Value {
86                name,
87                description: Some(description.into()),
88                value,
89            },
90        }
91    }
92
93    /// Returns the name associated with the word representation.
94    /// - For the `Template` variant, it always returns a reference to the name.
95    /// - For the `Value` variant, it returns `Some` if a name is present, or `None` otherwise.
96    pub fn name(&self) -> Option<&StorageValueName> {
97        match self {
98            WordRepresentation::Template { name, .. } => Some(name),
99            WordRepresentation::Value { name, .. } => name.as_ref(),
100        }
101    }
102
103    /// Returns the description associated with the word representation.
104    /// Both variants store an `Option<String>`, which is converted to an `Option<&str>`.
105    pub fn description(&self) -> Option<&str> {
106        match self {
107            WordRepresentation::Template { description, .. } => description.as_deref(),
108            WordRepresentation::Value { description, .. } => description.as_deref(),
109        }
110    }
111
112    /// Returns the type name.
113    pub fn word_type(&self) -> TemplateType {
114        match self {
115            WordRepresentation::Template { r#type, .. } => r#type.clone(),
116            WordRepresentation::Value { .. } => TemplateType::native_word(),
117        }
118    }
119
120    /// Returns the value (an array of 4 `FeltRepresentation`s) if this is a `Value`
121    /// variant; otherwise, returns `None`.
122    pub fn value(&self) -> Option<&[FeltRepresentation; 4]> {
123        match self {
124            WordRepresentation::Value { value, .. } => Some(value),
125            WordRepresentation::Template { .. } => None,
126        }
127    }
128
129    /// Returns an iterator over the word's placeholders.
130    ///
131    /// For [`WordRepresentation::Value`], it corresponds to the inner iterators (since inner
132    /// elements can be templated as well).
133    /// For [`WordRepresentation::Template`] it returns the words's placeholder requirements
134    /// as defined.
135    pub fn template_requirements(
136        &self,
137        placeholder_prefix: StorageValueName,
138    ) -> TemplateRequirementsIter<'_> {
139        let placeholder_key =
140            placeholder_prefix.with_suffix(self.name().unwrap_or(&StorageValueName::empty()));
141        match self {
142            // If it's a template, return the corresponding requirements
143            WordRepresentation::Template { description, r#type, .. } => Box::new(iter::once((
144                placeholder_key,
145                PlaceholderTypeRequirement {
146                    description: description.clone(),
147                    r#type: r#type.clone(),
148                },
149            ))),
150            // Otherwise, return inner iterators
151            WordRepresentation::Value { value, .. } => Box::new(
152                value
153                    .iter()
154                    .flat_map(move |felt| felt.template_requirements(placeholder_key.clone())),
155            ),
156        }
157    }
158
159    /// Attempts to convert the [WordRepresentation] into a [Word].
160    ///
161    /// If the representation is a template, the value is retrieved from
162    /// `init_storage_data`, identified by its key. If any of the inner elements
163    /// within the value are a template, they are retrieved in the same way.
164    pub(crate) fn try_build_word(
165        &self,
166        init_storage_data: &InitStorageData,
167        placeholder_prefix: StorageValueName,
168    ) -> Result<Word, AccountComponentTemplateError> {
169        match self {
170            WordRepresentation::Template { name: placeholder_key, r#type, .. } => {
171                let placeholder_path = placeholder_prefix.with_suffix(placeholder_key);
172                let maybe_value = init_storage_data.get(&placeholder_path);
173                if let Some(value) = maybe_value {
174                    let parsed_value = TEMPLATE_REGISTRY
175                        .try_parse_word(r#type, value)
176                        .map_err(AccountComponentTemplateError::StorageValueParsingError)?;
177
178                    Ok(parsed_value)
179                } else {
180                    Err(AccountComponentTemplateError::PlaceholderValueNotProvided(
181                        placeholder_path,
182                    ))
183                }
184            },
185            WordRepresentation::Value { value, name, .. } => {
186                let mut result = [Felt::ZERO; 4];
187
188                for (index, felt_repr) in value.iter().enumerate() {
189                    let placeholder = placeholder_prefix
190                        .clone()
191                        .with_suffix(&name.clone().unwrap_or(StorageValueName::empty()));
192                    result[index] = felt_repr.try_build_felt(init_storage_data, placeholder)?;
193                }
194                // SAFETY: result is guaranteed to have all its 4 indices rewritten
195                Ok(result)
196            },
197        }
198    }
199
200    /// Validates that the defined type exists and all the inner felt types exist as well
201    pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> {
202        // Check that type exists in registry
203        let type_exists = TEMPLATE_REGISTRY.contains_word_type(&self.word_type());
204        if !type_exists {
205            return Err(AccountComponentTemplateError::InvalidType(
206                self.word_type().to_string(),
207                "Word".into(),
208            ));
209        }
210
211        if let Some(felts) = self.value() {
212            for felt in felts {
213                felt.validate()?;
214            }
215        }
216
217        Ok(())
218    }
219}
220
221impl Serializable for WordRepresentation {
222    fn write_into<W: ByteWriter>(&self, target: &mut W) {
223        match self {
224            WordRepresentation::Template { name, description, r#type } => {
225                target.write_u8(0);
226                target.write(name);
227                target.write(description);
228                target.write(r#type);
229            },
230            WordRepresentation::Value { name, description, value } => {
231                target.write_u8(1);
232                target.write(name);
233                target.write(description);
234                target.write(value);
235            },
236        }
237    }
238}
239
240impl Deserializable for WordRepresentation {
241    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
242        let tag = source.read_u8()?;
243        match tag {
244            0 => {
245                let name = StorageValueName::read_from(source)?;
246                let description = Option::<String>::read_from(source)?;
247                let r#type = TemplateType::read_from(source)?;
248                Ok(WordRepresentation::Template { name, description, r#type })
249            },
250            1 => {
251                let name = Option::<StorageValueName>::read_from(source)?;
252                let description = Option::<String>::read_from(source)?;
253                let value = <[FeltRepresentation; 4]>::read_from(source)?;
254                Ok(WordRepresentation::Value { name, description, value })
255            },
256            other => Err(DeserializationError::InvalidValue(format!(
257                "Unknown tag for WordRepresentation: {}",
258                other
259            ))),
260        }
261    }
262}
263
264impl From<[FeltRepresentation; 4]> for WordRepresentation {
265    fn from(value: [FeltRepresentation; 4]) -> Self {
266        WordRepresentation::new_value(value, Option::<StorageValueName>::None)
267    }
268}
269
270impl From<[Felt; 4]> for WordRepresentation {
271    fn from(value: [Felt; 4]) -> Self {
272        WordRepresentation::new_value(
273            value.map(FeltRepresentation::from),
274            Option::<StorageValueName>::None,
275        )
276    }
277}
278
279// FELTS
280// ================================================================================================
281
282/// Supported element representations for a component's storage entries.
283///
284/// Each felt element in a storage entry can either be:
285/// - A concrete value that holds a predefined felt.
286/// - A template that specifies the type of felt expected, with the actual value to be provided
287///   later.
288#[derive(Debug, Clone, PartialEq, Eq)]
289pub enum FeltRepresentation {
290    /// A concrete felt value.
291    ///
292    /// This variant holds a felt that is part of the component's storage.
293    /// The optional name allows for identification, and the description offers additional context.
294    Value {
295        /// An optional identifier for this felt value.
296        name: Option<StorageValueName>,
297        /// An optional explanation of the felt's purpose.
298        description: Option<String>,
299        /// The actual felt value.
300        value: Felt,
301    },
302
303    /// A templated felt element.
304    ///
305    /// This variant specifies the expected type of the felt without providing a concrete value.
306    /// The name is required to uniquely identify the template, and an optional description can
307    /// further clarify its intended use.
308    Template {
309        /// The expected type for this felt element.
310        r#type: TemplateType,
311        /// A unique name for the felt template.
312        name: StorageValueName,
313        /// An optional description that explains the purpose of this template.
314        description: Option<String>,
315    },
316}
317
318impl FeltRepresentation {
319    /// Creates a new [`FeltRepresentation::Value`] variant.
320    pub fn new_value(value: impl Into<Felt>, name: Option<StorageValueName>) -> FeltRepresentation {
321        FeltRepresentation::Value {
322            value: value.into(),
323            name,
324            description: None,
325        }
326    }
327
328    /// Creates a new [`FeltRepresentation::Template`] variant.
329    ///
330    /// The name will be used for identification at the moment of instantiating the componentn.
331    pub fn new_template(r#type: TemplateType, name: StorageValueName) -> FeltRepresentation {
332        FeltRepresentation::Template { name, description: None, r#type }
333    }
334
335    /// Sets the description of the [`FeltRepresentation`] and returns `self`.
336    pub fn with_description(self, description: impl Into<String>) -> Self {
337        match self {
338            FeltRepresentation::Template { r#type, name, .. } => FeltRepresentation::Template {
339                r#type,
340                name,
341                description: Some(description.into()),
342            },
343            FeltRepresentation::Value { name, value, .. } => FeltRepresentation::Value {
344                name,
345                description: Some(description.into()),
346                value,
347            },
348        }
349    }
350
351    /// Returns the type name.
352    pub fn felt_type(&self) -> TemplateType {
353        match self {
354            FeltRepresentation::Template { r#type, .. } => r#type.clone(),
355            FeltRepresentation::Value { .. } => TemplateType::native_felt(),
356        }
357    }
358
359    /// Attempts to convert the [FeltRepresentation] into a [Felt].
360    ///
361    /// If the representation is a template, the value is retrieved from `init_storage_data`,
362    /// identified by its key. Otherwise, the returned value is just the inner element.
363    pub(crate) fn try_build_felt(
364        &self,
365        init_storage_data: &InitStorageData,
366        placeholder_prefix: StorageValueName,
367    ) -> Result<Felt, AccountComponentTemplateError> {
368        match self {
369            FeltRepresentation::Template { name, r#type, .. } => {
370                let placeholder_key = placeholder_prefix.with_suffix(name);
371                let raw_value = init_storage_data.get(&placeholder_key).ok_or(
372                    AccountComponentTemplateError::PlaceholderValueNotProvided(placeholder_key),
373                )?;
374
375                Ok(TEMPLATE_REGISTRY
376                    .try_parse_felt(r#type, raw_value)
377                    .map_err(AccountComponentTemplateError::StorageValueParsingError)?)
378            },
379            FeltRepresentation::Value { value, .. } => Ok(*value),
380        }
381    }
382
383    /// Returns an iterator over the felt's template.
384    ///
385    /// For [`FeltRepresentation::Value`], these is an empty set; for
386    /// [`FeltRepresentation::Template`] it returns the felt's placeholder key based on the
387    /// felt's name within the component description.
388    pub fn template_requirements(
389        &self,
390        placeholder_prefix: StorageValueName,
391    ) -> TemplateRequirementsIter<'_> {
392        match self {
393            FeltRepresentation::Template { name, description, r#type } => Box::new(iter::once((
394                placeholder_prefix.with_suffix(name),
395                PlaceholderTypeRequirement {
396                    description: description.clone(),
397                    r#type: r#type.clone(),
398                },
399            ))),
400            _ => Box::new(iter::empty()),
401        }
402    }
403
404    /// Validates that the defined Felt type exists
405    pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> {
406        // Check that type exists in registry
407        let type_exists = TEMPLATE_REGISTRY.contains_felt_type(&self.felt_type());
408        if !type_exists {
409            return Err(AccountComponentTemplateError::InvalidType(
410                self.felt_type().to_string(),
411                "Felt".into(),
412            ));
413        }
414        Ok(())
415    }
416}
417
418impl From<Felt> for FeltRepresentation {
419    fn from(value: Felt) -> Self {
420        FeltRepresentation::new_value(value, Option::<StorageValueName>::None)
421    }
422}
423
424impl Default for FeltRepresentation {
425    fn default() -> Self {
426        FeltRepresentation::new_value(Felt::default(), Option::<StorageValueName>::None)
427    }
428}
429
430impl Serializable for FeltRepresentation {
431    fn write_into<W: ByteWriter>(&self, target: &mut W) {
432        match self {
433            FeltRepresentation::Value { name, description, value } => {
434                target.write_u8(0);
435                target.write(name);
436                target.write(description);
437                target.write(value);
438            },
439            FeltRepresentation::Template { name, description, r#type } => {
440                target.write_u8(1);
441                target.write(name);
442                target.write(description);
443                target.write(r#type);
444            },
445        }
446    }
447}
448
449impl Deserializable for FeltRepresentation {
450    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
451        let tag = source.read_u8()?;
452        match tag {
453            0 => {
454                let name = Option::<StorageValueName>::read_from(source)?;
455                let description = Option::<String>::read_from(source)?;
456                let value = Felt::read_from(source)?;
457                Ok(FeltRepresentation::Value { value, name, description })
458            },
459            1 => {
460                let name = StorageValueName::read_from(source)?;
461                let description = Option::<String>::read_from(source)?;
462                let r#type = TemplateType::read_from(source)?;
463                Ok(FeltRepresentation::Template { r#type, name, description })
464            },
465            other => Err(DeserializationError::InvalidValue(format!(
466                "Unknown tag for FeltRepresentation: {}",
467                other
468            ))),
469        }
470    }
471}
472
473// MAP REPRESENTATION
474// ================================================================================================
475
476/// Supported map representations for a component's storage entries.
477#[derive(Debug, Clone, PartialEq, Eq)]
478#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))]
479pub struct MapRepresentation {
480    /// The human-readable name of the map slot.
481    name: StorageValueName,
482    /// An optional description for the slot, explaining its purpose.
483    description: Option<String>,
484    /// Storage map entries, consisting of a list of keys associated with their values.
485    entries: Vec<MapEntry>,
486}
487
488impl MapRepresentation {
489    /// Creates a new `MapRepresentation` from a vector of map entries.
490    pub fn new(entries: Vec<MapEntry>, name: impl Into<StorageValueName>) -> Self {
491        Self {
492            entries,
493            name: name.into(),
494            description: None,
495        }
496    }
497
498    /// Sets the description of the [`MapRepresentation`] and returns `self`.
499    pub fn with_description(self, description: impl Into<String>) -> Self {
500        MapRepresentation {
501            name: self.name,
502            entries: self.entries,
503            description: Some(description.into()),
504        }
505    }
506
507    /// Returns an iterator over all of the storage entries' placeholder keys, alongside their
508    /// expected type.
509    pub fn template_requirements(&self) -> TemplateRequirementsIter<'_> {
510        Box::new(
511            self.entries
512                .iter()
513                .flat_map(move |entry| entry.template_requirements(self.name.clone())),
514        )
515    }
516
517    /// Returns a reference to map entries.
518    pub fn entries(&self) -> &[MapEntry] {
519        &self.entries
520    }
521
522    /// Returns a reference to the map's name within the storage metadata.
523    pub fn name(&self) -> &StorageValueName {
524        &self.name
525    }
526
527    /// Returns a reference to the field's description.
528    pub fn description(&self) -> Option<&String> {
529        self.description.as_ref()
530    }
531
532    /// Returns the number of key-value pairs in the map.
533    pub fn len(&self) -> usize {
534        self.entries.len()
535    }
536
537    /// Returns `true` if there are no entries in the map.
538    pub fn is_empty(&self) -> bool {
539        self.entries.is_empty()
540    }
541
542    /// Attempts to convert the [MapRepresentation] into a [StorageMap].
543    ///
544    /// If any of the inner elements are templates, their values are retrieved from
545    /// `init_storage_data`, identified by their key.
546    pub fn try_build_map(
547        &self,
548        init_storage_data: &InitStorageData,
549    ) -> Result<StorageMap, AccountComponentTemplateError> {
550        let entries = self
551            .entries
552            .iter()
553            .map(|map_entry| {
554                let key = map_entry.key().try_build_word(init_storage_data, self.name().clone())?;
555                let value =
556                    map_entry.value().try_build_word(init_storage_data, self.name().clone())?;
557                Ok((key.into(), value))
558            })
559            .collect::<Result<Vec<(Digest, Word)>, _>>()?;
560
561        StorageMap::with_entries(entries)
562            .map_err(|err| AccountComponentTemplateError::StorageMapHasDuplicateKeys(Box::new(err)))
563    }
564
565    /// Validates map keys by checking for duplicates.
566    ///
567    /// Because keys can be represented in a variety of ways, the `to_string()` implementation is
568    /// used to check for duplicates.  
569    pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> {
570        let mut seen_keys = BTreeSet::new();
571        for entry in self.entries() {
572            // Until instantiating the component we can only tell if keys are duplicate for any
573            // predefined entries, so try to build any keys in the map to see if we have
574            // duplicates
575            entry.key().validate()?;
576            entry.value().validate()?;
577            if let Ok(key) = entry
578                .key()
579                .try_build_word(&InitStorageData::default(), StorageValueName::empty())
580            {
581                let key: Digest = key.into();
582                if !seen_keys.insert(key) {
583                    return Err(AccountComponentTemplateError::StorageMapHasDuplicateKeys(
584                        Box::from(format!("key `{key}` is duplicated")),
585                    ));
586                }
587            };
588        }
589        Ok(())
590    }
591}
592
593impl From<MapRepresentation> for Vec<MapEntry> {
594    fn from(value: MapRepresentation) -> Self {
595        value.entries
596    }
597}
598
599impl Serializable for MapRepresentation {
600    fn write_into<W: ByteWriter>(&self, target: &mut W) {
601        self.entries.write_into(target);
602        self.name.write_into(target);
603        self.description.write_into(target);
604    }
605}
606
607impl Deserializable for MapRepresentation {
608    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
609        let entries = Vec::<MapEntry>::read_from(source)?;
610        let name = StorageValueName::read_from(source)?;
611        let description = Option::<String>::read_from(source)?;
612        Ok(Self { entries, name, description })
613    }
614}