miden_protocol/account/component/storage/schema/
mod.rs1use alloc::collections::BTreeMap;
2use alloc::string::ToString;
3use alloc::vec::Vec;
4
5use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable};
6use miden_processor::DeserializationError;
7
8use super::type_registry::SchemaRequirement;
9use super::{InitStorageData, StorageValueName};
10use crate::account::{StorageSlot, StorageSlotName};
11use crate::crypto::utils::bytes_to_elements_with_padding;
12use crate::errors::ComponentMetadataError;
13use crate::{Hasher, Word};
14
15mod felt;
16pub use felt::FeltSchema;
17
18mod map_slot;
19pub use map_slot::MapSlotSchema;
20
21mod parse;
22pub(crate) use parse::parse_storage_value_with_schema;
23
24mod slot;
25pub use slot::StorageSlotSchema;
26
27mod value_slot;
28pub use value_slot::ValueSlotSchema;
29
30mod word;
31pub use word::WordSchema;
32
33#[cfg(test)]
34mod tests;
35
36#[derive(Debug, Clone, Default, PartialEq, Eq)]
41pub struct StorageSchema {
42 slots: BTreeMap<StorageSlotName, StorageSlotSchema>,
43}
44
45impl StorageSchema {
46 pub fn new(
53 slots: impl IntoIterator<Item = (StorageSlotName, StorageSlotSchema)>,
54 ) -> Result<Self, ComponentMetadataError> {
55 let mut map = BTreeMap::new();
56 for (slot_name, schema) in slots {
57 if map.insert(slot_name.clone(), schema).is_some() {
58 return Err(ComponentMetadataError::DuplicateSlotName(slot_name));
59 }
60 }
61
62 let schema = Self { slots: map };
63 schema.validate()?;
64 Ok(schema)
65 }
66
67 pub fn iter(&self) -> impl Iterator<Item = (&StorageSlotName, &StorageSlotSchema)> {
69 self.slots.iter()
70 }
71
72 pub fn slots(&self) -> &BTreeMap<StorageSlotName, StorageSlotSchema> {
74 &self.slots
75 }
76
77 pub fn build_storage_slots(
79 &self,
80 init_storage_data: &InitStorageData,
81 ) -> Result<Vec<StorageSlot>, ComponentMetadataError> {
82 self.slots
83 .iter()
84 .map(|(slot_name, schema)| schema.try_build_storage_slot(slot_name, init_storage_data))
85 .collect()
86 }
87
88 pub fn commitment(&self) -> Word {
92 let mut bytes = Vec::new();
93 self.write_into_with_optional_defaults(&mut bytes, false);
94 let elements = bytes_to_elements_with_padding(&bytes);
95 Hasher::hash_elements(&elements)
96 }
97
98 pub fn schema_requirements(
103 &self,
104 ) -> Result<BTreeMap<StorageValueName, SchemaRequirement>, ComponentMetadataError> {
105 let mut requirements = BTreeMap::new();
106 for (slot_name, schema) in self.slots.iter() {
107 schema.collect_init_value_requirements(slot_name, &mut requirements)?;
108 }
109 Ok(requirements)
110 }
111
112 fn write_into_with_optional_defaults<W: ByteWriter>(
115 &self,
116 target: &mut W,
117 include_defaults: bool,
118 ) {
119 target.write_u16(self.slots.len() as u16);
120 for (slot_name, schema) in self.slots.iter() {
121 target.write(slot_name);
122 schema.write_into_with_optional_defaults(target, include_defaults);
123 }
124 }
125
126 fn validate(&self) -> Result<(), ComponentMetadataError> {
128 let mut init_values = BTreeMap::new();
129
130 for (slot_name, schema) in self.slots.iter() {
131 schema.validate()?;
132 schema.collect_init_value_requirements(slot_name, &mut init_values)?;
133 }
134
135 Ok(())
136 }
137}
138
139impl Serializable for StorageSchema {
140 fn write_into<W: ByteWriter>(&self, target: &mut W) {
141 self.write_into_with_optional_defaults(target, true);
142 }
143}
144
145impl Deserializable for StorageSchema {
146 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
147 let num_entries = source.read_u16()? as usize;
148 let mut fields = BTreeMap::new();
149
150 for _ in 0..num_entries {
151 let slot_name = StorageSlotName::read_from(source)?;
152 let schema = StorageSlotSchema::read_from(source)?;
153
154 if fields.insert(slot_name.clone(), schema).is_some() {
155 return Err(DeserializationError::InvalidValue(format!(
156 "duplicate slot name in storage schema: {slot_name}",
157 )));
158 }
159 }
160
161 let schema = StorageSchema::new(fields)
162 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
163 Ok(schema)
164 }
165}
166
167pub(super) fn validate_description_ascii(description: &str) -> Result<(), ComponentMetadataError> {
168 if description.is_ascii() {
169 Ok(())
170 } else {
171 Err(ComponentMetadataError::InvalidSchema(
172 "description must contain only ASCII characters".to_string(),
173 ))
174 }
175}