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