miden_objects/account/component/template/storage/
placeholder.rs

1use alloc::boxed::Box;
2use alloc::collections::BTreeMap;
3use alloc::string::{String, ToString};
4use core::error::Error;
5use core::fmt::{self, Display};
6
7use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable};
8use miden_core::{Felt, Word};
9use miden_crypto::dsa::rpo_falcon512::{self};
10use miden_processor::DeserializationError;
11use thiserror::Error;
12
13use crate::asset::TokenSymbol;
14use crate::utils::sync::LazyLock;
15
16/// A global registry for template converters.
17///
18/// It is used during component instantiation to dynamically convert template placeholders into
19/// their respective storage values.
20pub static TEMPLATE_REGISTRY: LazyLock<TemplateRegistry> = LazyLock::new(|| {
21    let mut registry = TemplateRegistry::new();
22    registry.register_felt_type::<u8>();
23    registry.register_felt_type::<u16>();
24    registry.register_felt_type::<u32>();
25    registry.register_felt_type::<Felt>();
26    registry.register_felt_type::<TokenSymbol>();
27    registry.register_word_type::<Word>();
28    registry.register_word_type::<rpo_falcon512::PublicKey>();
29    registry
30});
31
32// STORAGE VALUE NAME
33// ================================================================================================
34
35/// A simple wrapper type around a string key that identifies values.
36///
37/// A storage value name is a string that identifies dynamic values within a component's metadata
38/// storage entries.
39///
40/// These names can be chained together, in a way that allows composing unique keys for inner
41/// templated elements.
42///
43/// At component instantiation, a map of names to values must be provided to dynamically
44/// replace these placeholders with the instance's actual values.
45#[derive(Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
46#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))]
47#[cfg_attr(feature = "std", serde(transparent))]
48pub struct StorageValueName {
49    fully_qualified_name: String,
50}
51
52impl StorageValueName {
53    /// Creates a new [`StorageValueName`] from the provided string.
54    ///
55    /// A [`StorageValueName`] serves as an identifier for storage values that are determined at
56    /// instantiation time of an [AccountComponentTemplate](super::super::AccountComponentTemplate).
57    ///
58    /// The key can consist of one or more segments separated by dots (`.`).
59    /// Each segment must be non-empty and may contain only alphanumeric characters, underscores
60    /// (`_`), or hyphens (`-`).
61    ///
62    /// # Errors
63    ///
64    /// This method returns an error if:
65    /// - Any segment (or the whole key) is empty.
66    /// - Any segment contains invalid characters.
67    pub fn new(base: impl Into<String>) -> Result<Self, StorageValueNameError> {
68        let base: String = base.into();
69        for segment in base.split('.') {
70            Self::validate_segment(segment)?;
71        }
72        Ok(Self { fully_qualified_name: base })
73    }
74
75    /// Creates an empty [`StorageValueName`].
76    pub(crate) fn empty() -> Self {
77        StorageValueName { fully_qualified_name: String::default() }
78    }
79
80    /// Adds a suffix to the storage value name, separated by a period.
81    #[must_use]
82    pub fn with_suffix(self, suffix: &StorageValueName) -> StorageValueName {
83        let mut key = self;
84        if !suffix.as_str().is_empty() {
85            if !key.as_str().is_empty() {
86                key.fully_qualified_name.push('.');
87            }
88            key.fully_qualified_name.push_str(suffix.as_str());
89        }
90
91        key
92    }
93
94    /// Returns the fully qualified name as a string slice.
95    pub fn as_str(&self) -> &str {
96        &self.fully_qualified_name
97    }
98
99    fn validate_segment(segment: &str) -> Result<(), StorageValueNameError> {
100        if segment.is_empty() {
101            return Err(StorageValueNameError::EmptySegment);
102        }
103        if let Some(offending_char) =
104            segment.chars().find(|&c| !(c.is_ascii_alphanumeric() || c == '_' || c == '-'))
105        {
106            return Err(StorageValueNameError::InvalidCharacter {
107                part: segment.to_string(),
108                character: offending_char,
109            });
110        }
111
112        Ok(())
113    }
114}
115
116impl Display for StorageValueName {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        f.write_str(self.as_str())
119    }
120}
121
122impl Serializable for StorageValueName {
123    fn write_into<W: ByteWriter>(&self, target: &mut W) {
124        target.write(&self.fully_qualified_name);
125    }
126}
127
128impl Deserializable for StorageValueName {
129    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
130        let key: String = source.read()?;
131        Ok(StorageValueName { fully_qualified_name: key })
132    }
133}
134
135#[derive(Debug, Error)]
136pub enum StorageValueNameError {
137    #[error("key segment is empty")]
138    EmptySegment,
139    #[error("key segment '{part}' contains invalid character '{character}'")]
140    InvalidCharacter { part: String, character: char },
141}
142
143// TEMPLATE TYPE ERROR
144// ================================================================================================
145
146/// Errors that can occur when parsing or converting template types.
147///
148/// This enum covers various failure cases including parsing errors, conversion errors,
149/// unsupported conversions, and cases where a required type is not found in the registry.
150#[derive(Debug, Error)]
151pub enum TemplateTypeError {
152    #[error("conversion error: {0}")]
153    ConversionError(String),
154    #[error("felt type ` {0}` not found in the type registry")]
155    FeltTypeNotFound(TemplateType),
156    #[error("invalid type name `{0}`: {1}")]
157    InvalidTypeName(String, String),
158    #[error("failed to parse input `{input}` as `{template_type}`")]
159    ParseError {
160        input: String,
161        template_type: TemplateType,
162        source: Box<dyn Error + Send + Sync + 'static>,
163    },
164    #[error("word type ` {0}` not found in the type registry")]
165    WordTypeNotFound(TemplateType),
166}
167
168impl TemplateTypeError {
169    /// Creates a [`TemplateTypeError::ParseError`].
170    pub fn parse(
171        input: impl Into<String>,
172        template_type: TemplateType,
173        source: impl Error + Send + Sync + 'static,
174    ) -> Self {
175        TemplateTypeError::ParseError {
176            input: input.into(),
177            template_type,
178            source: Box::new(source),
179        }
180    }
181}
182
183// TEMPLATE TYPE
184// ================================================================================================
185
186/// A newtype wrapper around a `String`, representing a template's type identifier.
187#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
188#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))]
189#[cfg_attr(feature = "std", serde(transparent))]
190pub struct TemplateType(String);
191
192impl TemplateType {
193    /// Creates a new [`TemplateType`] from a `String`.
194    ///
195    /// The name must follow a Rust-style namespace format, consisting of one or more segments
196    /// (non-empty, and alphanumerical) separated by double-colon (`::`) delimiters.
197    ///
198    /// # Errors
199    ///
200    /// - If the identifier is empty.
201    /// - If it is composed of one or more segments separated by `::`.
202    /// - If any segment is empty or contains something other than alphanumerical
203    ///   characters/underscores.
204    pub fn new(s: impl Into<String>) -> Result<Self, TemplateTypeError> {
205        let s = s.into();
206        if s.is_empty() {
207            return Err(TemplateTypeError::InvalidTypeName(
208                s.clone(),
209                "template type identifier is empty".to_string(),
210            ));
211        }
212        for segment in s.split("::") {
213            if segment.is_empty() {
214                return Err(TemplateTypeError::InvalidTypeName(
215                    s.clone(),
216                    "empty segment in template type identifier".to_string(),
217                ));
218            }
219            if !segment.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
220                return Err(TemplateTypeError::InvalidTypeName(
221                    s.clone(),
222                    format!("segment '{segment}' contains invalid characters"),
223                ));
224            }
225        }
226        Ok(Self(s))
227    }
228
229    /// Returns the [`TemplateType`] for the native [`Felt`] type.
230    pub fn native_felt() -> TemplateType {
231        TemplateType::new("felt").expect("type is well formed")
232    }
233
234    /// Returns the [`TemplateType`] for the native [`Word`] type.
235    pub fn native_word() -> TemplateType {
236        TemplateType::new("word").expect("type is well formed")
237    }
238
239    /// Returns the [`TemplateType`] for storage map placeholders.
240    pub fn storage_map() -> TemplateType {
241        TemplateType::new("map").expect("type is well formed")
242    }
243
244    /// Returns a reference to the inner string.
245    pub fn as_str(&self) -> &str {
246        &self.0
247    }
248}
249
250impl Display for TemplateType {
251    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252        f.write_str(self.as_str())
253    }
254}
255
256impl Serializable for TemplateType {
257    fn write_into<W: ByteWriter>(&self, target: &mut W) {
258        target.write(self.0.clone())
259    }
260}
261
262impl Deserializable for TemplateType {
263    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
264        let id: String = source.read()?;
265
266        TemplateType::new(id).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
267    }
268}
269
270// TEMPLATE REQUIREMENT
271// ================================================================================================
272
273/// Describes the expected type and additional metadata for a templated storage entry.
274///
275/// A `PlaceholderTypeRequirement` specifies the expected type identifier for a storage entry as
276/// well as an optional description. This information is used to validate and provide context for
277/// dynamic storage values.
278#[derive(Debug)]
279pub struct PlaceholderTypeRequirement {
280    /// The expected type identifier.
281    pub r#type: TemplateType,
282    /// An optional description providing additional context.
283    pub description: Option<String>,
284}
285
286// TEMPLATE TRAITS
287// ================================================================================================
288
289/// Trait for converting a string into a single `Felt`.
290pub trait TemplateFelt {
291    /// Returns the type identifier.
292    fn type_name() -> TemplateType;
293    /// Parses the input string into a `Felt`.
294    fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError>;
295}
296
297/// Trait for converting a string into a single `Word`.
298pub trait TemplateWord {
299    /// Returns the type identifier.
300    fn type_name() -> TemplateType;
301    /// Parses the input string into a `Word`.
302    fn parse_word(input: &str) -> Result<Word, TemplateTypeError>;
303}
304
305// FELT IMPLS FOR NATIVE TYPES
306// ================================================================================================
307
308impl TemplateFelt for u8 {
309    fn type_name() -> TemplateType {
310        TemplateType::new("u8").expect("type is well formed")
311    }
312
313    fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
314        let native: u8 = input
315            .parse()
316            .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
317        Ok(Felt::from(native))
318    }
319}
320
321impl TemplateFelt for u16 {
322    fn type_name() -> TemplateType {
323        TemplateType::new("u16").expect("type is well formed")
324    }
325
326    fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
327        let native: u16 = input
328            .parse()
329            .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
330        Ok(Felt::from(native))
331    }
332}
333
334impl TemplateFelt for u32 {
335    fn type_name() -> TemplateType {
336        TemplateType::new("u32").expect("type is well formed")
337    }
338
339    fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
340        let native: u32 = input
341            .parse()
342            .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
343        Ok(Felt::from(native))
344    }
345}
346
347impl TemplateFelt for Felt {
348    fn type_name() -> TemplateType {
349        TemplateType::new("felt").expect("type is well formed")
350    }
351
352    fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
353        let n = if let Some(hex) = input.strip_prefix("0x").or_else(|| input.strip_prefix("0X")) {
354            u64::from_str_radix(hex, 16)
355        } else {
356            input.parse::<u64>()
357        }
358        .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
359        Felt::try_from(n).map_err(|_| TemplateTypeError::ConversionError(input.to_string()))
360    }
361}
362
363impl TemplateFelt for TokenSymbol {
364    fn type_name() -> TemplateType {
365        TemplateType::new("token_symbol").expect("type is well formed")
366    }
367    fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
368        let token = TokenSymbol::new(input)
369            .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
370        Ok(Felt::from(token))
371    }
372}
373
374// WORD IMPLS FOR NATIVE TYPES
375// ================================================================================================
376
377#[derive(Debug, Error)]
378#[error("error parsing word: {0}")]
379struct WordParseError(String);
380
381/// Pads a hex string to 64 characters (excluding the 0x prefix).
382///
383/// If the input starts with "0x" and has fewer than 64 hex characters after the prefix,
384/// it will be left-padded with zeros. Otherwise, returns the input unchanged.
385fn pad_hex_string(input: &str) -> String {
386    if input.starts_with("0x") && input.len() < 66 {
387        // 66 = "0x" + 64 hex chars
388        let hex_part = &input[2..];
389        let padding = "0".repeat(64 - hex_part.len());
390        format!("0x{}{}", padding, hex_part)
391    } else {
392        input.to_string()
393    }
394}
395
396impl TemplateWord for Word {
397    fn type_name() -> TemplateType {
398        TemplateType::native_word()
399    }
400    fn parse_word(input: &str) -> Result<Word, TemplateTypeError> {
401        let padded_input = pad_hex_string(input);
402
403        Word::try_from(padded_input.as_str()).map_err(|err| {
404            TemplateTypeError::parse(
405                input.to_string(), // Use original input in error
406                Self::type_name(),
407                WordParseError(err.to_string()),
408            )
409        })
410    }
411}
412
413impl TemplateWord for rpo_falcon512::PublicKey {
414    fn type_name() -> TemplateType {
415        TemplateType::new("auth::rpo_falcon512::pub_key").expect("type is well formed")
416    }
417    fn parse_word(input: &str) -> Result<Word, TemplateTypeError> {
418        let padded_input = pad_hex_string(input);
419
420        Word::try_from(padded_input.as_str()).map_err(|err| {
421            TemplateTypeError::parse(
422                input.to_string(), // Use original input in error
423                Self::type_name(),
424                WordParseError(err.to_string()),
425            )
426        })
427    }
428}
429
430// TYPE ALIASES FOR CONVERTER CLOSURES
431// ================================================================================================
432
433/// Type alias for a function that converts a string into a [`Felt`] value.
434type TemplateFeltConverter = fn(&str) -> Result<Felt, TemplateTypeError>;
435
436/// Type alias for a function that converts a string into a [`Word`].
437type TemplateWordConverter = fn(&str) -> Result<Word, TemplateTypeError>;
438
439// TODO: Implement converting to list of words for multi-slot values
440
441// TEMPLATE REGISTRY
442// ================================================================================================
443
444/// Registry for template converters.
445///
446/// This registry maintains mappings from type identifiers (as strings) to conversion functions for
447/// [`Felt`], [`Word`], and [`Vec<Word>`] types. It is used to dynamically parse template inputs
448/// into their corresponding storage representations.
449#[derive(Clone, Debug, Default)]
450pub struct TemplateRegistry {
451    felt: BTreeMap<TemplateType, TemplateFeltConverter>,
452    word: BTreeMap<TemplateType, TemplateWordConverter>,
453}
454
455impl TemplateRegistry {
456    /// Creates a new, empty `TemplateRegistry`.
457    ///
458    /// The registry is initially empty and conversion functions can be registered using the
459    /// `register_*_type` methods.
460    pub fn new() -> Self {
461        Self { ..Default::default() }
462    }
463
464    /// Registers a `TemplateFelt` converter, to interpret a string as a [`Felt``].
465    pub fn register_felt_type<T: TemplateFelt + 'static>(&mut self) {
466        let key = T::type_name();
467        self.felt.insert(key, T::parse_felt);
468    }
469
470    /// Registers a `TemplateWord` converter, to interpret a string as a [`Word`].
471    pub fn register_word_type<T: TemplateWord + 'static>(&mut self) {
472        let key = T::type_name();
473        self.word.insert(key, T::parse_word);
474    }
475
476    /// Attempts to parse a string into a `Felt` using the registered converter for the given type
477    /// name.
478    ///
479    /// # Arguments
480    ///
481    /// - type_name: A string that acts as the type identifier.
482    /// - value: The string representation of the value to be parsed.
483    ///
484    /// # Errors
485    ///
486    /// - If the type is not registered or if the conversion fails.
487    pub fn try_parse_felt(
488        &self,
489        type_name: &TemplateType,
490        value: &str,
491    ) -> Result<Felt, TemplateTypeError> {
492        let converter = self
493            .felt
494            .get(type_name)
495            .ok_or(TemplateTypeError::FeltTypeNotFound(type_name.clone()))?;
496        converter(value)
497    }
498
499    /// Attempts to parse a string into a `Word` using the registered converter for the given type
500    /// name.
501    ///
502    /// # Arguments
503    ///
504    /// - type_name: A string that acts as the type identifier.
505    /// - value: The string representation of the value to be parsed.
506    ///
507    /// # Errors
508    ///
509    /// - If the type is not registered or if the conversion fails.
510    pub fn try_parse_word(
511        &self,
512        type_name: &TemplateType,
513        value: &str,
514    ) -> Result<Word, TemplateTypeError> {
515        let converter = self
516            .word
517            .get(type_name)
518            .ok_or(TemplateTypeError::WordTypeNotFound(type_name.clone()))?;
519        converter(value)
520    }
521
522    /// Returns `true` if a `TemplateFelt` is registered for the given type.
523    pub fn contains_felt_type(&self, type_name: &TemplateType) -> bool {
524        self.felt.contains_key(type_name)
525    }
526
527    /// Returns `true` if a `TemplateWord` is registered for the given type.
528    pub fn contains_word_type(&self, type_name: &TemplateType) -> bool {
529        self.word.contains_key(type_name)
530    }
531}