use alloc::boxed::Box;
use alloc::collections::BTreeSet;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::iter;
use super::placeholder::{PlaceholderTypeRequirement, TEMPLATE_REGISTRY, TemplateType};
use super::{
FieldIdentifier,
InitStorageData,
MapEntry,
StorageValueName,
TemplateRequirementsIter,
};
use crate::account::StorageMap;
use crate::account::component::template::AccountComponentTemplateError;
use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
use crate::{Felt, FieldElement, Word};
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(clippy::large_enum_variant)]
pub enum WordRepresentation {
Template {
r#type: TemplateType,
identifier: FieldIdentifier,
},
Value {
identifier: Option<FieldIdentifier>,
value: [FeltRepresentation; 4],
},
}
impl WordRepresentation {
pub fn new_template(r#type: TemplateType, identifier: FieldIdentifier) -> Self {
WordRepresentation::Template { r#type, identifier }
}
pub fn new_value(
value: impl Into<[FeltRepresentation; 4]>,
identifier: Option<FieldIdentifier>,
) -> Self {
WordRepresentation::Value { identifier, value: value.into() }
}
pub fn with_description(self, description: impl Into<String>) -> Self {
match self {
WordRepresentation::Template { r#type, identifier } => WordRepresentation::Template {
r#type,
identifier: FieldIdentifier {
name: identifier.name,
description: Some(description.into()),
},
},
WordRepresentation::Value { identifier, value } => WordRepresentation::Value {
identifier: identifier.map(|id| FieldIdentifier {
name: id.name,
description: Some(description.into()),
}),
value,
},
}
}
pub fn name(&self) -> Option<&StorageValueName> {
match self {
WordRepresentation::Template { identifier, .. } => Some(&identifier.name),
WordRepresentation::Value { identifier, .. } => identifier.as_ref().map(|id| &id.name),
}
}
pub fn description(&self) -> Option<&str> {
match self {
WordRepresentation::Template { identifier, .. } => identifier.description.as_deref(),
WordRepresentation::Value { identifier, .. } => {
identifier.as_ref().and_then(|id| id.description.as_deref())
},
}
}
pub fn word_type(&self) -> TemplateType {
match self {
WordRepresentation::Template { r#type, .. } => r#type.clone(),
WordRepresentation::Value { .. } => TemplateType::native_word(),
}
}
pub fn value(&self) -> Option<&[FeltRepresentation; 4]> {
match self {
WordRepresentation::Value { value, .. } => Some(value),
WordRepresentation::Template { .. } => None,
}
}
pub fn template_requirements(
&self,
placeholder_prefix: StorageValueName,
) -> TemplateRequirementsIter<'_> {
let placeholder_key =
placeholder_prefix.with_suffix(self.name().unwrap_or(&StorageValueName::empty()));
match self {
WordRepresentation::Template { identifier, r#type } => Box::new(iter::once((
placeholder_key,
PlaceholderTypeRequirement {
description: identifier.description.clone(),
r#type: r#type.clone(),
},
))),
WordRepresentation::Value { value, .. } => Box::new(
value
.iter()
.flat_map(move |felt| felt.template_requirements(placeholder_key.clone())),
),
}
}
pub(crate) fn try_build_word(
&self,
init_storage_data: &InitStorageData,
placeholder_prefix: StorageValueName,
) -> Result<Word, AccountComponentTemplateError> {
match self {
WordRepresentation::Template { identifier, r#type } => {
let placeholder_path = placeholder_prefix.with_suffix(&identifier.name);
let maybe_value = init_storage_data.get(&placeholder_path);
if let Some(value) = maybe_value {
let parsed_value = TEMPLATE_REGISTRY
.try_parse_word(r#type, value)
.map_err(AccountComponentTemplateError::StorageValueParsingError)?;
Ok(parsed_value)
} else {
Err(AccountComponentTemplateError::PlaceholderValueNotProvided(
placeholder_path,
))
}
},
WordRepresentation::Value { value, identifier } => {
let mut result = [Felt::ZERO; 4];
for (index, felt_repr) in value.iter().enumerate() {
let placeholder = placeholder_prefix.clone().with_suffix(
identifier
.as_ref()
.map(|id| &id.name)
.unwrap_or(&StorageValueName::empty()),
);
result[index] = felt_repr.try_build_felt(init_storage_data, placeholder)?;
}
Ok(Word::from(result))
},
}
}
pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> {
let type_exists = TEMPLATE_REGISTRY.contains_word_type(&self.word_type());
if !type_exists {
return Err(AccountComponentTemplateError::InvalidType(
self.word_type().to_string(),
"Word".into(),
));
}
if let Some(felts) = self.value() {
for felt in felts {
felt.validate()?;
}
}
Ok(())
}
}
impl Serializable for WordRepresentation {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
match self {
WordRepresentation::Template { identifier, r#type } => {
target.write_u8(0);
target.write(identifier);
target.write(r#type);
},
WordRepresentation::Value { identifier, value } => {
target.write_u8(1);
target.write(identifier);
target.write(value);
},
}
}
}
impl Deserializable for WordRepresentation {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let tag = source.read_u8()?;
match tag {
0 => {
let identifier = FieldIdentifier::read_from(source)?;
let r#type = TemplateType::read_from(source)?;
Ok(WordRepresentation::Template { identifier, r#type })
},
1 => {
let identifier = Option::<FieldIdentifier>::read_from(source)?;
let value = <[FeltRepresentation; 4]>::read_from(source)?;
Ok(WordRepresentation::Value { identifier, value })
},
other => Err(DeserializationError::InvalidValue(format!(
"unknown tag '{other}' for WordRepresentation"
))),
}
}
}
impl From<[FeltRepresentation; 4]> for WordRepresentation {
fn from(value: [FeltRepresentation; 4]) -> Self {
WordRepresentation::new_value(value, Option::<FieldIdentifier>::None)
}
}
impl From<[Felt; 4]> for WordRepresentation {
fn from(value: [Felt; 4]) -> Self {
WordRepresentation::new_value(
value.map(FeltRepresentation::from),
Option::<FieldIdentifier>::None,
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FeltRepresentation {
Value {
identifier: Option<FieldIdentifier>,
value: Felt,
},
Template {
r#type: TemplateType,
identifier: FieldIdentifier,
},
}
impl FeltRepresentation {
pub fn new_value(value: impl Into<Felt>, name: Option<StorageValueName>) -> Self {
FeltRepresentation::Value {
value: value.into(),
identifier: name.map(FieldIdentifier::with_name),
}
}
pub fn new_template(r#type: TemplateType, name: StorageValueName) -> Self {
FeltRepresentation::Template {
r#type,
identifier: FieldIdentifier::with_name(name),
}
}
pub fn with_description(self, description: impl Into<String>) -> Self {
match self {
FeltRepresentation::Template { r#type, identifier } => FeltRepresentation::Template {
r#type,
identifier: FieldIdentifier {
name: identifier.name,
description: Some(description.into()),
},
},
FeltRepresentation::Value { identifier, value } => FeltRepresentation::Value {
identifier: identifier.map(|id| FieldIdentifier {
name: id.name,
description: Some(description.into()),
}),
value,
},
}
}
pub fn felt_type(&self) -> TemplateType {
match self {
FeltRepresentation::Template { r#type, .. } => r#type.clone(),
FeltRepresentation::Value { .. } => TemplateType::native_felt(),
}
}
pub(crate) fn try_build_felt(
&self,
init_storage_data: &InitStorageData,
placeholder_prefix: StorageValueName,
) -> Result<Felt, AccountComponentTemplateError> {
match self {
FeltRepresentation::Template { identifier, r#type } => {
let placeholder_key = placeholder_prefix.with_suffix(&identifier.name);
let raw_value = init_storage_data.get(&placeholder_key).ok_or(
AccountComponentTemplateError::PlaceholderValueNotProvided(placeholder_key),
)?;
Ok(TEMPLATE_REGISTRY
.try_parse_felt(r#type, raw_value)
.map_err(AccountComponentTemplateError::StorageValueParsingError)?)
},
FeltRepresentation::Value { value, .. } => Ok(*value),
}
}
pub fn template_requirements(
&self,
placeholder_prefix: StorageValueName,
) -> TemplateRequirementsIter<'_> {
match self {
FeltRepresentation::Template { identifier, r#type } => Box::new(iter::once((
placeholder_prefix.with_suffix(&identifier.name),
PlaceholderTypeRequirement {
description: identifier.description.clone(),
r#type: r#type.clone(),
},
))),
_ => Box::new(iter::empty()),
}
}
pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> {
let type_exists = TEMPLATE_REGISTRY.contains_felt_type(&self.felt_type());
if !type_exists {
return Err(AccountComponentTemplateError::InvalidType(
self.felt_type().to_string(),
"Felt".into(),
));
}
Ok(())
}
}
impl From<Felt> for FeltRepresentation {
fn from(value: Felt) -> Self {
FeltRepresentation::new_value(value, Option::<StorageValueName>::None)
}
}
impl Default for FeltRepresentation {
fn default() -> Self {
FeltRepresentation::new_value(Felt::default(), Option::<StorageValueName>::None)
}
}
impl Serializable for FeltRepresentation {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
match self {
FeltRepresentation::Value { identifier, value } => {
target.write_u8(0);
target.write(identifier);
target.write(value);
},
FeltRepresentation::Template { identifier, r#type } => {
target.write_u8(1);
target.write(identifier);
target.write(r#type);
},
}
}
}
impl Deserializable for FeltRepresentation {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let tag = source.read_u8()?;
match tag {
0 => {
let identifier = Option::<FieldIdentifier>::read_from(source)?;
let value = Felt::read_from(source)?;
Ok(FeltRepresentation::Value { value, identifier })
},
1 => {
let identifier = FieldIdentifier::read_from(source)?;
let r#type = TemplateType::read_from(source)?;
Ok(FeltRepresentation::Template { r#type, identifier })
},
other => Err(DeserializationError::InvalidValue(format!(
"unknown tag '{other}' for FeltRepresentation"
))),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))]
pub enum MapRepresentation {
Template {
identifier: FieldIdentifier,
},
Value {
identifier: FieldIdentifier,
entries: Vec<MapEntry>,
},
}
impl MapRepresentation {
pub fn new_value(entries: Vec<MapEntry>, name: impl Into<StorageValueName>) -> Self {
MapRepresentation::Value {
entries,
identifier: FieldIdentifier::with_name(name.into()),
}
}
pub fn new_template(name: impl Into<StorageValueName>) -> Self {
MapRepresentation::Template {
identifier: FieldIdentifier::with_name(name.into()),
}
}
pub fn with_description(self, description: impl Into<String>) -> Self {
match self {
MapRepresentation::Template { identifier } => MapRepresentation::Template {
identifier: FieldIdentifier {
name: identifier.name,
description: Some(description.into()),
},
},
MapRepresentation::Value { identifier, entries } => MapRepresentation::Value {
entries,
identifier: FieldIdentifier {
name: identifier.name,
description: Some(description.into()),
},
},
}
}
pub fn template_requirements(&self) -> TemplateRequirementsIter<'_> {
match self {
MapRepresentation::Template { identifier } => Box::new(iter::once((
identifier.name.clone(),
PlaceholderTypeRequirement {
description: identifier.description.clone(),
r#type: TemplateType::storage_map(),
},
))),
MapRepresentation::Value { identifier, entries } => Box::new(
entries
.iter()
.flat_map(move |entry| entry.template_requirements(identifier.name.clone())),
),
}
}
pub fn entries(&self) -> &[MapEntry] {
match self {
MapRepresentation::Value { entries, .. } => entries,
MapRepresentation::Template { .. } => &[],
}
}
pub fn name(&self) -> &StorageValueName {
match self {
MapRepresentation::Template { identifier }
| MapRepresentation::Value { identifier, .. } => &identifier.name,
}
}
pub fn description(&self) -> Option<&String> {
match self {
MapRepresentation::Template { identifier }
| MapRepresentation::Value { identifier, .. } => identifier.description.as_ref(),
}
}
pub fn len(&self) -> usize {
match self {
MapRepresentation::Value { entries, .. } => entries.len(),
MapRepresentation::Template { .. } => 0,
}
}
pub fn is_empty(&self) -> bool {
match self {
MapRepresentation::Value { entries, .. } => entries.is_empty(),
MapRepresentation::Template { .. } => true,
}
}
pub fn try_build_map(
&self,
init_storage_data: &InitStorageData,
) -> Result<StorageMap, AccountComponentTemplateError> {
match self {
MapRepresentation::Value { identifier, entries } => {
let entries = entries
.iter()
.map(|map_entry| {
let key = map_entry
.key()
.try_build_word(init_storage_data, identifier.name.clone())?;
let value = map_entry
.value()
.try_build_word(init_storage_data, identifier.name.clone())?;
Ok((key, value))
})
.collect::<Result<Vec<(Word, Word)>, _>>()?;
StorageMap::with_entries(entries).map_err(|err| {
AccountComponentTemplateError::StorageMapHasDuplicateKeys(Box::new(err))
})
},
MapRepresentation::Template { identifier } => {
if let Some(entries) = init_storage_data.map_entries(&identifier.name) {
return StorageMap::with_entries(entries.clone()).map_err(|err| {
AccountComponentTemplateError::StorageMapHasDuplicateKeys(Box::new(err))
});
}
Err(AccountComponentTemplateError::PlaceholderValueNotProvided(
identifier.name.clone(),
))
},
}
}
pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> {
match self {
MapRepresentation::Template { .. } => Ok(()),
MapRepresentation::Value { entries, .. } => {
let mut seen_keys = BTreeSet::new();
for entry in entries.iter() {
entry.key().validate()?;
entry.value().validate()?;
if let Ok(key) = entry
.key()
.try_build_word(&InitStorageData::default(), StorageValueName::empty())
&& !seen_keys.insert(key)
{
return Err(AccountComponentTemplateError::StorageMapHasDuplicateKeys(
Box::from(format!("key `{key}` is duplicated")),
));
}
}
Ok(())
},
}
}
}
impl Serializable for MapRepresentation {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
match self {
MapRepresentation::Value { identifier, entries } => {
target.write_u8(0u8);
target.write(identifier);
target.write(entries);
},
MapRepresentation::Template { identifier } => {
target.write_u8(1u8);
target.write(identifier);
},
}
}
}
impl Deserializable for MapRepresentation {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let tag = source.read_u8()?;
match tag {
0 => {
let identifier = FieldIdentifier::read_from(source)?;
let entries = Vec::<MapEntry>::read_from(source)?;
Ok(MapRepresentation::Value { entries, identifier })
},
1 => {
let identifier = FieldIdentifier::read_from(source)?;
Ok(MapRepresentation::Template { identifier })
},
other => Err(DeserializationError::InvalidValue(format!(
"unknown tag '{other}' for MapRepresentation"
))),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MultiWordRepresentation {
Value {
identifier: FieldIdentifier,
values: Vec<[FeltRepresentation; 4]>,
},
}
impl MultiWordRepresentation {
pub fn num_words(&self) -> usize {
match self {
MultiWordRepresentation::Value { values, .. } => values.len(),
}
}
pub fn validate(&self) -> Result<(), AccountComponentTemplateError> {
match self {
MultiWordRepresentation::Value { values, .. } => {
for slot_word in values {
for felt_in_slot in slot_word {
felt_in_slot.validate()?;
}
}
},
}
Ok(())
}
}
impl Serializable for MultiWordRepresentation {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
match self {
MultiWordRepresentation::Value { identifier, values } => {
target.write_u8(0u8);
target.write(identifier);
target.write(values);
},
}
}
}
impl Deserializable for MultiWordRepresentation {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let variant_tag = source.read_u8()?;
match variant_tag {
0 => {
let identifier: FieldIdentifier = source.read()?;
let values: Vec<[FeltRepresentation; 4]> = source.read()?;
Ok(MultiWordRepresentation::Value { identifier, values })
},
_ => Err(DeserializationError::InvalidValue(format!(
"unknown variant tag '{variant_tag}' for MultiWordRepresentation"
))),
}
}
}