use alloc::string::ToString;
use alloc::vec::Vec;
use super::{
AccountError,
AccountStorageDelta,
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Felt,
Hasher,
Serializable,
Word,
};
use crate::account::{AccountComponent, AccountType};
mod slot;
pub use slot::{SlotName, StorageSlot, StorageSlotType};
mod map;
pub use map::{PartialStorageMap, StorageMap, 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(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));
}
Ok(Self { slots })
}
pub(super) fn from_components(
components: &[AccountComponent],
account_type: AccountType,
) -> Result<AccountStorage, AccountError> {
let mut storage_slots = match account_type {
AccountType::FungibleFaucet => vec![StorageSlot::empty_value()],
AccountType::NonFungibleFaucet => vec![StorageSlot::empty_map()],
_ => vec![],
};
storage_slots
.extend(components.iter().flat_map(|component| component.storage_slots()).cloned());
Self::new(storage_slots)
}
pub fn commitment(&self) -> Word {
build_slots_commitment(&self.slots)
}
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(|slot| (slot.slot_type(), slot.value())).collect(),
)
}
pub fn get_item(&self, index: u8) -> Result<Word, AccountError> {
self.slots
.get(index as usize)
.ok_or(AccountError::StorageIndexOutOfBounds {
slots_len: self.slots.len() as u8,
index,
})
.map(|slot| slot.value())
}
pub fn get_map_item(&self, index: u8, key: Word) -> Result<Word, AccountError> {
match self.slots.get(index as usize).ok_or(AccountError::StorageIndexOutOfBounds {
slots_len: self.slots.len() as u8,
index,
})? {
StorageSlot::Map(map) => Ok(map.get(&key)),
_ => Err(AccountError::StorageSlotNotMap(index)),
}
}
pub fn as_elements(&self) -> Vec<Felt> {
slots_as_elements(self.slots())
}
pub(super) fn apply_delta(&mut self, delta: &AccountStorageDelta) -> Result<(), AccountError> {
let len = self.slots.len() as u8;
for (&idx, map) in delta.maps().iter() {
let storage_slot = self
.slots
.get_mut(idx as usize)
.ok_or(AccountError::StorageIndexOutOfBounds { slots_len: len, index: idx })?;
let storage_map = match storage_slot {
StorageSlot::Map(map) => map,
_ => return Err(AccountError::StorageSlotNotMap(idx)),
};
storage_map.apply_delta(map)?;
}
for (&idx, &value) in delta.values().iter() {
self.set_item(idx, value)?;
}
Ok(())
}
pub fn set_item(&mut self, index: u8, value: Word) -> Result<Word, AccountError> {
let num_slots = self.slots.len();
if index as usize >= num_slots {
return Err(AccountError::StorageIndexOutOfBounds {
slots_len: self.slots.len() as u8,
index,
});
}
let old_value = match self.slots[index as usize] {
StorageSlot::Value(value) => value,
_ => return Err(AccountError::StorageSlotNotValue(index)),
};
self.slots[index as usize] = StorageSlot::Value(value);
Ok(old_value)
}
pub fn set_map_item(
&mut self,
index: u8,
key: Word,
value: Word,
) -> Result<(Word, Word), AccountError> {
let num_slots = self.slots.len();
if index as usize >= num_slots {
return Err(AccountError::StorageIndexOutOfBounds {
slots_len: self.slots.len() as u8,
index,
});
}
let storage_map = match self.slots[index as usize] {
StorageSlot::Map(ref mut map) => map,
_ => return Err(AccountError::StorageSlotNotMap(index)),
};
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()
}
}
fn slots_as_elements(slots: &[StorageSlot]) -> Vec<Felt> {
slots
.iter()
.flat_map(|slot| StorageSlotHeader::from(slot).as_elements())
.collect()
}
pub fn build_slots_commitment(slots: &[StorageSlot]) -> Word {
let elements = slots_as_elements(slots);
Hasher::hash_elements(&elements)
}
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::<StorageSlot>(num_slots)?;
Self::new(slots).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}
#[cfg(test)]
mod tests {
use super::{
AccountStorage,
Deserializable,
Serializable,
StorageMap,
Word,
build_slots_commitment,
};
use crate::account::StorageSlot;
#[test]
fn test_serde_account_storage() {
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::Value(Word::empty()),
StorageSlot::Map(StorageMap::default()),
])
.unwrap();
let bytes = storage.to_bytes();
assert_eq!(storage, AccountStorage::read_from_bytes(&bytes).unwrap());
}
#[test]
fn test_account_storage_slots_commitment() {
let storage = AccountStorage::mock();
let storage_slots_commitment = build_slots_commitment(storage.slots());
assert_eq!(storage_slots_commitment, storage.commitment())
}
}