Skip to main content

miden_protocol/account/
header.rs

1use alloc::vec::Vec;
2
3use super::{Account, AccountId, Felt, PartialAccount};
4use crate::crypto::SequentialCommit;
5use crate::errors::AccountError;
6use crate::transaction::memory::{
7    ACCT_CODE_COMMITMENT_OFFSET,
8    ACCT_DATA_MEM_SIZE,
9    ACCT_ID_AND_NONCE_OFFSET,
10    ACCT_ID_PREFIX_IDX,
11    ACCT_ID_SUFFIX_IDX,
12    ACCT_NONCE_IDX,
13    ACCT_STORAGE_COMMITMENT_OFFSET,
14    ACCT_VAULT_ROOT_OFFSET,
15    MemoryOffset,
16};
17use crate::utils::serde::{
18    ByteReader,
19    ByteWriter,
20    Deserializable,
21    DeserializationError,
22    Serializable,
23};
24use crate::{WORD_SIZE, Word, WordError};
25
26// ACCOUNT HEADER
27// ================================================================================================
28
29/// A header of an account which contains information that succinctly describes the state of the
30/// components of the account.
31///
32/// The [AccountHeader] is composed of:
33/// - id: the account ID ([`AccountId`]) of the account.
34/// - nonce: the nonce of the account.
35/// - vault_root: a commitment to the account's vault ([super::AssetVault]).
36/// - storage_commitment: a commitment to the account's storage ([super::AccountStorage]).
37/// - code_commitment: a commitment to the account's code ([super::AccountCode]).
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct AccountHeader {
40    id: AccountId,
41    nonce: Felt,
42    vault_root: Word,
43    storage_commitment: Word,
44    code_commitment: Word,
45}
46
47impl AccountHeader {
48    // CONSTRUCTORS
49    // --------------------------------------------------------------------------------------------
50    /// Creates a new [AccountHeader].
51    pub fn new(
52        id: AccountId,
53        nonce: Felt,
54        vault_root: Word,
55        storage_commitment: Word,
56        code_commitment: Word,
57    ) -> Self {
58        Self {
59            id,
60            nonce,
61            vault_root,
62            storage_commitment,
63            code_commitment,
64        }
65    }
66
67    /// Parses the account header data returned by the VM into individual account component
68    /// commitments. Returns a tuple of account ID, vault root, storage commitment, code
69    /// commitment, and nonce.
70    pub(crate) fn try_from_elements(elements: &[Felt]) -> Result<AccountHeader, AccountError> {
71        if elements.len() != ACCT_DATA_MEM_SIZE {
72            return Err(AccountError::HeaderDataIncorrectLength {
73                actual: elements.len(),
74                expected: ACCT_DATA_MEM_SIZE,
75            });
76        }
77
78        let id = AccountId::try_from_elements(
79            elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_ID_SUFFIX_IDX],
80            elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_ID_PREFIX_IDX],
81        )
82        .map_err(AccountError::FinalAccountHeaderIdParsingFailed)?;
83        let nonce = elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_NONCE_IDX];
84        let vault_root = parse_word(elements, ACCT_VAULT_ROOT_OFFSET)
85            .expect("we should have sliced off exactly 4 bytes");
86        let storage_commitment = parse_word(elements, ACCT_STORAGE_COMMITMENT_OFFSET)
87            .expect("we should have sliced off exactly 4 bytes");
88        let code_commitment = parse_word(elements, ACCT_CODE_COMMITMENT_OFFSET)
89            .expect("we should have sliced off exactly 4 bytes");
90
91        Ok(AccountHeader::new(id, nonce, vault_root, storage_commitment, code_commitment))
92    }
93
94    // PUBLIC ACCESSORS
95    // --------------------------------------------------------------------------------------------
96
97    /// Returns the commitment of this account.
98    ///
99    /// The commitment of an account is computed as a hash over the account header elements returned
100    /// by [`Self::to_elements`]. Computing the account commitment requires 2 permutations of the
101    /// hash function.
102    pub fn to_commitment(&self) -> Word {
103        <Self as SequentialCommit>::to_commitment(self)
104    }
105
106    /// Returns the id of this account.
107    pub fn id(&self) -> AccountId {
108        self.id
109    }
110
111    /// Returns the nonce of this account.
112    pub fn nonce(&self) -> Felt {
113        self.nonce
114    }
115
116    /// Returns the vault root of this account.
117    pub fn vault_root(&self) -> Word {
118        self.vault_root
119    }
120
121    /// Returns the storage commitment of this account.
122    pub fn storage_commitment(&self) -> Word {
123        self.storage_commitment
124    }
125
126    /// Returns the code commitment of this account.
127    pub fn code_commitment(&self) -> Word {
128        self.code_commitment
129    }
130
131    /// Returns the account header encoded to a vector of field elements.
132    ///
133    /// This is a vector of the following field elements:
134    /// ```text
135    /// [
136    ///     [account_nonce, 0, account_id_suffix, account_id_prefix],
137    ///     VAULT_ROOT,
138    ///     STORAGE_COMMITMENT,
139    ///     CODE_COMMITMENT,
140    /// ]
141    /// ```
142    pub fn to_elements(&self) -> Vec<Felt> {
143        <Self as SequentialCommit>::to_elements(self)
144    }
145}
146
147impl From<PartialAccount> for AccountHeader {
148    fn from(account: PartialAccount) -> Self {
149        (&account).into()
150    }
151}
152
153impl From<&PartialAccount> for AccountHeader {
154    fn from(account: &PartialAccount) -> Self {
155        Self {
156            id: account.id(),
157            nonce: account.nonce(),
158            vault_root: account.vault().root(),
159            storage_commitment: account.storage().commitment(),
160            code_commitment: account.code().commitment(),
161        }
162    }
163}
164
165impl From<Account> for AccountHeader {
166    fn from(account: Account) -> Self {
167        (&account).into()
168    }
169}
170
171impl From<&Account> for AccountHeader {
172    fn from(account: &Account) -> Self {
173        Self {
174            id: account.id(),
175            nonce: account.nonce(),
176            vault_root: account.vault().root(),
177            storage_commitment: account.storage().to_commitment(),
178            code_commitment: account.code().commitment(),
179        }
180    }
181}
182
183impl SequentialCommit for AccountHeader {
184    type Commitment = Word;
185
186    fn to_elements(&self) -> Vec<Felt> {
187        let mut id_nonce = Word::empty();
188        id_nonce[ACCT_NONCE_IDX] = self.nonce;
189        id_nonce[ACCT_ID_SUFFIX_IDX] = self.id.suffix();
190        id_nonce[ACCT_ID_PREFIX_IDX] = self.id.prefix().as_felt();
191
192        [
193            id_nonce.as_elements(),
194            self.vault_root.as_elements(),
195            self.storage_commitment.as_elements(),
196            self.code_commitment.as_elements(),
197        ]
198        .concat()
199    }
200}
201
202// SERIALIZATION
203// ================================================================================================
204
205impl Serializable for AccountHeader {
206    fn write_into<W: ByteWriter>(&self, target: &mut W) {
207        self.id.write_into(target);
208        self.nonce.write_into(target);
209        self.vault_root.write_into(target);
210        self.storage_commitment.write_into(target);
211        self.code_commitment.write_into(target);
212    }
213}
214
215impl Deserializable for AccountHeader {
216    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
217        let id = AccountId::read_from(source)?;
218        let nonce = Felt::read_from(source)?;
219        let vault_root = Word::read_from(source)?;
220        let storage_commitment = Word::read_from(source)?;
221        let code_commitment = Word::read_from(source)?;
222
223        Ok(AccountHeader {
224            id,
225            nonce,
226            vault_root,
227            storage_commitment,
228            code_commitment,
229        })
230    }
231}
232
233// HELPER FUNCTIONS
234// ================================================================================================
235
236/// Creates a new `Word` instance from the slice of `Felt`s using provided offset.
237fn parse_word(data: &[Felt], offset: MemoryOffset) -> Result<Word, WordError> {
238    Word::try_from(&data[offset as usize..offset as usize + WORD_SIZE])
239}
240
241// TESTS
242// ================================================================================================
243
244#[cfg(test)]
245mod tests {
246    use miden_core::Felt;
247
248    use super::AccountHeader;
249    use crate::Word;
250    use crate::account::StorageSlotContent;
251    use crate::account::tests::build_account;
252    use crate::asset::FungibleAsset;
253    use crate::utils::serde::{Deserializable, Serializable};
254
255    #[test]
256    fn test_serde_account_storage() {
257        let init_nonce = Felt::new(1);
258        let asset_0 = FungibleAsset::mock(99);
259        let word = Word::from([1, 2, 3, 4u32]);
260        let storage_slot = StorageSlotContent::Value(word);
261        let account = build_account(vec![asset_0], init_nonce, vec![storage_slot]);
262
263        let account_header: AccountHeader = account.into();
264
265        let header_bytes = account_header.to_bytes();
266        let deserialized_header = AccountHeader::read_from_bytes(&header_bytes).unwrap();
267        assert_eq!(deserialized_header, account_header);
268    }
269}