use alloc::vec::Vec;
use super::{AccountStorage, Felt, Hasher, StorageSlot, StorageSlotType, Word};
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
use crate::{AccountError, ZERO};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StorageSlotHeader(StorageSlotType, Word);
impl StorageSlotHeader {
pub fn new(value: &(StorageSlotType, Word)) -> Self {
Self(value.0, value.1)
}
pub fn as_elements(&self) -> [Felt; StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT] {
let mut elements = [ZERO; StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT];
elements[0..4].copy_from_slice(self.1.as_elements());
elements[4..8].copy_from_slice(self.0.as_word().as_elements());
elements
}
}
impl From<&StorageSlot> for StorageSlotHeader {
fn from(value: &StorageSlot) -> Self {
Self(value.slot_type(), value.value())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccountStorageHeader {
slots: Vec<(StorageSlotType, Word)>,
}
impl AccountStorageHeader {
pub fn new(slots: Vec<(StorageSlotType, Word)>) -> Self {
assert!(slots.len() <= AccountStorage::MAX_NUM_STORAGE_SLOTS);
Self { slots }
}
pub fn slots(&self) -> impl Iterator<Item = &(StorageSlotType, Word)> {
self.slots.iter()
}
pub fn map_slot_roots(&self) -> impl Iterator<Item = Word> {
self.slots
.iter()
.filter(|(slot_type, _)| matches!(slot_type, StorageSlotType::Map))
.map(|x| x.1)
}
pub fn num_slots(&self) -> u8 {
self.slots.len() as u8
}
pub fn slot(&self, index: usize) -> Result<&(StorageSlotType, Word), AccountError> {
self.slots.get(index).ok_or(AccountError::StorageIndexOutOfBounds {
slots_len: self.slots.len() as u8,
index: index as u8,
})
}
pub fn compute_commitment(&self) -> Word {
Hasher::hash_elements(&self.as_elements())
}
pub fn is_map_slot(&self, index: usize) -> Result<bool, AccountError> {
match self.slot(index)?.0 {
StorageSlotType::Map => Ok(true),
StorageSlotType::Value => Ok(false),
}
}
pub fn as_elements(&self) -> Vec<Felt> {
self.slots
.iter()
.flat_map(|slot| StorageSlotHeader::new(slot).as_elements())
.collect()
}
}
impl From<&AccountStorage> for AccountStorageHeader {
fn from(value: &AccountStorage) -> Self {
value.to_header()
}
}
impl Serializable for AccountStorageHeader {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let len = self.slots.len() as u8;
target.write_u8(len);
target.write_many(self.slots())
}
}
impl Deserializable for AccountStorageHeader {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let len = source.read_u8()?;
let slots = source.read_many(len as usize)?;
Ok(Self::new(slots))
}
}
#[cfg(test)]
mod tests {
use miden_core::Felt;
use miden_core::utils::{Deserializable, Serializable};
use super::AccountStorageHeader;
use crate::Word;
use crate::account::{AccountStorage, StorageSlotType};
#[test]
fn test_from_account_storage() {
let storage_map = AccountStorage::mock_map();
let slots = vec![
(StorageSlotType::Value, Word::from([1, 2, 3, 4u32])),
(
StorageSlotType::Value,
Word::from([Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]),
),
(StorageSlotType::Map, storage_map.root()),
];
let expected_header = AccountStorageHeader { slots };
let account_storage = AccountStorage::mock();
assert_eq!(expected_header, AccountStorageHeader::from(&account_storage))
}
#[test]
fn test_serde_account_storage_header() {
let storage = AccountStorage::mock();
let storage_header = AccountStorageHeader::from(&storage);
let bytes = storage_header.to_bytes();
let deserialized = AccountStorageHeader::read_from_bytes(&bytes).unwrap();
assert_eq!(storage_header, deserialized);
}
}