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 for WordRepresentation: {}",
251                other
252            ))),
253        }
254    }
255}
256
257impl From<[FeltRepresentation; 4]> for WordRepresentation {
258    fn from(value: [FeltRepresentation; 4]) -> Self {
259        WordRepresentation::new_value(value, Option::<FieldIdentifier>::None)
260    }
261}
262
263impl From<[Felt; 4]> for WordRepresentation {
264    fn from(value: [Felt; 4]) -> Self {
265        WordRepresentation::new_value(
266            value.map(FeltRepresentation::from),
267            Option::<FieldIdentifier>::None,
268        )
269    }
270}
271
272// FELTS
273// ================================================================================================
274
275/// Supported element representations for a component's storage entries.
276///
277/// Each felt element in a storage entry can either be:
278/// - A concrete value that holds a predefined felt.
279/// - A template that specifies the type of felt expected, with the actual value to be provided
280///   later.
281#[derive(Debug, Clone, PartialEq, Eq)]
282pub enum FeltRepresentation {
283    /// A concrete felt value.
284    ///
285    /// This variant holds a felt that is part of the component's storage.
286    /// The optional name allows for identification, and the description offers additional context.
287    Value {
288        /// An optional identifier for this felt value.
289        /// An optional explanation of the felt's purpose.
290        identifier: Option<FieldIdentifier>,
291        /// The actual felt value.
292        value: Felt,
293    },
294
295    /// A templated felt element.
296    ///
297    /// This variant specifies the expected type of the felt without providing a concrete value.
298    /// The name is required to uniquely identify the template, and an optional description can
299    /// further clarify its intended use.
300    Template {
301        /// The expected type for this felt element.
302        r#type: TemplateType,
303        /// A unique name for the felt template.
304        /// An optional description that explains the purpose of this template.
305        identifier: FieldIdentifier,
306    },
307}
308
309impl FeltRepresentation {
310    /// Creates a new [`FeltRepresentation::Value`] variant.
311    pub fn new_value(value: impl Into<Felt>, name: Option<StorageValueName>) -> Self {
312        FeltRepresentation::Value {
313            value: value.into(),
314            identifier: name.map(FieldIdentifier::with_name),
315        }
316    }
317
318    /// Creates a new [`FeltRepresentation::Template`] variant.
319    ///
320    /// The name will be used for identification at the moment of instantiating the componentn.
321    pub fn new_template(r#type: TemplateType, name: StorageValueName) -> Self {
322        FeltRepresentation::Template {
323            r#type,
324            identifier: FieldIdentifier::with_name(name),
325        }
326    }
327
328    /// Sets the description of the [`FeltRepresentation`] and returns `self`.
329    pub fn with_description(self, description: impl Into<String>) -> Self {
330        match self {
331            FeltRepresentation::Template { r#type, identifier } => FeltRepresentation::Template {
332                r#type,
333                identifier: FieldIdentifier {
334                    name: identifier.name,
335                    description: Some(description.into()),
336                },
337            },
338            FeltRepresentation::Value { identifier, value } => FeltRepresentation::Value {
339                identifier: identifier.map(|id| FieldIdentifier {
340                    name: id.name,
341                    description: Some(description.into()),
342                }),
343                value,
344            },
345        }
346    }
347
348    /// Returns the felt type.
349    pub fn felt_type(&self) -> TemplateType {
350        match self {
351            FeltRepresentation::Template { r#type, .. } => r#type.clone(),
352            FeltRepresentation::Value { .. } => TemplateType::native_felt(),
353        }
354    }
355
356    /// Attempts to convert the [FeltRepresentation] into a [Felt].
357    ///
358    /// If the representation is a template, the value is retrieved from `init_storage_data`,
359    /// identified by its key. Otherwise, the returned value is just the inner element.
360    pub(crate) fn try_build_felt(
361        &self,
362        init_storage_data: &InitStorageData,
363        placeholder_prefix: StorageValueName,
364    ) -> Result<Felt, AccountComponentTemplateError> {
365        match self {
366            FeltRepresentation::Template { identifier, r#type } => {
367                let placeholder_key = placeholder_prefix.with_suffix(&identifier.name);
368                let raw_value = init_storage_data.get(&placeholder_key).ok_or(
369                    AccountComponentTemplateError::PlaceholderValueNotProvided(placeholder_key),
370                )?;
371
372                Ok(TEMPLATE_REGISTRY
373                    .try_parse_felt(r#type, raw_value)
374                    .map_err(AccountComponentTemplateError::StorageValueParsingError)?)
375            },
376            FeltRepresentation::Value { value, .. } => Ok(*value),
377        }
378    }
379
380    /// Returns an iterator over the felt's template.
381    ///
382    /// For [`FeltRepresentation::Value`], these is an empty set; for
383    /// [`FeltRepresentation::Template`] it returns the felt's placeholder key based on the
384    /// felt's name within the component description.
385    pub fn template_requirements(
386        &self,
387        placeholder_prefix: StorageValueName,
388    ) -> TemplateRequirementsIter<'_> {
389        match self {
390            FeltRepresentation::Template { identifier, r#type } => Box::new(iter::once((
391                placeholder_prefix.with_suffix(&identifier.name),
392                PlaceholderTypeRequirement {
393                    description: identifier.description.clone(),
394                    r#type: r#type.clone(),
395                },
396            ))),
397            _ => Box::new(iter::empty()),
398        }
399    }
400
401    /// Validates that the defined Felt type exists
402    pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> {
403        // Check that type exists in registry
404        let type_exists = TEMPLATE_REGISTRY.contains_felt_type(&self.felt_type());
405        if !type_exists {
406            return Err(AccountComponentTemplateError::InvalidType(
407                self.felt_type().to_string(),
408                "Felt".into(),
409            ));
410        }
411        Ok(())
412    }
413}
414
415impl From<Felt> for FeltRepresentation {
416    fn from(value: Felt) -> Self {
417        FeltRepresentation::new_value(value, Option::<StorageValueName>::None)
418    }
419}
420
421impl Default for FeltRepresentation {
422    fn default() -> Self {
423        FeltRepresentation::new_value(Felt::default(), Option::<StorageValueName>::None)
424    }
425}
426
427impl Serializable for FeltRepresentation {
428    fn write_into<W: ByteWriter>(&self, target: &mut W) {
429        match self {
430            FeltRepresentation::Value { identifier, value } => {
431                target.write_u8(0);
432                target.write(identifier);
433                target.write(value);
434            },
435            FeltRepresentation::Template { identifier, r#type } => {
436                target.write_u8(1);
437                target.write(identifier);
438                target.write(r#type);
439            },
440        }
441    }
442}
443
444impl Deserializable for FeltRepresentation {
445    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
446        let tag = source.read_u8()?;
447        match tag {
448            0 => {
449                let identifier = Option::<FieldIdentifier>::read_from(source)?;
450                let value = Felt::read_from(source)?;
451                Ok(FeltRepresentation::Value { value, identifier })
452            },
453            1 => {
454                let identifier = FieldIdentifier::read_from(source)?;
455                let r#type = TemplateType::read_from(source)?;
456                Ok(FeltRepresentation::Template { r#type, identifier })
457            },
458            other => Err(DeserializationError::InvalidValue(format!(
459                "Unknown tag for FeltRepresentation: {}",
460                other
461            ))),
462        }
463    }
464}
465
466// MAP REPRESENTATION
467// ================================================================================================
468
469/// Supported map representations for a component's storage entries.
470#[derive(Debug, Clone, PartialEq, Eq)]
471#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))]
472pub struct MapRepresentation {
473    /// The human-readable name of the map slot.
474    /// An optional description for the slot, explaining its purpose.
475    identifier: FieldIdentifier,
476    /// Storage map entries, consisting of a list of keys associated with their values.
477    entries: Vec<MapEntry>,
478}
479
480impl MapRepresentation {
481    /// Creates a new `MapRepresentation` from a vector of map entries.
482    pub fn new(entries: Vec<MapEntry>, name: impl Into<StorageValueName>) -> Self {
483        Self {
484            entries,
485            identifier: FieldIdentifier::with_name(name.into()),
486        }
487    }
488
489    /// Sets the description of the [`MapRepresentation`] and returns `self`.
490    pub fn with_description(self, description: impl Into<String>) -> Self {
491        MapRepresentation {
492            entries: self.entries,
493            identifier: FieldIdentifier {
494                name: self.identifier.name,
495                description: Some(description.into()),
496            },
497        }
498    }
499
500    /// Returns an iterator over all of the storage entries' placeholder keys, alongside their
501    /// expected type.
502    pub fn template_requirements(&self) -> TemplateRequirementsIter<'_> {
503        Box::new(
504            self.entries
505                .iter()
506                .flat_map(move |entry| entry.template_requirements(self.identifier.name.clone())),
507        )
508    }
509
510    /// Returns a reference to map entries.
511    pub fn entries(&self) -> &[MapEntry] {
512        &self.entries
513    }
514
515    /// Returns a reference to the map's name within the storage metadata.
516    pub fn name(&self) -> &StorageValueName {
517        &self.identifier.name
518    }
519
520    /// Returns a reference to the field's description.
521    pub fn description(&self) -> Option<&String> {
522        self.identifier.description.as_ref()
523    }
524
525    /// Returns the number of key-value pairs in the map.
526    pub fn len(&self) -> usize {
527        self.entries.len()
528    }
529
530    /// Returns `true` if there are no entries in the map.
531    pub fn is_empty(&self) -> bool {
532        self.entries.is_empty()
533    }
534
535    /// Attempts to convert the [MapRepresentation] into a [StorageMap].
536    ///
537    /// If any of the inner elements are templates, their values are retrieved from
538    /// `init_storage_data`, identified by their key.
539    pub fn try_build_map(
540        &self,
541        init_storage_data: &InitStorageData,
542    ) -> Result<StorageMap, AccountComponentTemplateError> {
543        let entries = self
544            .entries
545            .iter()
546            .map(|map_entry| {
547                let key = map_entry
548                    .key()
549                    .try_build_word(init_storage_data, self.identifier.name.clone())?;
550                let value = map_entry
551                    .value()
552                    .try_build_word(init_storage_data, self.identifier.name.clone())?;
553                Ok((key.into(), value))
554            })
555            .collect::<Result<Vec<(Digest, Word)>, _>>()?;
556
557        StorageMap::with_entries(entries)
558            .map_err(|err| AccountComponentTemplateError::StorageMapHasDuplicateKeys(Box::new(err)))
559    }
560
561    /// Validates map keys by checking for duplicates.
562    ///
563    /// Because keys can be represented in a variety of ways, the `to_string()` implementation is
564    /// used to check for duplicates.  
565    pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> {
566        let mut seen_keys = BTreeSet::new();
567        for entry in self.entries() {
568            entry.key().validate()?;
569            entry.value().validate()?;
570            if let Ok(key) = entry
571                .key()
572                .try_build_word(&InitStorageData::default(), StorageValueName::empty())
573            {
574                let key: Digest = key.into();
575                if !seen_keys.insert(key) {
576                    return Err(AccountComponentTemplateError::StorageMapHasDuplicateKeys(
577                        Box::from(format!("key `{key}` is duplicated")),
578                    ));
579                }
580            };
581        }
582        Ok(())
583    }
584}
585
586impl From<MapRepresentation> for Vec<MapEntry> {
587    fn from(value: MapRepresentation) -> Self {
588        value.entries
589    }
590}
591
592impl Serializable for MapRepresentation {
593    fn write_into<W: ByteWriter>(&self, target: &mut W) {
594        self.entries.write_into(target);
595        target.write(&self.identifier);
596    }
597}
598
599impl Deserializable for MapRepresentation {
600    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
601        let entries = Vec::<MapEntry>::read_from(source)?;
602        let identifier = FieldIdentifier::read_from(source)?;
603        Ok(Self { entries, identifier })
604    }
605}
606
607// MULTI-WORD VALUE
608// ================================================================================================
609
610/// Defines how multi-slot values are represented within the component's storage description.
611///
612/// Each multi-word value representation can be:
613/// - A predefined value that may contain a hardcoded word or a mix of fixed and templated felts.
614#[derive(Debug, Clone, PartialEq, Eq)]
615pub enum MultiWordRepresentation {
616    // TODO: Once there are multi-slot template types, add a MultiWordRepresentation::Template
617    // here
618    Value {
619        /// The human-readable name of this multi-slot entry.
620        identifier: FieldIdentifier,
621        /// A list of values to fill the logical slot, with a length equal to the number of slots.
622        values: Vec<[FeltRepresentation; 4]>,
623    },
624}
625
626impl MultiWordRepresentation {
627    /// Returns the number of words in this representation.
628    pub fn num_words(&self) -> usize {
629        match self {
630            MultiWordRepresentation::Value { values, .. } => values.len(),
631        }
632    }
633
634    /// Validates the multi-slot value.
635    pub fn validate(&self) -> Result<(), AccountComponentTemplateError> {
636        match self {
637            MultiWordRepresentation::Value { values, .. } => {
638                for slot_word in values {
639                    for felt_in_slot in slot_word {
640                        felt_in_slot.validate()?;
641                    }
642                }
643            },
644        }
645        Ok(())
646    }
647}
648
649impl Serializable for MultiWordRepresentation {
650    fn write_into<W: ByteWriter>(&self, target: &mut W) {
651        match self {
652            MultiWordRepresentation::Value { identifier, values } => {
653                target.write_u8(0u8);
654                target.write(identifier);
655                target.write(values);
656            },
657        }
658    }
659}
660impl Deserializable for MultiWordRepresentation {
661    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
662        let variant_tag = source.read_u8()?;
663        match variant_tag {
664            0 => {
665                let identifier: FieldIdentifier = source.read()?;
666                let values: Vec<[FeltRepresentation; 4]> = source.read()?;
667                Ok(MultiWordRepresentation::Value { identifier, values })
668            },
669            _ => Err(DeserializationError::InvalidValue(format!(
670                "unknown variant tag `{}` for MultiWordRepresentation",
671                variant_tag
672            ))),
673        }
674    }
675}