1use alloc::collections::BTreeMap;
2use alloc::string::{String, ToString};
3use alloc::vec::Vec;
4use core::fmt;
5
6use miden_core::{Felt, Word};
7use serde::de::value::MapAccessDeserializer;
8use serde::de::{self, Error, MapAccess, SeqAccess, Visitor};
9use serde::ser::{SerializeMap, SerializeStruct};
10use serde::{Deserialize, Deserializer, Serialize, Serializer};
11use thiserror::Error;
12
13use super::placeholder::TemplateType;
14use super::{
15 FeltRepresentation,
16 InitStorageData,
17 MapEntry,
18 MapRepresentation,
19 MultiWordRepresentation,
20 StorageEntry,
21 StorageValueNameError,
22 WordRepresentation,
23};
24use crate::account::component::FieldIdentifier;
25use crate::account::component::template::storage::placeholder::{TEMPLATE_REGISTRY, TemplateFelt};
26use crate::account::{AccountComponentMetadata, StorageValueName};
27use crate::errors::AccountComponentTemplateError;
28
29impl AccountComponentMetadata {
33 pub fn from_toml(toml_string: &str) -> Result<Self, AccountComponentTemplateError> {
42 let component: AccountComponentMetadata = toml::from_str(toml_string)
43 .map_err(AccountComponentTemplateError::TomlDeserializationError)?;
44
45 component.validate()?;
46 Ok(component)
47 }
48
49 pub fn to_toml(&self) -> Result<String, AccountComponentTemplateError> {
51 let toml =
52 toml::to_string(self).map_err(AccountComponentTemplateError::TomlSerializationError)?;
53 Ok(toml)
54 }
55}
56
57impl Serialize for WordRepresentation {
61 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
62 where
63 S: Serializer,
64 {
65 match self {
66 WordRepresentation::Template { identifier, r#type } => {
67 let mut state = serializer.serialize_struct("WordRepresentation", 3)?;
68 state.serialize_field("name", &identifier.name())?;
69 state.serialize_field("description", &identifier.description())?;
70 state.serialize_field("type", r#type)?;
71 state.end()
72 },
73 WordRepresentation::Value { identifier, value } => {
74 let mut state = serializer.serialize_struct("WordRepresentation", 3)?;
75
76 state.serialize_field("name", &identifier.as_ref().map(|id| id.name()))?;
77 state.serialize_field(
78 "description",
79 &identifier.as_ref().map(|id| id.description()),
80 )?;
81 state.serialize_field("value", value)?;
82 state.end()
83 },
84 }
85 }
86}
87
88impl<'de> Deserialize<'de> for WordRepresentation {
89 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
90 where
91 D: Deserializer<'de>,
92 {
93 struct WordRepresentationVisitor;
94
95 impl<'de> Visitor<'de> for WordRepresentationVisitor {
96 type Value = WordRepresentation;
97
98 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
99 formatter.write_str("a string or a map representing a WordRepresentation")
100 }
101
102 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
104 where
105 E: Error,
106 {
107 let parsed_value = Word::parse(value).map_err(|_err| {
108 E::invalid_value(
109 serde::de::Unexpected::Str(value),
110 &"a valid hexadecimal string",
111 )
112 })?;
113 Ok(<[Felt; _]>::from(&parsed_value).into())
114 }
115
116 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
117 where
118 E: Error,
119 {
120 self.visit_str(&value)
121 }
122
123 fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
124 where
125 A: SeqAccess<'de>,
126 {
127 let elements: Vec<FeltRepresentation> =
129 Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(seq))?;
130 if elements.len() != 4 {
131 return Err(Error::invalid_length(
132 elements.len(),
133 &"expected an array of 4 elements",
134 ));
135 }
136 let value: [FeltRepresentation; 4] =
137 elements.try_into().expect("length was checked");
138 Ok(WordRepresentation::new_value(value, None))
139 }
140
141 fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
142 where
143 M: MapAccess<'de>,
144 {
145 #[derive(Deserialize, Debug)]
146 struct WordRepresentationHelper {
147 name: Option<String>,
148 description: Option<String>,
149 value: Option<[FeltRepresentation; 4]>,
151 #[serde(rename = "type")]
152 r#type: Option<TemplateType>,
153 }
154
155 let helper =
156 WordRepresentationHelper::deserialize(MapAccessDeserializer::new(map))?;
157
158 if let Some(value) = helper.value {
159 let identifier = helper
160 .name
161 .map(|n| parse_field_identifier::<M::Error>(n, helper.description.clone()))
162 .transpose()?;
163 Ok(WordRepresentation::Value { value, identifier })
164 } else {
165 let identifier = expect_parse_field_identifier::<M::Error>(
167 helper.name,
168 helper.description,
169 "word template",
170 )?;
171 let r#type = helper.r#type.unwrap_or_else(TemplateType::native_word);
172 Ok(WordRepresentation::Template { r#type, identifier })
173 }
174 }
175 }
176
177 deserializer.deserialize_any(WordRepresentationVisitor)
178 }
179}
180
181impl Serialize for FeltRepresentation {
185 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
186 where
187 S: Serializer,
188 {
189 match self {
190 FeltRepresentation::Value { identifier, value } => {
191 let hex = value.to_string();
192 if identifier.is_none() {
193 serializer.serialize_str(&hex)
194 } else {
195 let mut state = serializer.serialize_struct("FeltRepresentation", 3)?;
196 if let Some(id) = identifier {
197 state.serialize_field("name", &id.name)?;
198 state.serialize_field("description", &id.description)?;
199 }
200 state.serialize_field("value", &hex)?;
201 state.end()
202 }
203 },
204 FeltRepresentation::Template { identifier, r#type } => {
205 let mut state = serializer.serialize_struct("FeltRepresentation", 3)?;
206 state.serialize_field("name", &identifier.name)?;
207 state.serialize_field("description", &identifier.description)?;
208 state.serialize_field("type", r#type)?;
209 state.end()
210 },
211 }
212 }
213}
214
215impl<'de> Deserialize<'de> for FeltRepresentation {
216 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
217 where
218 D: Deserializer<'de>,
219 {
220 #[derive(Deserialize)]
226 #[serde(untagged)]
227 enum Intermediate {
228 Map {
229 name: Option<String>,
230 description: Option<String>,
231 #[serde(default)]
232 value: Option<String>,
233 #[serde(rename = "type")]
234 r#type: Option<TemplateType>,
235 },
236 Scalar(String),
237 }
238
239 let intermediate = Intermediate::deserialize(deserializer)?;
240 match intermediate {
241 Intermediate::Scalar(s) => {
242 let felt = Felt::parse_felt(&s)
243 .map_err(|e| D::Error::custom(format!("failed to parse Felt: {e}")))?;
244 Ok(FeltRepresentation::Value { identifier: None, value: felt })
245 },
246 Intermediate::Map { name, description, value, r#type } => {
247 let felt_type = r#type.unwrap_or_else(TemplateType::native_felt);
249 if let Some(val_str) = value {
250 let felt =
252 TEMPLATE_REGISTRY.try_parse_felt(&felt_type, &val_str).map_err(|e| {
253 D::Error::custom(format!("failed to parse {felt_type} as Felt: {e}"))
254 })?;
255 let identifier = name
256 .map(|n| parse_field_identifier::<D::Error>(n, description.clone()))
257 .transpose()?;
258 Ok(FeltRepresentation::Value { identifier, value: felt })
259 } else {
260 let identifier = expect_parse_field_identifier::<D::Error>(
262 name,
263 description,
264 "map template",
265 )?;
266 Ok(FeltRepresentation::Template { r#type: felt_type, identifier })
267 }
268 },
269 }
270 }
271}
272
273#[derive(Debug, Deserialize, Serialize)]
278#[serde(untagged)]
279enum StorageValues {
280 Words(Vec<[FeltRepresentation; 4]>),
282 MapEntries(Vec<MapEntry>),
284}
285
286#[derive(Default, Debug, Deserialize, Serialize)]
290struct RawStorageEntry {
291 #[serde(flatten)]
292 identifier: Option<FieldIdentifier>,
293 slot: Option<u8>,
294 slots: Option<Vec<u8>>,
295 #[serde(rename = "type")]
296 word_type: Option<TemplateType>,
297 value: Option<[FeltRepresentation; 4]>,
298 values: Option<StorageValues>,
299}
300
301impl From<StorageEntry> for RawStorageEntry {
302 fn from(entry: StorageEntry) -> Self {
303 match entry {
304 StorageEntry::Value { slot, word_entry } => match word_entry {
305 WordRepresentation::Value { identifier, value } => RawStorageEntry {
306 slot: Some(slot),
307 identifier,
308 value: Some(value),
309 ..Default::default()
310 },
311 WordRepresentation::Template { identifier, r#type } => RawStorageEntry {
312 slot: Some(slot),
313 identifier: Some(identifier),
314 word_type: Some(r#type),
315 ..Default::default()
316 },
317 },
318 StorageEntry::Map { slot, map } => match map {
319 MapRepresentation::Value { identifier, entries } => RawStorageEntry {
320 slot: Some(slot),
321 identifier: Some(FieldIdentifier {
322 name: identifier.name,
323 description: identifier.description,
324 }),
325 values: Some(StorageValues::MapEntries(entries)),
326 ..Default::default()
327 },
328 MapRepresentation::Template { identifier } => RawStorageEntry {
329 slot: Some(slot),
330 identifier: Some(FieldIdentifier {
331 name: identifier.name,
332 description: identifier.description,
333 }),
334 word_type: Some(TemplateType::storage_map()),
335 ..Default::default()
336 },
337 },
338 StorageEntry::MultiSlot { slots, word_entries } => match word_entries {
339 MultiWordRepresentation::Value { identifier, values } => RawStorageEntry {
340 slot: None,
341 identifier: Some(identifier),
342 slots: Some(slots.collect()),
343 values: Some(StorageValues::Words(values)),
344 ..Default::default()
345 },
346 },
347 }
348 }
349}
350
351impl Serialize for StorageEntry {
352 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
353 where
354 S: Serializer,
355 {
356 let raw_storage_entry: RawStorageEntry = self.clone().into();
357 raw_storage_entry.serialize(serializer)
358 }
359}
360
361impl<'de> Deserialize<'de> for StorageEntry {
362 fn deserialize<D>(deserializer: D) -> Result<StorageEntry, D::Error>
363 where
364 D: Deserializer<'de>,
365 {
366 let raw = RawStorageEntry::deserialize(deserializer)?;
367
368 if let Some(word_entry) = raw.value {
369 let slot = raw.slot.ok_or_else(|| missing_field_for("slot", "value entry"))?;
371 let identifier = raw.identifier;
372 Ok(StorageEntry::Value {
373 slot,
374 word_entry: WordRepresentation::Value { value: word_entry, identifier },
375 })
376 } else if let Some(StorageValues::MapEntries(map_entries)) = raw.values {
377 let identifier =
379 raw.identifier.ok_or_else(|| missing_field_for("identifier", "map entry"))?;
380 let name = identifier.name;
381 let slot = raw.slot.ok_or_else(|| missing_field_for("slot", "map entry"))?;
382 if let Some(word_type) = raw.word_type.clone()
383 && word_type != TemplateType::storage_map()
384 {
385 return Err(D::Error::custom(
386 "map storage entries with `values` must have `type = \"map\"`",
387 ));
388 }
389 let mut map = MapRepresentation::new_value(map_entries, name);
390 if let Some(desc) = identifier.description {
391 map = map.with_description(desc);
392 }
393 Ok(StorageEntry::Map { slot, map })
394 } else if let Some(word_type) = raw.word_type.clone()
395 && word_type == TemplateType::storage_map()
396 {
397 let identifier =
398 raw.identifier.ok_or_else(|| missing_field_for("identifier", "map entry"))?;
399 let slot = raw.slot.ok_or_else(|| missing_field_for("slot", "map entry"))?;
400 let FieldIdentifier { name, description } = identifier;
401 let mut map = MapRepresentation::new_template(name);
402 if let Some(desc) = description {
403 map = map.with_description(desc);
404 }
405 Ok(StorageEntry::Map { slot, map })
406 } else if let Some(StorageValues::Words(values)) = raw.values {
407 let identifier = raw
408 .identifier
409 .ok_or_else(|| missing_field_for("identifier", "multislot entry"))?;
410
411 let mut slots =
412 raw.slots.ok_or_else(|| missing_field_for("slots", "multislot entry"))?;
413
414 slots.sort_unstable();
416 for pair in slots.windows(2) {
417 if pair[1] != pair[0] + 1 {
418 return Err(serde::de::Error::custom(format!(
419 "`slots` in the `{}` storage entry are not contiguous",
420 identifier.name
421 )));
422 }
423 }
424 let start = slots[0];
425 let end = slots.last().expect("checked validity") + 1;
426 Ok(StorageEntry::new_multislot(identifier, start..end, values))
427 } else if let Some(word_type) = raw.word_type {
428 let slot = raw.slot.ok_or_else(|| missing_field_for("slot", "single-slot entry"))?;
430 let identifier = raw
431 .identifier
432 .ok_or_else(|| missing_field_for("identifier", "single-slot entry"))?;
433 let word_entry = WordRepresentation::Template { r#type: word_type, identifier };
434 Ok(StorageEntry::Value { slot, word_entry })
435 } else {
436 Err(D::Error::custom("placeholder storage entries require the `type` field"))
437 }
438 }
439}
440
441impl InitStorageData {
445 pub fn from_toml(toml_str: &str) -> Result<Self, InitStorageDataError> {
457 let value: toml::Value = toml::from_str(toml_str)?;
458 let mut value_entries = BTreeMap::new();
459 let mut map_entries = BTreeMap::new();
460 Self::flatten_parse_toml_value(
462 StorageValueName::empty(),
463 value,
464 &mut value_entries,
465 &mut map_entries,
466 )?;
467
468 Ok(InitStorageData::new(value_entries, map_entries))
469 }
470
471 fn flatten_parse_toml_value(
477 prefix: StorageValueName,
478 value: toml::Value,
479 value_entries: &mut BTreeMap<StorageValueName, String>,
480 map_entries: &mut BTreeMap<StorageValueName, Vec<(Word, Word)>>,
481 ) -> Result<(), InitStorageDataError> {
482 match value {
483 toml::Value::Table(table) => {
484 if !prefix.as_str().is_empty() && table.is_empty() {
486 return Err(InitStorageDataError::EmptyTable(prefix.as_str().into()));
487 }
488 for (key, val) in table {
489 let new_key = StorageValueName::new(key.to_string())
491 .map_err(InitStorageDataError::InvalidStorageValueName)?;
492 let new_prefix = prefix.clone().with_suffix(&new_key);
493 Self::flatten_parse_toml_value(new_prefix, val, value_entries, map_entries)?;
494 }
495 },
496 toml::Value::Array(items) if items.is_empty() => {
497 if prefix.as_str().is_empty() {
498 return Err(InitStorageDataError::ArraysNotSupported);
499 }
500 map_entries.insert(prefix, Vec::new());
501 },
502 toml::Value::Array(items) => {
503 if prefix.as_str().is_empty()
504 || !items.iter().all(|item| matches!(item, toml::Value::Table(_)))
505 {
506 return Err(InitStorageDataError::ArraysNotSupported);
507 }
508
509 let entries = items
510 .into_iter()
511 .map(parse_map_entry_value)
512 .collect::<Result<Vec<(Word, Word)>, _>>()?;
513 map_entries.insert(prefix, entries);
514 },
515 toml_value => {
516 let value = match toml_value {
518 toml::Value::String(s) => s.clone(),
519 _ => toml_value.to_string(),
520 };
521 value_entries.insert(prefix, value);
522 },
523 }
524 Ok(())
525 }
526}
527
528#[derive(Debug, Error)]
529pub enum InitStorageDataError {
530 #[error("failed to parse TOML")]
531 InvalidToml(#[from] toml::de::Error),
532
533 #[error("empty table encountered for key `{0}`")]
534 EmptyTable(String),
535
536 #[error("invalid input: arrays are not supported")]
537 ArraysNotSupported,
538
539 #[error("invalid storage value name")]
540 InvalidStorageValueName(#[source] StorageValueNameError),
541
542 #[error("invalid map entry: {0}")]
543 InvalidMapEntry(String),
544}
545
546impl Serialize for FieldIdentifier {
547 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
548 where
549 S: Serializer,
550 {
551 let mut map = serializer.serialize_map(Some(2))?;
552 map.serialize_entry("name", &self.name)?;
553 map.serialize_entry("description", &self.description)?;
554 map.end()
555 }
556}
557
558struct FieldIdentifierVisitor;
559
560impl<'de> Visitor<'de> for FieldIdentifierVisitor {
561 type Value = FieldIdentifier;
562
563 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
564 formatter.write_str("a map with 'name' and optionally 'description'")
565 }
566
567 fn visit_map<M>(self, mut map: M) -> Result<FieldIdentifier, M::Error>
568 where
569 M: MapAccess<'de>,
570 {
571 let mut name = None;
572 let mut description = None;
573 while let Some(key) = map.next_key::<String>()? {
574 match key.as_str() {
575 "name" => {
576 name = Some(map.next_value()?);
577 },
578 "description" => {
579 let d: String = map.next_value()?;
580 description = if d.trim().is_empty() { None } else { Some(d) };
582 },
583 _ => {
584 let _: de::IgnoredAny = map.next_value()?;
586 },
587 }
588 }
589 let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
590 Ok(FieldIdentifier { name, description })
591 }
592}
593
594impl<'de> Deserialize<'de> for FieldIdentifier {
595 fn deserialize<D>(deserializer: D) -> Result<FieldIdentifier, D::Error>
596 where
597 D: Deserializer<'de>,
598 {
599 deserializer.deserialize_map(FieldIdentifierVisitor)
600 }
601}
602
603fn missing_field_for<E: serde::de::Error>(field: &str, context: &str) -> E {
607 E::custom(format!("missing '{field}' field for {context}"))
608}
609
610fn expect_parse_field_identifier<E: serde::de::Error>(
612 n: Option<String>,
613 description: Option<String>,
614 context: &str,
615) -> Result<FieldIdentifier, E> {
616 let name = n.ok_or_else(|| missing_field_for("name", context))?;
617 parse_field_identifier(name, description)
618}
619
620fn parse_field_identifier<E: serde::de::Error>(
622 n: String,
623 description: Option<String>,
624) -> Result<FieldIdentifier, E> {
625 StorageValueName::new(n)
626 .map_err(|err| E::custom(format!("invalid `name`: {err}")))
627 .map(|storage_name| {
628 if let Some(desc) = description {
629 FieldIdentifier::with_description(storage_name, desc)
630 } else {
631 FieldIdentifier::with_name(storage_name)
632 }
633 })
634}
635
636fn parse_map_entry_value(item: toml::Value) -> Result<(Word, Word), InitStorageDataError> {
638 let entry: MapEntry = MapEntry::deserialize(item)
640 .map_err(|err| InitStorageDataError::InvalidMapEntry(err.to_string()))?;
641
642 if entry.key().template_requirements(StorageValueName::empty()).next().is_some()
644 || entry.value().template_requirements(StorageValueName::empty()).next().is_some()
645 {
646 return Err(InitStorageDataError::InvalidMapEntry(
647 "map entries cannot contain templates".into(),
648 ));
649 }
650
651 let key = entry
653 .key()
654 .try_build_word(&InitStorageData::default(), StorageValueName::empty())
655 .map_err(|err| InitStorageDataError::InvalidMapEntry(err.to_string()))?;
656 let value = entry
657 .value()
658 .try_build_word(&InitStorageData::default(), StorageValueName::empty())
659 .map_err(|err| InitStorageDataError::InvalidMapEntry(err.to_string()))?;
660
661 Ok((key, value))
662}
663
664#[cfg(test)]
668mod tests {
669 use alloc::string::ToString;
670 use core::error::Error;
671
672 use super::*;
673 use crate::account::component::toml::InitStorageDataError;
674
675 #[test]
676 fn from_toml_str_with_nested_table_and_flattened() {
677 let toml_table = r#"
678 [token_metadata]
679 max_supply = "1000000000"
680 symbol = "ETH"
681 decimals = "9"
682 "#;
683
684 let toml_inline = r#"
685 token_metadata.max_supply = "1000000000"
686 token_metadata.symbol = "ETH"
687 token_metadata.decimals = "9"
688 "#;
689
690 let storage_table = InitStorageData::from_toml(toml_table).unwrap();
691 let storage_inline = InitStorageData::from_toml(toml_inline).unwrap();
692
693 assert_eq!(storage_table.placeholders(), storage_inline.placeholders());
694 }
695
696 #[test]
697 fn from_toml_str_with_deeply_nested_tables() {
698 let toml_str = r#"
699 [a]
700 b = "0xb"
701
702 [a.c]
703 d = "0xd"
704
705 [x.y.z]
706 w = 42 # NOTE: This gets parsed as string
707 "#;
708
709 let storage = InitStorageData::from_toml(toml_str).expect("Failed to parse TOML");
710 let key1 = StorageValueName::new("a.b".to_string()).unwrap();
711 let key2 = StorageValueName::new("a.c.d".to_string()).unwrap();
712 let key3 = StorageValueName::new("x.y.z.w".to_string()).unwrap();
713
714 assert_eq!(storage.get(&key1).unwrap(), "0xb");
715 assert_eq!(storage.get(&key2).unwrap(), "0xd");
716 assert_eq!(storage.get(&key3).unwrap(), "42");
717 }
718
719 #[test]
720 fn test_error_on_array() {
721 let toml_str = r#"
722 token_metadata.v = [1, 2, 3]
723 "#;
724
725 let result = InitStorageData::from_toml(toml_str);
726 assert_matches::assert_matches!(
727 result.unwrap_err(),
728 InitStorageDataError::ArraysNotSupported
729 );
730 }
731
732 #[test]
733 fn parse_map_entries_from_array() {
734 let toml_str = r#"
735 my_map = [
736 { key = "0x0000000000000000000000000000000000000000000000000000000000000001", value = "0x0000000000000000000000000000000000000000000000000000000000000010" },
737 { key = "0x0000000000000000000000000000000000000000000000000000000000000002", value = ["1", "2", "3", "4"] }
738 ]
739 "#;
740
741 let storage = InitStorageData::from_toml(toml_str).expect("Failed to parse map entries");
742 let map_name = StorageValueName::new("my_map").unwrap();
743 let entries = storage.map_entries(&map_name).expect("map entries missing");
744 assert_eq!(entries.len(), 2);
745
746 let first_key =
747 Word::try_from("0x0000000000000000000000000000000000000000000000000000000000000001")
748 .unwrap();
749 assert_eq!(entries[0].0, first_key);
750
751 let second_value =
752 Word::from([Felt::new(1u64), Felt::new(2u64), Felt::new(3u64), Felt::new(4u64)]);
753 assert_eq!(entries[1].1, second_value);
754 }
755
756 #[test]
757 fn error_on_empty_subtable() {
758 let toml_str = r#"
759 [a]
760 b = {}
761 "#;
762
763 let result = InitStorageData::from_toml(toml_str);
764 assert_matches::assert_matches!(result.unwrap_err(), InitStorageDataError::EmptyTable(_));
765 }
766
767 #[test]
768 fn error_on_duplicate_keys() {
769 let toml_str = r#"
770 token_metadata.max_supply = "1000000000"
771 token_metadata.max_supply = "500000000"
772 "#;
773
774 let result = InitStorageData::from_toml(toml_str).unwrap_err();
775 assert_matches::assert_matches!(result, InitStorageDataError::InvalidToml(_));
777 assert!(result.source().unwrap().to_string().contains("duplicate"));
778 }
779}