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