Skip to main content

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

1use alloc::boxed::Box;
2use alloc::collections::BTreeMap;
3use alloc::string::String;
4use alloc::vec::Vec;
5
6use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable};
7use miden_processor::DeserializationError;
8
9use super::super::{InitStorageData, StorageValueName};
10use super::{WordSchema, parse_storage_value_with_schema, validate_description_ascii};
11use crate::Word;
12use crate::account::{StorageMap, StorageMapKey, StorageSlotName};
13use crate::errors::ComponentMetadataError;
14
15// MAP SLOT SCHEMA
16// ================================================================================================
17
18/// Describes the schema for a storage map slot.
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct MapSlotSchema {
21    description: Option<String>,
22    default_values: Option<BTreeMap<Word, Word>>,
23    key_schema: WordSchema,
24    value_schema: WordSchema,
25}
26
27impl MapSlotSchema {
28    pub fn new(
29        description: Option<String>,
30        default_values: Option<BTreeMap<Word, Word>>,
31        key_schema: WordSchema,
32        value_schema: WordSchema,
33    ) -> Self {
34        Self {
35            description,
36            default_values,
37            key_schema,
38            value_schema,
39        }
40    }
41
42    pub fn description(&self) -> Option<&String> {
43        self.description.as_ref()
44    }
45
46    /// Builds a [`StorageMap`] from the provided initialization data.
47    ///
48    /// Merges any default values with entries from the init data, validating that the data
49    /// contains map entries (not a direct value or field entries).
50    pub fn try_build_map(
51        &self,
52        init_storage_data: &InitStorageData,
53        slot_name: &StorageSlotName,
54    ) -> Result<StorageMap, ComponentMetadataError> {
55        let mut entries = self.default_values.clone().unwrap_or_default();
56        let slot_prefix = StorageValueName::from_slot_name(slot_name);
57
58        if init_storage_data.slot_value_entry(slot_name).is_some() {
59            return Err(ComponentMetadataError::InvalidInitStorageValue(
60                slot_prefix,
61                "expected a map, got a value".into(),
62            ));
63        }
64        if init_storage_data.has_field_entries_for_slot(slot_name) {
65            return Err(ComponentMetadataError::InvalidInitStorageValue(
66                slot_prefix,
67                "expected a map, got field entries".into(),
68            ));
69        }
70        if let Some(init_entries) = init_storage_data.map_entries(slot_name) {
71            let mut parsed_entries = Vec::with_capacity(init_entries.len());
72            for (raw_key, raw_value) in init_entries.iter() {
73                let key = parse_storage_value_with_schema(&self.key_schema, raw_key, &slot_prefix)?;
74                let value =
75                    parse_storage_value_with_schema(&self.value_schema, raw_value, &slot_prefix)?;
76
77                parsed_entries.push((key, value));
78            }
79
80            for (key, value) in parsed_entries.iter() {
81                entries.insert(*key, *value);
82            }
83        }
84
85        if entries.is_empty() {
86            return Ok(StorageMap::new());
87        }
88
89        StorageMap::with_entries(
90            entries.into_iter().map(|(key, value)| (StorageMapKey::from_raw(key), value)),
91        )
92        .map_err(|err| ComponentMetadataError::StorageMapHasDuplicateKeys(Box::new(err)))
93    }
94
95    pub fn key_schema(&self) -> &WordSchema {
96        &self.key_schema
97    }
98
99    pub fn value_schema(&self) -> &WordSchema {
100        &self.value_schema
101    }
102
103    pub fn default_values(&self) -> Option<BTreeMap<Word, Word>> {
104        self.default_values.clone()
105    }
106
107    pub(super) fn write_into_with_optional_defaults<W: ByteWriter>(
108        &self,
109        target: &mut W,
110        include_defaults: bool,
111    ) {
112        target.write(&self.description);
113        let default_values = if include_defaults {
114            self.default_values.clone()
115        } else {
116            None
117        };
118        target.write(&default_values);
119        self.key_schema.write_into_with_optional_defaults(target, include_defaults);
120        self.value_schema.write_into_with_optional_defaults(target, include_defaults);
121    }
122
123    pub(super) fn validate(&self) -> Result<(), ComponentMetadataError> {
124        if let Some(description) = self.description.as_deref() {
125            validate_description_ascii(description)?;
126        }
127        self.key_schema.validate()?;
128        self.value_schema.validate()?;
129        Ok(())
130    }
131}
132
133impl Serializable for MapSlotSchema {
134    fn write_into<W: ByteWriter>(&self, target: &mut W) {
135        self.write_into_with_optional_defaults(target, true);
136    }
137}
138
139impl Deserializable for MapSlotSchema {
140    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
141        let description = Option::<String>::read_from(source)?;
142        let default_values = Option::<BTreeMap<Word, Word>>::read_from(source)?;
143        let key_schema = WordSchema::read_from(source)?;
144        let value_schema = WordSchema::read_from(source)?;
145        Ok(MapSlotSchema::new(description, default_values, key_schema, value_schema))
146    }
147}