miden_protocol/account/component/storage/
init_storage_data.rs1use alloc::collections::BTreeMap;
2use alloc::string::{String, ToString};
3use alloc::vec::Vec;
4
5use thiserror::Error;
6
7use super::StorageValueName;
8use crate::account::StorageSlotName;
9use crate::{Felt, FieldElement, Word};
10
11#[derive(Clone, Debug, PartialEq, Eq)]
17pub enum WordValue {
18 FullyTyped(Word),
20 Atomic(String),
22 Elements([String; 4]),
24}
25
26impl From<Word> for WordValue {
27 fn from(value: Word) -> Self {
28 WordValue::FullyTyped(value)
29 }
30}
31
32impl From<String> for WordValue {
33 fn from(value: String) -> Self {
34 WordValue::Atomic(value)
35 }
36}
37
38impl From<&str> for WordValue {
39 fn from(value: &str) -> Self {
40 WordValue::Atomic(String::from(value))
41 }
42}
43
44impl From<Felt> for WordValue {
48 fn from(value: Felt) -> Self {
50 WordValue::FullyTyped(Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, value]))
51 }
52}
53
54impl From<[Felt; 4]> for WordValue {
55 fn from(value: [Felt; 4]) -> Self {
56 WordValue::FullyTyped(Word::from(value))
57 }
58}
59
60#[derive(Clone, Debug, Default)]
69pub struct InitStorageData {
70 value_entries: BTreeMap<StorageValueName, WordValue>,
72 map_entries: BTreeMap<StorageSlotName, Vec<(WordValue, WordValue)>>,
74}
75
76impl InitStorageData {
77 pub fn new(
86 value_entries: BTreeMap<StorageValueName, WordValue>,
87 map_entries: BTreeMap<StorageSlotName, Vec<(WordValue, WordValue)>>,
88 ) -> Result<Self, InitStorageDataError> {
89 for slot_name in map_entries.keys() {
91 if value_entries.keys().any(|v| v.slot_name() == slot_name) {
92 return Err(InitStorageDataError::ConflictingEntries(slot_name.as_str().into()));
93 }
94 }
95
96 for value_name in value_entries.keys() {
98 if value_name.field_name().is_none() {
99 let has_field_entries = value_entries.keys().any(|other| {
101 other.slot_name() == value_name.slot_name() && other.field_name().is_some()
102 });
103 if has_field_entries {
104 return Err(InitStorageDataError::ConflictingEntries(
105 value_name.slot_name().as_str().into(),
106 ));
107 }
108 }
109 }
110
111 Ok(InitStorageData { value_entries, map_entries })
112 }
113
114 pub fn values(&self) -> &BTreeMap<StorageValueName, WordValue> {
116 &self.value_entries
117 }
118
119 pub fn maps(&self) -> &BTreeMap<StorageSlotName, Vec<(WordValue, WordValue)>> {
121 &self.map_entries
122 }
123
124 pub fn value_entry(&self, name: &StorageValueName) -> Option<&WordValue> {
126 self.value_entries.get(name)
127 }
128
129 pub fn slot_value_entry(&self, slot_name: &StorageSlotName) -> Option<&WordValue> {
131 let name = StorageValueName::from_slot_name(slot_name);
132 self.value_entries.get(&name)
133 }
134
135 pub fn map_entries(&self, slot_name: &StorageSlotName) -> Option<&Vec<(WordValue, WordValue)>> {
137 self.map_entries.get(slot_name)
138 }
139
140 pub fn has_value_entries_for_slot(&self, slot_name: &StorageSlotName) -> bool {
142 self.value_entries.keys().any(|name| name.slot_name() == slot_name)
143 }
144
145 pub fn has_field_entries_for_slot(&self, slot_name: &StorageSlotName) -> bool {
147 self.value_entries
148 .keys()
149 .any(|name| name.slot_name() == slot_name && name.field_name().is_some())
150 }
151
152 pub fn insert_value(
165 &mut self,
166 name: StorageValueName,
167 value: impl Into<WordValue>,
168 ) -> Result<(), InitStorageDataError> {
169 if self.value_entries.contains_key(&name) {
170 return Err(InitStorageDataError::DuplicateKey(name.to_string()));
171 }
172 if self.map_entries.contains_key(name.slot_name()) {
173 return Err(InitStorageDataError::ConflictingEntries(name.slot_name().as_str().into()));
174 }
175 self.value_entries.insert(name, value.into());
176 Ok(())
177 }
178
179 pub fn set_value(
183 &mut self,
184 name: StorageValueName,
185 value: impl Into<WordValue>,
186 ) -> Result<(), InitStorageDataError> {
187 if self.map_entries.contains_key(name.slot_name()) {
188 return Err(InitStorageDataError::ConflictingEntries(name.slot_name().as_str().into()));
189 }
190 self.value_entries.insert(name, value.into());
191 Ok(())
192 }
193
194 pub fn insert_map_entry(
198 &mut self,
199 slot_name: StorageSlotName,
200 key: impl Into<WordValue>,
201 value: impl Into<WordValue>,
202 ) -> Result<(), InitStorageDataError> {
203 if self.has_value_entries_for_slot(&slot_name) {
204 return Err(InitStorageDataError::ConflictingEntries(slot_name.as_str().into()));
205 }
206
207 let key = key.into();
208 if let Some(entries) = self.map_entries.get(&slot_name)
209 && entries.iter().any(|(existing_key, _)| existing_key == &key)
210 {
211 return Err(InitStorageDataError::DuplicateKey(format!(
212 "{}[{key:?}]",
213 slot_name.as_str()
214 )));
215 }
216
217 self.map_entries.entry(slot_name).or_default().push((key, value.into()));
218 Ok(())
219 }
220
221 pub fn set_map_values(
225 &mut self,
226 slot_name: StorageSlotName,
227 entries: Vec<(WordValue, WordValue)>,
228 ) -> Result<(), InitStorageDataError> {
229 if self.has_value_entries_for_slot(&slot_name) {
230 return Err(InitStorageDataError::ConflictingEntries(slot_name.as_str().into()));
231 }
232 self.map_entries.insert(slot_name, entries);
233 Ok(())
234 }
235
236 pub fn merge_with(&mut self, other: InitStorageData) {
239 self.value_entries.extend(other.value_entries);
240 for (slot_name, entries) in other.map_entries {
241 self.map_entries.entry(slot_name).or_default().extend(entries);
242 }
243 }
244
245 pub fn merge_from(&mut self, other: InitStorageData) {
248 self.merge_with(other);
249 }
250}
251
252#[derive(Debug, Error, PartialEq, Eq)]
257pub enum InitStorageDataError {
258 #[error("duplicate init key `{0}`")]
259 DuplicateKey(String),
260 #[error("conflicting init entries for `{0}`")]
261 ConflictingEntries(String),
262}