use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::string::{String, ToString};
use core::error::Error;
use core::fmt::{self, Display};
use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable};
use miden_core::{Felt, Word};
use miden_crypto::dsa::{ecdsa_k256_keccak, rpo_falcon512};
use miden_processor::DeserializationError;
use thiserror::Error;
use crate::asset::TokenSymbol;
use crate::utils::sync::LazyLock;
pub static TEMPLATE_REGISTRY: LazyLock<TemplateRegistry> = LazyLock::new(|| {
let mut registry = TemplateRegistry::new();
registry.register_felt_type::<u8>();
registry.register_felt_type::<u16>();
registry.register_felt_type::<u32>();
registry.register_felt_type::<Felt>();
registry.register_felt_type::<TokenSymbol>();
registry.register_word_type::<Word>();
registry.register_word_type::<rpo_falcon512::PublicKey>();
registry.register_word_type::<ecdsa_k256_keccak::PublicKey>();
registry
});
#[derive(Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))]
#[cfg_attr(feature = "std", serde(transparent))]
pub struct StorageValueName {
fully_qualified_name: String,
}
impl StorageValueName {
pub fn new(base: impl Into<String>) -> Result<Self, StorageValueNameError> {
let base: String = base.into();
for segment in base.split('.') {
Self::validate_segment(segment)?;
}
Ok(Self { fully_qualified_name: base })
}
pub(crate) fn empty() -> Self {
StorageValueName { fully_qualified_name: String::default() }
}
#[must_use]
pub fn with_suffix(self, suffix: &StorageValueName) -> StorageValueName {
let mut key = self;
if !suffix.as_str().is_empty() {
if !key.as_str().is_empty() {
key.fully_qualified_name.push('.');
}
key.fully_qualified_name.push_str(suffix.as_str());
}
key
}
pub fn as_str(&self) -> &str {
&self.fully_qualified_name
}
fn validate_segment(segment: &str) -> Result<(), StorageValueNameError> {
if segment.is_empty() {
return Err(StorageValueNameError::EmptySegment);
}
if let Some(offending_char) =
segment.chars().find(|&c| !(c.is_ascii_alphanumeric() || c == '_' || c == '-'))
{
return Err(StorageValueNameError::InvalidCharacter {
part: segment.to_string(),
character: offending_char,
});
}
Ok(())
}
}
impl Display for StorageValueName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl Serializable for StorageValueName {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write(&self.fully_qualified_name);
}
}
impl Deserializable for StorageValueName {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let key: String = source.read()?;
Ok(StorageValueName { fully_qualified_name: key })
}
}
#[derive(Debug, Error)]
pub enum StorageValueNameError {
#[error("key segment is empty")]
EmptySegment,
#[error("key segment '{part}' contains invalid character '{character}'")]
InvalidCharacter { part: String, character: char },
}
#[derive(Debug, Error)]
pub enum TemplateTypeError {
#[error("conversion error: {0}")]
ConversionError(String),
#[error("felt type ` {0}` not found in the type registry")]
FeltTypeNotFound(TemplateType),
#[error("invalid type name `{0}`: {1}")]
InvalidTypeName(String, String),
#[error("failed to parse input `{input}` as `{template_type}`")]
ParseError {
input: String,
template_type: TemplateType,
source: Box<dyn Error + Send + Sync + 'static>,
},
#[error("word type ` {0}` not found in the type registry")]
WordTypeNotFound(TemplateType),
}
impl TemplateTypeError {
pub fn parse(
input: impl Into<String>,
template_type: TemplateType,
source: impl Error + Send + Sync + 'static,
) -> Self {
TemplateTypeError::ParseError {
input: input.into(),
template_type,
source: Box::new(source),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))]
#[cfg_attr(feature = "std", serde(transparent))]
pub struct TemplateType(String);
impl TemplateType {
pub fn new(s: impl Into<String>) -> Result<Self, TemplateTypeError> {
let s = s.into();
if s.is_empty() {
return Err(TemplateTypeError::InvalidTypeName(
s.clone(),
"template type identifier is empty".to_string(),
));
}
for segment in s.split("::") {
if segment.is_empty() {
return Err(TemplateTypeError::InvalidTypeName(
s.clone(),
"empty segment in template type identifier".to_string(),
));
}
if !segment.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
return Err(TemplateTypeError::InvalidTypeName(
s.clone(),
format!("segment '{segment}' contains invalid characters"),
));
}
}
Ok(Self(s))
}
pub fn native_felt() -> TemplateType {
TemplateType::new("felt").expect("type is well formed")
}
pub fn native_word() -> TemplateType {
TemplateType::new("word").expect("type is well formed")
}
pub fn storage_map() -> TemplateType {
TemplateType::new("map").expect("type is well formed")
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl Display for TemplateType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl Serializable for TemplateType {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write(self.0.clone())
}
}
impl Deserializable for TemplateType {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let id: String = source.read()?;
TemplateType::new(id).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}
#[derive(Debug)]
pub struct PlaceholderTypeRequirement {
pub r#type: TemplateType,
pub description: Option<String>,
}
pub trait TemplateFelt {
fn type_name() -> TemplateType;
fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError>;
}
pub trait TemplateWord {
fn type_name() -> TemplateType;
fn parse_word(input: &str) -> Result<Word, TemplateTypeError>;
}
impl TemplateFelt for u8 {
fn type_name() -> TemplateType {
TemplateType::new("u8").expect("type is well formed")
}
fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
let native: u8 = input
.parse()
.map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
Ok(Felt::from(native))
}
}
impl TemplateFelt for u16 {
fn type_name() -> TemplateType {
TemplateType::new("u16").expect("type is well formed")
}
fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
let native: u16 = input
.parse()
.map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
Ok(Felt::from(native))
}
}
impl TemplateFelt for u32 {
fn type_name() -> TemplateType {
TemplateType::new("u32").expect("type is well formed")
}
fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
let native: u32 = input
.parse()
.map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
Ok(Felt::from(native))
}
}
impl TemplateFelt for Felt {
fn type_name() -> TemplateType {
TemplateType::new("felt").expect("type is well formed")
}
fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
let n = if let Some(hex) = input.strip_prefix("0x").or_else(|| input.strip_prefix("0X")) {
u64::from_str_radix(hex, 16)
} else {
input.parse::<u64>()
}
.map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
Felt::try_from(n).map_err(|_| TemplateTypeError::ConversionError(input.to_string()))
}
}
impl TemplateFelt for TokenSymbol {
fn type_name() -> TemplateType {
TemplateType::new("token_symbol").expect("type is well formed")
}
fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
let token = TokenSymbol::new(input)
.map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
Ok(Felt::from(token))
}
}
#[derive(Debug, Error)]
#[error("error parsing word: {0}")]
struct WordParseError(String);
fn pad_hex_string(input: &str) -> String {
if input.starts_with("0x") && input.len() < 66 {
let hex_part = &input[2..];
let padding = "0".repeat(64 - hex_part.len());
format!("0x{}{}", padding, hex_part)
} else {
input.to_string()
}
}
impl TemplateWord for Word {
fn type_name() -> TemplateType {
TemplateType::native_word()
}
fn parse_word(input: &str) -> Result<Word, TemplateTypeError> {
let padded_input = pad_hex_string(input);
Word::try_from(padded_input.as_str()).map_err(|err| {
TemplateTypeError::parse(
input.to_string(), Self::type_name(),
WordParseError(err.to_string()),
)
})
}
}
impl TemplateWord for rpo_falcon512::PublicKey {
fn type_name() -> TemplateType {
TemplateType::new("auth::rpo_falcon512::pub_key").expect("type is well formed")
}
fn parse_word(input: &str) -> Result<Word, TemplateTypeError> {
let padded_input = pad_hex_string(input);
Word::try_from(padded_input.as_str()).map_err(|err| {
TemplateTypeError::parse(
input.to_string(), Self::type_name(),
WordParseError(err.to_string()),
)
})
}
}
impl TemplateWord for ecdsa_k256_keccak::PublicKey {
fn type_name() -> TemplateType {
TemplateType::new("auth::ecdsa_k256_keccak::pub_key").expect("type is well formed")
}
fn parse_word(input: &str) -> Result<Word, TemplateTypeError> {
let padded_input = pad_hex_string(input);
Word::try_from(padded_input.as_str()).map_err(|err| {
TemplateTypeError::parse(
input.to_string(),
Self::type_name(),
WordParseError(err.to_string()),
)
})
}
}
type TemplateFeltConverter = fn(&str) -> Result<Felt, TemplateTypeError>;
type TemplateWordConverter = fn(&str) -> Result<Word, TemplateTypeError>;
#[derive(Clone, Debug, Default)]
pub struct TemplateRegistry {
felt: BTreeMap<TemplateType, TemplateFeltConverter>,
word: BTreeMap<TemplateType, TemplateWordConverter>,
}
impl TemplateRegistry {
pub fn new() -> Self {
Self { ..Default::default() }
}
pub fn register_felt_type<T: TemplateFelt + 'static>(&mut self) {
let key = T::type_name();
self.felt.insert(key, T::parse_felt);
}
pub fn register_word_type<T: TemplateWord + 'static>(&mut self) {
let key = T::type_name();
self.word.insert(key, T::parse_word);
}
pub fn try_parse_felt(
&self,
type_name: &TemplateType,
value: &str,
) -> Result<Felt, TemplateTypeError> {
let converter = self
.felt
.get(type_name)
.ok_or(TemplateTypeError::FeltTypeNotFound(type_name.clone()))?;
converter(value)
}
pub fn try_parse_word(
&self,
type_name: &TemplateType,
value: &str,
) -> Result<Word, TemplateTypeError> {
let converter = self
.word
.get(type_name)
.ok_or(TemplateTypeError::WordTypeNotFound(type_name.clone()))?;
converter(value)
}
pub fn contains_felt_type(&self, type_name: &TemplateType) -> bool {
self.felt.contains_key(type_name)
}
pub fn contains_word_type(&self, type_name: &TemplateType) -> bool {
self.word.contains_key(type_name)
}
}