use alloc::string::ToString;
use alloc::vec::Vec;
use super::{
AccountError,
AccountStorageDelta,
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Felt,
Serializable,
Word,
};
use crate::account::AccountComponent;
use crate::crypto::SequentialCommit;
mod slot;
pub use slot::{StorageSlot, StorageSlotContent, StorageSlotId, StorageSlotName, StorageSlotType};
mod map;
pub use map::{PartialStorageMap, StorageMap, StorageMapKey, StorageMapKeyHash, StorageMapWitness};
mod header;
pub use header::{AccountStorageHeader, StorageSlotHeader};
mod partial;
pub use partial::PartialStorage;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct AccountStorage {
slots: Vec<StorageSlot>,
}
impl AccountStorage {
pub const MAX_NUM_STORAGE_SLOTS: usize = 255;
pub fn new(mut slots: Vec<StorageSlot>) -> Result<AccountStorage, AccountError> {
let num_slots = slots.len();
if num_slots > Self::MAX_NUM_STORAGE_SLOTS {
return Err(AccountError::StorageTooManySlots(num_slots as u64));
}
slots.sort_unstable_by(|a, b| a.name().cmp(b.name()));
for slots in slots.windows(2) {
if slots[0].id() == slots[1].id() {
return Err(AccountError::DuplicateStorageSlotName(slots[0].name().clone()));
}
}
Ok(Self { slots })
}
pub(super) fn from_components(
components: Vec<AccountComponent>,
) -> Result<AccountStorage, AccountError> {
let storage_slots = components
.into_iter()
.flat_map(|component| {
let AccountComponent { storage_slots, .. } = component;
storage_slots.into_iter()
})
.collect();
Self::new(storage_slots)
}
pub fn to_elements(&self) -> Vec<Felt> {
<Self as SequentialCommit>::to_elements(self)
}
pub fn to_commitment(&self) -> Word {
<Self as SequentialCommit>::to_commitment(self)
}
pub fn num_slots(&self) -> u8 {
self.slots.len() as u8
}
pub fn slots(&self) -> &[StorageSlot] {
&self.slots
}
pub fn into_slots(self) -> Vec<StorageSlot> {
self.slots
}
pub fn to_header(&self) -> AccountStorageHeader {
AccountStorageHeader::new(self.slots.iter().map(StorageSlotHeader::from).collect())
.expect("slots should be valid as ensured by AccountStorage")
}
pub fn get(&self, slot_name: &StorageSlotName) -> Option<&StorageSlot> {
self.slots.iter().find(|slot| slot.name().id() == slot_name.id())
}
fn get_mut(&mut self, slot_name: &StorageSlotName) -> Option<&mut StorageSlot> {
self.slots.iter_mut().find(|slot| slot.name().id() == slot_name.id())
}
pub fn get_item(&self, slot_name: &StorageSlotName) -> Result<Word, AccountError> {
self.get(slot_name)
.map(|slot| slot.content().value())
.ok_or_else(|| AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() })
}
pub fn get_map_item(
&self,
slot_name: &StorageSlotName,
key: Word,
) -> Result<Word, AccountError> {
self.get(slot_name)
.ok_or_else(|| AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() })
.and_then(|slot| match slot.content() {
StorageSlotContent::Map(map) => Ok(map.get(&StorageMapKey::from_raw(key))),
_ => Err(AccountError::StorageSlotNotMap(slot_name.clone())),
})
}
pub(super) fn apply_delta(&mut self, delta: &AccountStorageDelta) -> Result<(), AccountError> {
for (slot_name, &value) in delta.values() {
self.set_item(slot_name, value)?;
}
for (slot_name, map_delta) in delta.maps() {
let slot = self
.get_mut(slot_name)
.ok_or(AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() })?;
let storage_map = match slot.content_mut() {
StorageSlotContent::Map(map) => map,
_ => return Err(AccountError::StorageSlotNotMap(slot_name.clone())),
};
storage_map.apply_delta(map_delta)?;
}
Ok(())
}
pub fn set_item(
&mut self,
slot_name: &StorageSlotName,
value: Word,
) -> Result<Word, AccountError> {
let slot = self.get_mut(slot_name).ok_or_else(|| {
AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() }
})?;
let StorageSlotContent::Value(old_value) = slot.content() else {
return Err(AccountError::StorageSlotNotValue(slot_name.clone()));
};
let old_value = *old_value;
let mut new_slot = StorageSlotContent::Value(value);
core::mem::swap(slot.content_mut(), &mut new_slot);
Ok(old_value)
}
pub fn set_map_item(
&mut self,
slot_name: &StorageSlotName,
key: StorageMapKey,
value: Word,
) -> Result<(Word, Word), AccountError> {
let slot = self.get_mut(slot_name).ok_or_else(|| {
AccountError::StorageSlotNameNotFound { slot_name: slot_name.clone() }
})?;
let StorageSlotContent::Map(storage_map) = slot.content_mut() else {
return Err(AccountError::StorageSlotNotMap(slot_name.clone()));
};
let old_root = storage_map.root();
let old_value = storage_map.insert(key, value)?;
Ok((old_root, old_value))
}
}
impl IntoIterator for AccountStorage {
type Item = StorageSlot;
type IntoIter = alloc::vec::IntoIter<StorageSlot>;
fn into_iter(self) -> Self::IntoIter {
self.slots.into_iter()
}
}
impl SequentialCommit for AccountStorage {
type Commitment = Word;
fn to_elements(&self) -> Vec<Felt> {
self.slots()
.iter()
.flat_map(|slot| {
StorageSlotHeader::new(
slot.name().clone(),
slot.content().slot_type(),
slot.content().value(),
)
.to_elements()
})
.collect()
}
}
impl Serializable for AccountStorage {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_u8(self.slots().len() as u8);
target.write_many(self.slots());
}
fn get_size_hint(&self) -> usize {
let u8_size = 0u8.get_size_hint();
let mut size = u8_size;
for slot in self.slots() {
size += slot.get_size_hint();
}
size
}
}
impl Deserializable for AccountStorage {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let num_slots = source.read_u8()? as usize;
let slots = source.read_many_iter::<StorageSlot>(num_slots)?.collect::<Result<_, _>>()?;
Self::new(slots).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use super::{AccountStorage, Deserializable, Serializable};
use crate::account::{AccountStorageHeader, StorageSlot, StorageSlotHeader, StorageSlotName};
use crate::errors::AccountError;
#[test]
fn test_serde_account_storage() -> anyhow::Result<()> {
let storage = AccountStorage::new(vec![]).unwrap();
let bytes = storage.to_bytes();
assert_eq!(storage, AccountStorage::read_from_bytes(&bytes).unwrap());
let storage = AccountStorage::new(vec![
StorageSlot::with_empty_value(StorageSlotName::new("miden::test::value")?),
StorageSlot::with_empty_map(StorageSlotName::new("miden::test::map")?),
])
.unwrap();
let bytes = storage.to_bytes();
assert_eq!(storage, AccountStorage::read_from_bytes(&bytes).unwrap());
Ok(())
}
#[test]
fn test_get_slot_by_name() -> anyhow::Result<()> {
let counter_slot = StorageSlotName::new("miden::test::counter")?;
let map_slot = StorageSlotName::new("miden::test::map")?;
let slots = vec![
StorageSlot::with_empty_value(counter_slot.clone()),
StorageSlot::with_empty_map(map_slot.clone()),
];
let storage = AccountStorage::new(slots.clone())?;
assert_eq!(storage.get(&counter_slot).unwrap(), &slots[0]);
assert_eq!(storage.get(&map_slot).unwrap(), &slots[1]);
Ok(())
}
#[test]
fn test_account_storage_and_header_fail_on_duplicate_slot_name() -> anyhow::Result<()> {
let slot_name0 = StorageSlotName::mock(0);
let slot_name1 = StorageSlotName::mock(1);
let slot_name2 = StorageSlotName::mock(2);
let mut slots = vec![
StorageSlot::with_empty_value(slot_name0.clone()),
StorageSlot::with_empty_value(slot_name1.clone()),
StorageSlot::with_empty_map(slot_name0.clone()),
StorageSlot::with_empty_value(slot_name2.clone()),
];
let err = AccountStorage::new(slots.clone()).unwrap_err();
assert_matches!(err, AccountError::DuplicateStorageSlotName(name) => {
assert_eq!(name, slot_name0);
});
slots.sort_unstable_by(|a, b| a.name().cmp(b.name()));
let err = AccountStorageHeader::new(slots.iter().map(StorageSlotHeader::from).collect())
.unwrap_err();
assert_matches!(err, AccountError::DuplicateStorageSlotName(name) => {
assert_eq!(name, slot_name0);
});
Ok(())
}
}