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
402 let mut map = if raw.values.is_some() {
407 MapRepresentation::new_value(Vec::new(), name)
408 } else {
409 MapRepresentation::new_template(name)
410 };
411
412 if let Some(desc) = description {
413 map = map.with_description(desc);
414 }
415 Ok(StorageEntry::Map { slot, map })
416 } else if let Some(StorageValues::Words(values)) = raw.values {
417 let identifier = raw
418 .identifier
419 .ok_or_else(|| missing_field_for("identifier", "multislot entry"))?;
420
421 let mut slots =
422 raw.slots.ok_or_else(|| missing_field_for("slots", "multislot entry"))?;
423
424 slots.sort_unstable();
426 for pair in slots.windows(2) {
427 if pair[1] != pair[0] + 1 {
428 return Err(serde::de::Error::custom(format!(
429 "`slots` in the `{}` storage entry are not contiguous",
430 identifier.name
431 )));
432 }
433 }
434 let start = slots[0];
435 let end = slots.last().expect("checked validity") + 1;
436 Ok(StorageEntry::new_multislot(identifier, start..end, values))
437 } else if let Some(word_type) = raw.word_type {
438 let slot = raw.slot.ok_or_else(|| missing_field_for("slot", "single-slot entry"))?;
440 let identifier = raw
441 .identifier
442 .ok_or_else(|| missing_field_for("identifier", "single-slot entry"))?;
443 let word_entry = WordRepresentation::Template { r#type: word_type, identifier };
444 Ok(StorageEntry::Value { slot, word_entry })
445 } else {
446 Err(D::Error::custom("placeholder storage entries require the `type` field"))
447 }
448 }
449}
450
451impl InitStorageData {
455 pub fn from_toml(toml_str: &str) -> Result<Self, InitStorageDataError> {
467 let value: toml::Value = toml::from_str(toml_str)?;
468 let mut value_entries = BTreeMap::new();
469 let mut map_entries = BTreeMap::new();
470 Self::flatten_parse_toml_value(
472 StorageValueName::empty(),
473 value,
474 &mut value_entries,
475 &mut map_entries,
476 )?;
477
478 Ok(InitStorageData::new(value_entries, map_entries))
479 }
480
481 fn flatten_parse_toml_value(
487 prefix: StorageValueName,
488 value: toml::Value,
489 value_entries: &mut BTreeMap<StorageValueName, String>,
490 map_entries: &mut BTreeMap<StorageValueName, Vec<(Word, Word)>>,
491 ) -> Result<(), InitStorageDataError> {
492 match value {
493 toml::Value::Table(table) => {
494 if !prefix.as_str().is_empty() && table.is_empty() {
496 return Err(InitStorageDataError::EmptyTable(prefix.as_str().into()));
497 }
498 for (key, val) in table {
499 let new_key = StorageValueName::new(key.to_string())
501 .map_err(InitStorageDataError::InvalidStorageValueName)?;
502 let new_prefix = prefix.clone().with_suffix(&new_key);
503 Self::flatten_parse_toml_value(new_prefix, val, value_entries, map_entries)?;
504 }
505 },
506 toml::Value::Array(items) if items.is_empty() => {
507 if prefix.as_str().is_empty() {
508 return Err(InitStorageDataError::ArraysNotSupported);
509 }
510 map_entries.insert(prefix, Vec::new());
511 },
512 toml::Value::Array(items) => {
513 if prefix.as_str().is_empty()
514 || !items.iter().all(|item| matches!(item, toml::Value::Table(_)))
515 {
516 return Err(InitStorageDataError::ArraysNotSupported);
517 }
518
519 let entries = items
520 .into_iter()
521 .map(parse_map_entry_value)
522 .collect::<Result<Vec<(Word, Word)>, _>>()?;
523 map_entries.insert(prefix, entries);
524 },
525 toml_value => {
526 let value = match toml_value {
528 toml::Value::String(s) => s.clone(),
529 _ => toml_value.to_string(),
530 };
531 value_entries.insert(prefix, value);
532 },
533 }
534 Ok(())
535 }
536}
537
538#[derive(Debug, Error)]
539pub enum InitStorageDataError {
540 #[error("failed to parse TOML")]
541 InvalidToml(#[from] toml::de::Error),
542
543 #[error("empty table encountered for key `{0}`")]
544 EmptyTable(String),
545
546 #[error("invalid input: arrays are not supported")]
547 ArraysNotSupported,
548
549 #[error("invalid storage value name")]
550 InvalidStorageValueName(#[source] StorageValueNameError),
551
552 #[error("invalid map entry: {0}")]
553 InvalidMapEntry(String),
554}
555
556impl Serialize for FieldIdentifier {
557 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
558 where
559 S: Serializer,
560 {
561 let mut map = serializer.serialize_map(Some(2))?;
562 map.serialize_entry("name", &self.name)?;
563 map.serialize_entry("description", &self.description)?;
564 map.end()
565 }
566}
567
568struct FieldIdentifierVisitor;
569
570impl<'de> Visitor<'de> for FieldIdentifierVisitor {
571 type Value = FieldIdentifier;
572
573 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
574 formatter.write_str("a map with 'name' and optionally 'description'")
575 }
576
577 fn visit_map<M>(self, mut map: M) -> Result<FieldIdentifier, M::Error>
578 where
579 M: MapAccess<'de>,
580 {
581 let mut name = None;
582 let mut description = None;
583 while let Some(key) = map.next_key::<String>()? {
584 match key.as_str() {
585 "name" => {
586 name = Some(map.next_value()?);
587 },
588 "description" => {
589 let d: String = map.next_value()?;
590 description = if d.trim().is_empty() { None } else { Some(d) };
592 },
593 _ => {
594 let _: de::IgnoredAny = map.next_value()?;
596 },
597 }
598 }
599 let name = name.ok_or_else(|| de::Error::missing_field("name"))?;
600 Ok(FieldIdentifier { name, description })
601 }
602}
603
604impl<'de> Deserialize<'de> for FieldIdentifier {
605 fn deserialize<D>(deserializer: D) -> Result<FieldIdentifier, D::Error>
606 where
607 D: Deserializer<'de>,
608 {
609 deserializer.deserialize_map(FieldIdentifierVisitor)
610 }
611}
612
613fn missing_field_for<E: serde::de::Error>(field: &str, context: &str) -> E {
617 E::custom(format!("missing '{field}' field for {context}"))
618}
619
620fn expect_parse_field_identifier<E: serde::de::Error>(
622 n: Option<String>,
623 description: Option<String>,
624 context: &str,
625) -> Result<FieldIdentifier, E> {
626 let name = n.ok_or_else(|| missing_field_for("name", context))?;
627 parse_field_identifier(name, description)
628}
629
630fn parse_field_identifier<E: serde::de::Error>(
632 n: String,
633 description: Option<String>,
634) -> Result<FieldIdentifier, E> {
635 StorageValueName::new(n)
636 .map_err(|err| E::custom(format!("invalid `name`: {err}")))
637 .map(|storage_name| {
638 if let Some(desc) = description {
639 FieldIdentifier::with_description(storage_name, desc)
640 } else {
641 FieldIdentifier::with_name(storage_name)
642 }
643 })
644}
645
646fn parse_map_entry_value(item: toml::Value) -> Result<(Word, Word), InitStorageDataError> {
648 let entry: MapEntry = MapEntry::deserialize(item)
650 .map_err(|err| InitStorageDataError::InvalidMapEntry(err.to_string()))?;
651
652 if entry.key().template_requirements(StorageValueName::empty()).next().is_some()
654 || entry.value().template_requirements(StorageValueName::empty()).next().is_some()
655 {
656 return Err(InitStorageDataError::InvalidMapEntry(
657 "map entries cannot contain templates".into(),
658 ));
659 }
660
661 let key = entry
663 .key()
664 .try_build_word(&InitStorageData::default(), StorageValueName::empty())
665 .map_err(|err| InitStorageDataError::InvalidMapEntry(err.to_string()))?;
666 let value = entry
667 .value()
668 .try_build_word(&InitStorageData::default(), StorageValueName::empty())
669 .map_err(|err| InitStorageDataError::InvalidMapEntry(err.to_string()))?;
670
671 Ok((key, value))
672}
673
674#[cfg(test)]
678mod tests {
679 use alloc::string::ToString;
680 use core::error::Error;
681
682 use super::*;
683 use crate::account::component::toml::InitStorageDataError;
684
685 #[test]
686 fn from_toml_str_with_nested_table_and_flattened() {
687 let toml_table = r#"
688 [token_metadata]
689 max_supply = "1000000000"
690 symbol = "ETH"
691 decimals = "9"
692 "#;
693
694 let toml_inline = r#"
695 token_metadata.max_supply = "1000000000"
696 token_metadata.symbol = "ETH"
697 token_metadata.decimals = "9"
698 "#;
699
700 let storage_table = InitStorageData::from_toml(toml_table).unwrap();
701 let storage_inline = InitStorageData::from_toml(toml_inline).unwrap();
702
703 assert_eq!(storage_table.placeholders(), storage_inline.placeholders());
704 }
705
706 #[test]
707 fn from_toml_str_with_deeply_nested_tables() {
708 let toml_str = r#"
709 [a]
710 b = "0xb"
711
712 [a.c]
713 d = "0xd"
714
715 [x.y.z]
716 w = 42 # NOTE: This gets parsed as string
717 "#;
718
719 let storage = InitStorageData::from_toml(toml_str).expect("Failed to parse TOML");
720 let key1 = StorageValueName::new("a.b".to_string()).unwrap();
721 let key2 = StorageValueName::new("a.c.d".to_string()).unwrap();
722 let key3 = StorageValueName::new("x.y.z.w".to_string()).unwrap();
723
724 assert_eq!(storage.get(&key1).unwrap(), "0xb");
725 assert_eq!(storage.get(&key2).unwrap(), "0xd");
726 assert_eq!(storage.get(&key3).unwrap(), "42");
727 }
728
729 #[test]
730 fn test_error_on_array() {
731 let toml_str = r#"
732 token_metadata.v = [1, 2, 3]
733 "#;
734
735 let result = InitStorageData::from_toml(toml_str);
736 assert_matches::assert_matches!(
737 result.unwrap_err(),
738 InitStorageDataError::ArraysNotSupported
739 );
740 }
741
742 #[test]
743 fn parse_map_entries_from_array() {
744 let toml_str = r#"
745 my_map = [
746 { key = "0x0000000000000000000000000000000000000000000000000000000000000001", value = "0x0000000000000000000000000000000000000000000000000000000000000010" },
747 { key = "0x0000000000000000000000000000000000000000000000000000000000000002", value = ["1", "2", "3", "4"] }
748 ]
749 "#;
750
751 let storage = InitStorageData::from_toml(toml_str).expect("Failed to parse map entries");
752 let map_name = StorageValueName::new("my_map").unwrap();
753 let entries = storage.map_entries(&map_name).expect("map entries missing");
754 assert_eq!(entries.len(), 2);
755
756 let first_key =
757 Word::try_from("0x0000000000000000000000000000000000000000000000000000000000000001")
758 .unwrap();
759 assert_eq!(entries[0].0, first_key);
760
761 let second_value =
762 Word::from([Felt::new(1u64), Felt::new(2u64), Felt::new(3u64), Felt::new(4u64)]);
763 assert_eq!(entries[1].1, second_value);
764 }
765
766 #[test]
767 fn error_on_empty_subtable() {
768 let toml_str = r#"
769 [a]
770 b = {}
771 "#;
772
773 let result = InitStorageData::from_toml(toml_str);
774 assert_matches::assert_matches!(result.unwrap_err(), InitStorageDataError::EmptyTable(_));
775 }
776
777 #[test]
778 fn error_on_duplicate_keys() {
779 let toml_str = r#"
780 token_metadata.max_supply = "1000000000"
781 token_metadata.max_supply = "500000000"
782 "#;
783
784 let result = InitStorageData::from_toml(toml_str).unwrap_err();
785 assert_matches::assert_matches!(result, InitStorageDataError::InvalidToml(_));
787 assert!(result.source().unwrap().to_string().contains("duplicate"));
788 }
789}