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