1use crate::{
2 asset::AssetVault,
3 utils::serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
4 AccountError, Digest, Felt, Hasher, Word, ZERO,
5};
6
7mod account_id;
8pub use account_id::{
9 AccountId, AccountIdAnchor, AccountIdPrefix, AccountIdPrefixV0, AccountIdV0, AccountIdVersion,
10 AccountStorageMode, AccountType,
11};
12
13pub mod auth;
14
15pub use auth::AuthSecretKey;
16
17mod builder;
18pub use builder::AccountBuilder;
19
20pub mod code;
21pub use code::{procedure::AccountProcedureInfo, AccountCode};
22
23mod component;
24pub use component::{
25 AccountComponent, AccountComponentMetadata, AccountComponentTemplate, FeltRepresentation,
26 InitStorageData, MapRepresentation, PlaceholderType, StorageEntry, StoragePlaceholder,
27 StorageValue, WordRepresentation,
28};
29
30pub mod delta;
31pub use delta::{
32 AccountDelta, AccountStorageDelta, AccountVaultDelta, FungibleAssetDelta,
33 NonFungibleAssetDelta, NonFungibleDeltaAction, StorageMapDelta,
34};
35
36mod storage;
37pub use storage::{AccountStorage, AccountStorageHeader, StorageMap, StorageSlot, StorageSlotType};
38
39mod header;
40pub use header::AccountHeader;
41
42mod data;
43pub use data::AccountData;
44
45#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct Account {
68 id: AccountId,
69 vault: AssetVault,
70 storage: AccountStorage,
71 code: AccountCode,
72 nonce: Felt,
73}
74
75impl Account {
76 pub fn from_parts(
81 id: AccountId,
82 vault: AssetVault,
83 storage: AccountStorage,
84 code: AccountCode,
85 nonce: Felt,
86 ) -> Self {
87 Self { id, vault, storage, code, nonce }
88 }
89
90 pub(super) fn initialize_from_components(
130 account_type: AccountType,
131 components: &[AccountComponent],
132 ) -> Result<(AccountCode, AccountStorage), AccountError> {
133 validate_components_support_account_type(components, account_type)?;
134
135 let code = AccountCode::from_components_unchecked(components, account_type)?;
136 let storage = AccountStorage::from_components(components, account_type)?;
137
138 Ok((code, storage))
139 }
140
141 pub fn builder(init_seed: [u8; 32]) -> AccountBuilder {
146 AccountBuilder::new(init_seed)
147 }
148
149 pub fn hash(&self) -> Digest {
158 hash_account(
159 self.id,
160 self.nonce,
161 self.vault.commitment(),
162 self.storage.commitment(),
163 self.code.commitment(),
164 )
165 }
166
167 pub fn init_hash(&self) -> Digest {
177 if self.is_new() {
178 Digest::default()
179 } else {
180 self.hash()
181 }
182 }
183
184 pub fn id(&self) -> AccountId {
186 self.id
187 }
188
189 pub fn account_type(&self) -> AccountType {
191 self.id.account_type()
192 }
193
194 pub fn vault(&self) -> &AssetVault {
196 &self.vault
197 }
198
199 pub fn storage(&self) -> &AccountStorage {
201 &self.storage
202 }
203
204 pub fn code(&self) -> &AccountCode {
206 &self.code
207 }
208
209 pub fn nonce(&self) -> Felt {
211 self.nonce
212 }
213
214 pub fn is_faucet(&self) -> bool {
216 self.id.is_faucet()
217 }
218
219 pub fn is_regular_account(&self) -> bool {
221 self.id.is_regular_account()
222 }
223
224 pub fn is_public(&self) -> bool {
226 self.id.is_public()
227 }
228
229 pub fn is_new(&self) -> bool {
231 self.nonce == ZERO
232 }
233
234 pub fn into_parts(self) -> (AccountId, AssetVault, AccountStorage, AccountCode, Felt) {
236 (self.id, self.vault, self.storage, self.code, self.nonce)
237 }
238
239 pub fn apply_delta(&mut self, delta: &AccountDelta) -> Result<(), AccountError> {
252 self.vault
255 .apply_delta(delta.vault())
256 .map_err(AccountError::AssetVaultUpdateError)?;
257
258 self.storage.apply_delta(delta.storage())?;
260
261 if let Some(nonce) = delta.nonce() {
263 self.set_nonce(nonce)?;
264 }
265
266 Ok(())
267 }
268
269 pub fn set_nonce(&mut self, nonce: Felt) -> Result<(), AccountError> {
276 if self.nonce.as_int() >= nonce.as_int() {
277 return Err(AccountError::NonceNotMonotonicallyIncreasing {
278 current: self.nonce.as_int(),
279 new: nonce.as_int(),
280 });
281 }
282
283 self.nonce = nonce;
284
285 Ok(())
286 }
287
288 #[cfg(any(feature = "testing", test))]
292 pub fn vault_mut(&mut self) -> &mut AssetVault {
294 &mut self.vault
295 }
296
297 #[cfg(any(feature = "testing", test))]
298 pub fn storage_mut(&mut self) -> &mut AccountStorage {
300 &mut self.storage
301 }
302}
303
304impl Serializable for Account {
308 fn write_into<W: ByteWriter>(&self, target: &mut W) {
309 let Account { id, vault, storage, code, nonce } = self;
310
311 id.write_into(target);
312 vault.write_into(target);
313 storage.write_into(target);
314 code.write_into(target);
315 nonce.write_into(target);
316 }
317
318 fn get_size_hint(&self) -> usize {
319 self.id.get_size_hint()
320 + self.vault.get_size_hint()
321 + self.storage.get_size_hint()
322 + self.code.get_size_hint()
323 + self.nonce.get_size_hint()
324 }
325}
326
327impl Deserializable for Account {
328 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
329 let id = AccountId::read_from(source)?;
330 let vault = AssetVault::read_from(source)?;
331 let storage = AccountStorage::read_from(source)?;
332 let code = AccountCode::read_from(source)?;
333 let nonce = Felt::read_from(source)?;
334
335 Ok(Self::from_parts(id, vault, storage, code, nonce))
336 }
337}
338
339pub fn hash_account(
348 id: AccountId,
349 nonce: Felt,
350 vault_root: Digest,
351 storage_commitment: Digest,
352 code_commitment: Digest,
353) -> Digest {
354 let mut elements = [ZERO; 16];
355 elements[0] = id.suffix();
356 elements[1] = id.prefix().as_felt();
357 elements[3] = nonce;
358 elements[4..8].copy_from_slice(&*vault_root);
359 elements[8..12].copy_from_slice(&*storage_commitment);
360 elements[12..].copy_from_slice(&*code_commitment);
361 Hasher::hash_elements(&elements)
362}
363
364fn validate_components_support_account_type(
366 components: &[AccountComponent],
367 account_type: AccountType,
368) -> Result<(), AccountError> {
369 for (component_index, component) in components.iter().enumerate() {
370 if !component.supports_type(account_type) {
371 return Err(AccountError::UnsupportedComponentForAccountType {
372 account_type,
373 component_index,
374 });
375 }
376 }
377
378 Ok(())
379}
380
381#[cfg(test)]
385mod tests {
386 use alloc::vec::Vec;
387
388 use assembly::Assembler;
389 use assert_matches::assert_matches;
390 use miden_crypto::{
391 utils::{Deserializable, Serializable},
392 Felt, Word,
393 };
394 use vm_processor::Digest;
395
396 use super::{
397 AccountCode, AccountDelta, AccountId, AccountStorage, AccountStorageDelta,
398 AccountVaultDelta,
399 };
400 use crate::{
401 account::{
402 Account, AccountComponent, AccountType, StorageMap, StorageMapDelta, StorageSlot,
403 },
404 asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset},
405 testing::{
406 account_id::ACCOUNT_ID_REGULAR_ACCOUNT_IMMUTABLE_CODE_ON_CHAIN,
407 storage::AccountStorageDeltaBuilder,
408 },
409 AccountError,
410 };
411
412 #[test]
413 fn test_serde_account() {
414 let init_nonce = Felt::new(1);
415 let asset_0 = FungibleAsset::mock(99);
416 let word = [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)];
417 let storage_slot = StorageSlot::Value(word);
418 let account = build_account(vec![asset_0], init_nonce, vec![storage_slot]);
419
420 let serialized = account.to_bytes();
421 let deserialized = Account::read_from_bytes(&serialized).unwrap();
422 assert_eq!(deserialized, account);
423 }
424
425 #[test]
426 fn test_serde_account_delta() {
427 let final_nonce = Felt::new(2);
428 let asset_0 = FungibleAsset::mock(15);
429 let asset_1 = NonFungibleAsset::mock(&[5, 5, 5]);
430 let storage_delta = AccountStorageDeltaBuilder::default()
431 .add_cleared_items([0])
432 .add_updated_values([(1_u8, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])])
433 .build()
434 .unwrap();
435 let account_delta =
436 build_account_delta(vec![asset_1], vec![asset_0], final_nonce, storage_delta);
437
438 let serialized = account_delta.to_bytes();
439 let deserialized = AccountDelta::read_from_bytes(&serialized).unwrap();
440 assert_eq!(deserialized, account_delta);
441 }
442
443 #[test]
444 fn valid_account_delta_is_correctly_applied() {
445 let init_nonce = Felt::new(1);
447 let asset_0 = FungibleAsset::mock(100);
448 let asset_1 = NonFungibleAsset::mock(&[1, 2, 3]);
449
450 let storage_slot_value_0 =
452 StorageSlot::Value([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]);
453 let storage_slot_value_1 =
454 StorageSlot::Value([Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]);
455 let mut storage_map = StorageMap::with_entries([
456 (
457 Digest::new([Felt::new(101), Felt::new(102), Felt::new(103), Felt::new(104)]),
458 [Felt::new(1_u64), Felt::new(2_u64), Felt::new(3_u64), Felt::new(4_u64)],
459 ),
460 (
461 Digest::new([Felt::new(105), Felt::new(106), Felt::new(107), Felt::new(108)]),
462 [Felt::new(5_u64), Felt::new(6_u64), Felt::new(7_u64), Felt::new(8_u64)],
463 ),
464 ]);
465 let storage_slot_map = StorageSlot::Map(storage_map.clone());
466
467 let mut account = build_account(
468 vec![asset_0],
469 init_nonce,
470 vec![storage_slot_value_0, storage_slot_value_1, storage_slot_map],
471 );
472
473 let new_map_entry = (
475 Digest::new([Felt::new(101), Felt::new(102), Felt::new(103), Felt::new(104)]),
476 [Felt::new(9_u64), Felt::new(10_u64), Felt::new(11_u64), Felt::new(12_u64)],
477 );
478
479 let updated_map =
480 StorageMapDelta::from_iters([], [(new_map_entry.0.into(), new_map_entry.1)]);
481 storage_map.insert(new_map_entry.0, new_map_entry.1);
482
483 let final_nonce = Felt::new(2);
485 let storage_delta = AccountStorageDeltaBuilder::default()
486 .add_cleared_items([0])
487 .add_updated_values([(1, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])])
488 .add_updated_maps([(2, updated_map)])
489 .build()
490 .unwrap();
491 let account_delta =
492 build_account_delta(vec![asset_1], vec![asset_0], final_nonce, storage_delta);
493
494 account.apply_delta(&account_delta).unwrap();
496
497 let final_account = build_account(
498 vec![asset_1],
499 final_nonce,
500 vec![
501 StorageSlot::Value(Word::default()),
502 StorageSlot::Value([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]),
503 StorageSlot::Map(storage_map),
504 ],
505 );
506
507 assert_eq!(account, final_account);
509 }
510
511 #[test]
512 #[should_panic]
513 fn valid_account_delta_with_unchanged_nonce() {
514 let init_nonce = Felt::new(1);
516 let asset = FungibleAsset::mock(110);
517 let mut account =
518 build_account(vec![asset], init_nonce, vec![StorageSlot::Value(Word::default())]);
519
520 let storage_delta = AccountStorageDeltaBuilder::default()
522 .add_cleared_items([0])
523 .add_updated_values([(1_u8, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])])
524 .build()
525 .unwrap();
526 let account_delta = build_account_delta(vec![], vec![asset], init_nonce, storage_delta);
527
528 account.apply_delta(&account_delta).unwrap()
530 }
531
532 #[test]
533 #[should_panic]
534 fn valid_account_delta_with_decremented_nonce() {
535 let init_nonce = Felt::new(2);
537 let asset = FungibleAsset::mock(100);
538 let mut account =
539 build_account(vec![asset], init_nonce, vec![StorageSlot::Value(Word::default())]);
540
541 let final_nonce = Felt::new(1);
543 let storage_delta = AccountStorageDeltaBuilder::default()
544 .add_cleared_items([0])
545 .add_updated_values([(1_u8, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])])
546 .build()
547 .unwrap();
548 let account_delta = build_account_delta(vec![], vec![asset], final_nonce, storage_delta);
549
550 account.apply_delta(&account_delta).unwrap()
552 }
553
554 #[test]
555 fn empty_account_delta_with_incremented_nonce() {
556 let init_nonce = Felt::new(1);
558 let word = [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)];
559 let storage_slot = StorageSlot::Value(word);
560 let mut account = build_account(vec![], init_nonce, vec![storage_slot]);
561
562 let final_nonce = Felt::new(2);
564 let account_delta = AccountDelta::new(
565 AccountStorageDelta::default(),
566 AccountVaultDelta::default(),
567 Some(final_nonce),
568 )
569 .unwrap();
570
571 account.apply_delta(&account_delta).unwrap()
573 }
574
575 pub fn build_account_delta(
576 added_assets: Vec<Asset>,
577 removed_assets: Vec<Asset>,
578 nonce: Felt,
579 storage_delta: AccountStorageDelta,
580 ) -> AccountDelta {
581 let vault_delta = AccountVaultDelta::from_iters(added_assets, removed_assets);
582 AccountDelta::new(storage_delta, vault_delta, Some(nonce)).unwrap()
583 }
584
585 pub fn build_account(assets: Vec<Asset>, nonce: Felt, slots: Vec<StorageSlot>) -> Account {
586 let id = AccountId::try_from(ACCOUNT_ID_REGULAR_ACCOUNT_IMMUTABLE_CODE_ON_CHAIN).unwrap();
587 let code = AccountCode::mock();
588
589 let vault = AssetVault::new(&assets).unwrap();
590
591 let storage = AccountStorage::new(slots).unwrap();
592
593 Account::from_parts(id, vault, storage, code, nonce)
594 }
595
596 #[test]
599 fn test_account_unsupported_component_type() {
600 let code1 = "export.foo add end";
601 let library1 = Assembler::default().assemble_library([code1]).unwrap();
602
603 let component1 = AccountComponent::new(library1, vec![])
605 .unwrap()
606 .with_supported_type(AccountType::FungibleFaucet)
607 .with_supported_type(AccountType::NonFungibleFaucet)
608 .with_supported_type(AccountType::RegularAccountImmutableCode);
609
610 let err = Account::initialize_from_components(
611 AccountType::RegularAccountUpdatableCode,
612 &[component1],
613 )
614 .unwrap_err();
615
616 assert!(matches!(
617 err,
618 AccountError::UnsupportedComponentForAccountType {
619 account_type: AccountType::RegularAccountUpdatableCode,
620 component_index: 0
621 }
622 ))
623 }
624
625 #[test]
628 fn test_account_duplicate_exported_mast_root() {
629 let code1 = "export.foo add eq.1 end";
630 let code2 = "export.bar add eq.1 end";
631
632 let library1 = Assembler::default().assemble_library([code1]).unwrap();
633 let library2 = Assembler::default().assemble_library([code2]).unwrap();
634
635 let component1 = AccountComponent::new(library1, vec![]).unwrap().with_supports_all_types();
636 let component2 = AccountComponent::new(library2, vec![]).unwrap().with_supports_all_types();
637
638 let err = Account::initialize_from_components(
639 AccountType::RegularAccountUpdatableCode,
640 &[component1, component2],
641 )
642 .unwrap_err();
643
644 assert_matches!(err, AccountError::AccountComponentDuplicateProcedureRoot(_))
645 }
646}