miden_objects/account/storage/
header.rs

1use alloc::vec::Vec;
2
3use vm_processor::Digest;
4
5use super::{AccountStorage, Felt, Hasher, StorageSlot, StorageSlotType, Word};
6use crate::{
7    AccountError, ZERO,
8    utils::serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
9};
10
11// ACCOUNT STORAGE HEADER
12// ================================================================================================
13
14/// Storage slot header is a lighter version of the [StorageSlot] storing only the type and the
15/// top-level value for the slot, and being, in fact, just a thin wrapper around a tuple.
16///
17/// That is, for complex storage slot (e.g., storage map), the header contains only the commitment
18/// to the underlying data.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct StorageSlotHeader(StorageSlotType, Word);
21
22impl StorageSlotHeader {
23    /// Returns a new instance of storage slot header from the provided storage slot type and value.
24    pub fn new(value: &(StorageSlotType, Word)) -> Self {
25        Self(value.0, value.1)
26    }
27
28    /// Returns this storage slot header as field elements.
29    ///
30    /// This is done by converting this storage slot into 8 field elements as follows:
31    /// ```text
32    /// [SLOT_VALUE, slot_type, 0, 0, 0]
33    /// ```
34    pub fn as_elements(&self) -> [Felt; StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT] {
35        let mut elements = [ZERO; StorageSlot::NUM_ELEMENTS_PER_STORAGE_SLOT];
36        elements[0..4].copy_from_slice(&self.1);
37        elements[4..8].copy_from_slice(&self.0.as_word());
38        elements
39    }
40}
41
42impl From<&StorageSlot> for StorageSlotHeader {
43    fn from(value: &StorageSlot) -> Self {
44        Self(value.slot_type(), value.value())
45    }
46}
47
48/// Account storage header is a lighter version of the [AccountStorage] storing only the type and
49/// the top-level value for each storage slot.
50///
51/// That is, for complex storage slots (e.g., storage maps), the header contains only the commitment
52/// to the underlying data.
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct AccountStorageHeader {
55    slots: Vec<(StorageSlotType, Word)>,
56}
57
58impl AccountStorageHeader {
59    // CONSTRUCTOR
60    // --------------------------------------------------------------------------------------------
61
62    /// Returns a new instance of account storage header initialized with the provided slots.
63    ///
64    /// # Panics
65    /// - If the number of provided slots is greater than [AccountStorage::MAX_NUM_STORAGE_SLOTS].
66    pub fn new(slots: Vec<(StorageSlotType, Word)>) -> Self {
67        assert!(slots.len() <= AccountStorage::MAX_NUM_STORAGE_SLOTS);
68        Self { slots }
69    }
70
71    // PUBLIC ACCESSORS
72    // --------------------------------------------------------------------------------------------
73
74    /// Returns an iterator over the storage header slots.
75    pub fn slots(&self) -> impl Iterator<Item = &(StorageSlotType, Word)> {
76        self.slots.iter()
77    }
78
79    /// Returns the number of slots contained in the storage header.
80    pub fn num_slots(&self) -> u8 {
81        // SAFETY: The constructors of this type ensure this value fits in a u8.
82        self.slots.len() as u8
83    }
84
85    /// Returns a slot contained in the storage header at a given index.
86    ///
87    /// # Errors
88    /// - If the index is out of bounds.
89    pub fn slot(&self, index: usize) -> Result<&(StorageSlotType, Word), AccountError> {
90        self.slots.get(index).ok_or(AccountError::StorageIndexOutOfBounds {
91            slots_len: self.slots.len() as u8,
92            index: index as u8,
93        })
94    }
95
96    // NOTE: The way of computing the commitment should be kept in sync with `AccountStorage`
97    /// Computes the account storage header commitment.
98    pub fn compute_commitment(&self) -> Digest {
99        Hasher::hash_elements(&self.as_elements())
100    }
101
102    /// Converts storage slots of this account storage header into a vector of field elements.
103    ///
104    /// This is done by first converting each storage slot into exactly 8 elements as follows:
105    /// ```text
106    /// [STORAGE_SLOT_VALUE, storage_slot_type, 0, 0, 0]
107    /// ```
108    /// And then concatenating the resulting elements into a single vector.
109    pub fn as_elements(&self) -> Vec<Felt> {
110        self.slots
111            .iter()
112            .flat_map(|slot| StorageSlotHeader::new(slot).as_elements())
113            .collect()
114    }
115}
116
117impl From<&AccountStorage> for AccountStorageHeader {
118    fn from(value: &AccountStorage) -> Self {
119        value.to_header()
120    }
121}
122
123// SERIALIZATION
124// ================================================================================================
125
126impl Serializable for AccountStorageHeader {
127    fn write_into<W: ByteWriter>(&self, target: &mut W) {
128        let len = self.slots.len() as u8;
129        target.write_u8(len);
130        target.write_many(self.slots())
131    }
132}
133
134impl Deserializable for AccountStorageHeader {
135    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
136        let len = source.read_u8()?;
137        let slots = source.read_many(len as usize)?;
138        // number of storage slots is guaranteed to be smaller than or equal to 255
139        Ok(Self::new(slots))
140    }
141}
142
143// TESTS
144// ================================================================================================
145
146#[cfg(test)]
147mod tests {
148    use vm_core::{
149        Felt,
150        utils::{Deserializable, Serializable},
151    };
152
153    use super::AccountStorageHeader;
154    use crate::account::{AccountStorage, StorageSlotType};
155
156    #[test]
157    fn test_from_account_storage() {
158        let storage_map = AccountStorage::mock_map();
159
160        // create new storage header from AccountStorage
161        let slots = vec![
162            (StorageSlotType::Value, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]),
163            (StorageSlotType::Value, [Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]),
164            (StorageSlotType::Map, storage_map.root().into()),
165        ];
166
167        let expected_header = AccountStorageHeader { slots };
168        let account_storage = AccountStorage::mock();
169
170        assert_eq!(expected_header, AccountStorageHeader::from(&account_storage))
171    }
172
173    #[test]
174    fn test_serde_account_storage_header() {
175        // create new storage header
176        let storage = AccountStorage::mock();
177        let storage_header = AccountStorageHeader::from(&storage);
178
179        // serde storage header
180        let bytes = storage_header.to_bytes();
181        let deserialized = AccountStorageHeader::read_from_bytes(&bytes).unwrap();
182
183        // assert deserialized == storage header
184        assert_eq!(storage_header, deserialized);
185    }
186}