1use alloc::vec::Vec;
2
3use super::{Account, AccountId, Felt, PartialAccount, ZERO, hash_account};
4use crate::errors::AccountError;
5use crate::transaction::memory::{
6 ACCT_CODE_COMMITMENT_OFFSET,
7 ACCT_DATA_MEM_SIZE,
8 ACCT_ID_AND_NONCE_OFFSET,
9 ACCT_ID_PREFIX_IDX,
10 ACCT_ID_SUFFIX_IDX,
11 ACCT_NONCE_IDX,
12 ACCT_STORAGE_COMMITMENT_OFFSET,
13 ACCT_VAULT_ROOT_OFFSET,
14 MemoryOffset,
15};
16use crate::utils::serde::{Deserializable, Serializable};
17use crate::{WORD_SIZE, Word, WordError};
18
19#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct AccountHeader {
33 id: AccountId,
34 nonce: Felt,
35 vault_root: Word,
36 storage_commitment: Word,
37 code_commitment: Word,
38}
39
40impl AccountHeader {
41 pub fn new(
45 id: AccountId,
46 nonce: Felt,
47 vault_root: Word,
48 storage_commitment: Word,
49 code_commitment: Word,
50 ) -> Self {
51 Self {
52 id,
53 nonce,
54 vault_root,
55 storage_commitment,
56 code_commitment,
57 }
58 }
59
60 pub(crate) fn try_from_elements(elements: &[Felt]) -> Result<AccountHeader, AccountError> {
64 if elements.len() != ACCT_DATA_MEM_SIZE {
65 return Err(AccountError::HeaderDataIncorrectLength {
66 actual: elements.len(),
67 expected: ACCT_DATA_MEM_SIZE,
68 });
69 }
70
71 let id = AccountId::try_from([
72 elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_ID_PREFIX_IDX],
73 elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_ID_SUFFIX_IDX],
74 ])
75 .map_err(AccountError::FinalAccountHeaderIdParsingFailed)?;
76 let nonce = elements[ACCT_ID_AND_NONCE_OFFSET as usize + ACCT_NONCE_IDX];
77 let vault_root = parse_word(elements, ACCT_VAULT_ROOT_OFFSET)
78 .expect("we should have sliced off exactly 4 bytes");
79 let storage_commitment = parse_word(elements, ACCT_STORAGE_COMMITMENT_OFFSET)
80 .expect("we should have sliced off exactly 4 bytes");
81 let code_commitment = parse_word(elements, ACCT_CODE_COMMITMENT_OFFSET)
82 .expect("we should have sliced off exactly 4 bytes");
83
84 Ok(AccountHeader::new(id, nonce, vault_root, storage_commitment, code_commitment))
85 }
86
87 pub fn commitment(&self) -> Word {
96 hash_account(
97 self.id,
98 self.nonce,
99 self.vault_root,
100 self.storage_commitment,
101 self.code_commitment,
102 )
103 }
104
105 pub fn id(&self) -> AccountId {
107 self.id
108 }
109
110 pub fn nonce(&self) -> Felt {
112 self.nonce
113 }
114
115 pub fn vault_root(&self) -> Word {
117 self.vault_root
118 }
119
120 pub fn storage_commitment(&self) -> Word {
122 self.storage_commitment
123 }
124
125 pub fn code_commitment(&self) -> Word {
127 self.code_commitment
128 }
129
130 pub fn as_elements(&self) -> Vec<Felt> {
143 [
144 &[self.id.suffix(), self.id.prefix().as_felt(), ZERO, self.nonce],
145 self.vault_root.as_elements(),
146 self.storage_commitment.as_elements(),
147 self.code_commitment.as_elements(),
148 ]
149 .concat()
150 }
151}
152
153impl From<PartialAccount> for AccountHeader {
154 fn from(account: PartialAccount) -> Self {
155 (&account).into()
156 }
157}
158
159impl From<&PartialAccount> for AccountHeader {
160 fn from(account: &PartialAccount) -> Self {
161 Self {
162 id: account.id(),
163 nonce: account.nonce(),
164 vault_root: account.vault().root(),
165 storage_commitment: account.storage().commitment(),
166 code_commitment: account.code().commitment(),
167 }
168 }
169}
170
171impl From<Account> for AccountHeader {
172 fn from(account: Account) -> Self {
173 (&account).into()
174 }
175}
176
177impl From<&Account> for AccountHeader {
178 fn from(account: &Account) -> Self {
179 Self {
180 id: account.id(),
181 nonce: account.nonce(),
182 vault_root: account.vault().root(),
183 storage_commitment: account.storage().to_commitment(),
184 code_commitment: account.code().commitment(),
185 }
186 }
187}
188
189impl Serializable for AccountHeader {
190 fn write_into<W: miden_core::utils::ByteWriter>(&self, target: &mut W) {
191 self.id.write_into(target);
192 self.nonce.write_into(target);
193 self.vault_root.write_into(target);
194 self.storage_commitment.write_into(target);
195 self.code_commitment.write_into(target);
196 }
197}
198
199impl Deserializable for AccountHeader {
200 fn read_from<R: miden_core::utils::ByteReader>(
201 source: &mut R,
202 ) -> Result<Self, miden_processor::DeserializationError> {
203 let id = AccountId::read_from(source)?;
204 let nonce = Felt::read_from(source)?;
205 let vault_root = Word::read_from(source)?;
206 let storage_commitment = Word::read_from(source)?;
207 let code_commitment = Word::read_from(source)?;
208
209 Ok(AccountHeader {
210 id,
211 nonce,
212 vault_root,
213 storage_commitment,
214 code_commitment,
215 })
216 }
217}
218
219fn parse_word(data: &[Felt], offset: MemoryOffset) -> Result<Word, WordError> {
224 Word::try_from(&data[offset as usize..offset as usize + WORD_SIZE])
225}
226
227#[cfg(test)]
231mod tests {
232 use miden_core::Felt;
233 use miden_core::utils::{Deserializable, Serializable};
234
235 use super::AccountHeader;
236 use crate::Word;
237 use crate::account::StorageSlotContent;
238 use crate::account::tests::build_account;
239 use crate::asset::FungibleAsset;
240
241 #[test]
242 fn test_serde_account_storage() {
243 let init_nonce = Felt::new(1);
244 let asset_0 = FungibleAsset::mock(99);
245 let word = Word::from([1, 2, 3, 4u32]);
246 let storage_slot = StorageSlotContent::Value(word);
247 let account = build_account(vec![asset_0], init_nonce, vec![storage_slot]);
248
249 let account_header: AccountHeader = account.into();
250
251 let header_bytes = account_header.to_bytes();
252 let deserialized_header = AccountHeader::read_from_bytes(&header_bytes).unwrap();
253 assert_eq!(deserialized_header, account_header);
254 }
255}