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

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