1use crate::{
2 AccountError, Digest, Felt, Hasher, Word, ZERO,
3 asset::AssetVault,
4 utils::serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
5};
6
7mod account_id;
8pub use account_id::{
9 AccountId, AccountIdPrefix, AccountIdPrefixV0, AccountIdV0, AccountIdVersion,
10 AccountStorageMode, AccountType, AddressType, NetworkId,
11};
12
13pub mod auth;
14
15pub use auth::AuthSecretKey;
16
17mod builder;
18pub use builder::AccountBuilder;
19
20pub mod code;
21pub use code::{AccountCode, procedure::AccountProcedureInfo};
22
23pub mod component;
24pub use component::{
25 AccountComponent, AccountComponentMetadata, AccountComponentTemplate, FeltRepresentation,
26 InitStorageData, MapEntry, MapRepresentation, PlaceholderTypeRequirement, StorageEntry,
27 StorageValueName, StorageValueNameError, TemplateType, TemplateTypeError, 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::{
38 AccountStorage, AccountStorageHeader, PartialStorage, StorageMap, StorageSlot, StorageSlotType,
39};
40
41mod header;
42pub use header::AccountHeader;
43
44mod file;
45pub use file::AccountFile;
46
47mod partial;
48pub use partial::PartialAccount;
49
50#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct Account {
73 id: AccountId,
74 vault: AssetVault,
75 storage: AccountStorage,
76 code: AccountCode,
77 nonce: Felt,
78}
79
80impl Account {
81 pub fn from_parts(
86 id: AccountId,
87 vault: AssetVault,
88 storage: AccountStorage,
89 code: AccountCode,
90 nonce: Felt,
91 ) -> Self {
92 Self { id, vault, storage, code, nonce }
93 }
94
95 pub(super) fn initialize_from_components(
137 account_type: AccountType,
138 components: &[AccountComponent],
139 ) -> Result<(AccountCode, AccountStorage), AccountError> {
140 validate_components_support_account_type(components, account_type)?;
141
142 let code = AccountCode::from_components_unchecked(components, account_type)?;
143 let storage = AccountStorage::from_components(components, account_type)?;
144
145 Ok((code, storage))
146 }
147
148 pub fn builder(init_seed: [u8; 32]) -> AccountBuilder {
153 AccountBuilder::new(init_seed)
154 }
155
156 pub fn commitment(&self) -> Digest {
165 hash_account(
166 self.id,
167 self.nonce,
168 self.vault.root(),
169 self.storage.commitment(),
170 self.code.commitment(),
171 )
172 }
173
174 pub fn init_commitment(&self) -> Digest {
184 if self.is_new() {
185 Digest::default()
186 } else {
187 self.commitment()
188 }
189 }
190
191 pub fn id(&self) -> AccountId {
193 self.id
194 }
195
196 pub fn account_type(&self) -> AccountType {
198 self.id.account_type()
199 }
200
201 pub fn vault(&self) -> &AssetVault {
203 &self.vault
204 }
205
206 pub fn storage(&self) -> &AccountStorage {
208 &self.storage
209 }
210
211 pub fn code(&self) -> &AccountCode {
213 &self.code
214 }
215
216 pub fn nonce(&self) -> Felt {
218 self.nonce
219 }
220
221 pub fn is_faucet(&self) -> bool {
223 self.id.is_faucet()
224 }
225
226 pub fn is_regular_account(&self) -> bool {
228 self.id.is_regular_account()
229 }
230
231 pub fn is_onchain(&self) -> bool {
234 self.id().is_onchain()
235 }
236
237 pub fn is_public(&self) -> bool {
239 self.id().is_public()
240 }
241
242 pub fn is_private(&self) -> bool {
244 self.id().is_private()
245 }
246
247 pub fn is_network(&self) -> bool {
249 self.id().is_network()
250 }
251
252 pub fn is_new(&self) -> bool {
254 self.nonce == ZERO
255 }
256
257 pub fn into_parts(self) -> (AccountId, AssetVault, AccountStorage, AccountCode, Felt) {
259 (self.id, self.vault, self.storage, self.code, self.nonce)
260 }
261
262 pub fn apply_delta(&mut self, delta: &AccountDelta) -> Result<(), AccountError> {
275 self.vault
278 .apply_delta(delta.vault())
279 .map_err(AccountError::AssetVaultUpdateError)?;
280
281 self.storage.apply_delta(delta.storage())?;
283
284 self.increment_nonce(delta.nonce_delta())?;
286
287 Ok(())
288 }
289
290 pub fn increment_nonce(&mut self, nonce_delta: Felt) -> Result<(), AccountError> {
297 let new_nonce = self.nonce + nonce_delta;
298
299 if new_nonce.as_int() < self.nonce.as_int() {
300 return Err(AccountError::NonceOverflow {
301 current: self.nonce,
302 increment: nonce_delta,
303 new: new_nonce,
304 });
305 }
306
307 self.nonce = new_nonce;
308
309 Ok(())
310 }
311
312 #[cfg(any(feature = "testing", test))]
316 pub fn vault_mut(&mut self) -> &mut AssetVault {
318 &mut self.vault
319 }
320
321 #[cfg(any(feature = "testing", test))]
322 pub fn storage_mut(&mut self) -> &mut AccountStorage {
324 &mut self.storage
325 }
326}
327
328impl Serializable for Account {
332 fn write_into<W: ByteWriter>(&self, target: &mut W) {
333 let Account { id, vault, storage, code, nonce } = self;
334
335 id.write_into(target);
336 vault.write_into(target);
337 storage.write_into(target);
338 code.write_into(target);
339 nonce.write_into(target);
340 }
341
342 fn get_size_hint(&self) -> usize {
343 self.id.get_size_hint()
344 + self.vault.get_size_hint()
345 + self.storage.get_size_hint()
346 + self.code.get_size_hint()
347 + self.nonce.get_size_hint()
348 }
349}
350
351impl Deserializable for Account {
352 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
353 let id = AccountId::read_from(source)?;
354 let vault = AssetVault::read_from(source)?;
355 let storage = AccountStorage::read_from(source)?;
356 let code = AccountCode::read_from(source)?;
357 let nonce = Felt::read_from(source)?;
358
359 Ok(Self::from_parts(id, vault, storage, code, nonce))
360 }
361}
362
363pub fn hash_account(
372 id: AccountId,
373 nonce: Felt,
374 vault_root: Digest,
375 storage_commitment: Digest,
376 code_commitment: Digest,
377) -> Digest {
378 let mut elements = [ZERO; 16];
379 elements[0] = id.suffix();
380 elements[1] = id.prefix().as_felt();
381 elements[3] = nonce;
382 elements[4..8].copy_from_slice(&*vault_root);
383 elements[8..12].copy_from_slice(&*storage_commitment);
384 elements[12..].copy_from_slice(&*code_commitment);
385 Hasher::hash_elements(&elements)
386}
387
388fn validate_components_support_account_type(
390 components: &[AccountComponent],
391 account_type: AccountType,
392) -> Result<(), AccountError> {
393 for (component_index, component) in components.iter().enumerate() {
394 if !component.supports_type(account_type) {
395 return Err(AccountError::UnsupportedComponentForAccountType {
396 account_type,
397 component_index,
398 });
399 }
400 }
401
402 Ok(())
403}
404
405#[cfg(test)]
409mod tests {
410 use alloc::vec::Vec;
411
412 use assembly::Assembler;
413 use assert_matches::assert_matches;
414 use miden_crypto::{
415 Felt, Word,
416 utils::{Deserializable, Serializable},
417 };
418 use vm_processor::Digest;
419
420 use super::{
421 AccountCode, AccountDelta, AccountId, AccountStorage, AccountStorageDelta,
422 AccountVaultDelta,
423 };
424 use crate::{
425 AccountError,
426 account::{
427 Account, AccountComponent, AccountType, StorageMap, StorageMapDelta, StorageSlot,
428 },
429 asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset},
430 testing::{
431 account_component::NoopAuthComponent,
432 account_id::{
433 ACCOUNT_ID_PRIVATE_SENDER, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
434 },
435 storage::AccountStorageDeltaBuilder,
436 },
437 };
438
439 #[test]
440 fn test_serde_account() {
441 let init_nonce = Felt::new(1);
442 let asset_0 = FungibleAsset::mock(99);
443 let word = [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)];
444 let storage_slot = StorageSlot::Value(word);
445 let account = build_account(vec![asset_0], init_nonce, vec![storage_slot]);
446
447 let serialized = account.to_bytes();
448 let deserialized = Account::read_from_bytes(&serialized).unwrap();
449 assert_eq!(deserialized, account);
450 }
451
452 #[test]
453 fn test_serde_account_delta() {
454 let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap();
455 let nonce_delta = Felt::new(2);
456 let asset_0 = FungibleAsset::mock(15);
457 let asset_1 = NonFungibleAsset::mock(&[5, 5, 5]);
458 let storage_delta = AccountStorageDeltaBuilder::new()
459 .add_cleared_items([0])
460 .add_updated_values([(1_u8, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])])
461 .build()
462 .unwrap();
463 let account_delta = build_account_delta(
464 account_id,
465 vec![asset_1],
466 vec![asset_0],
467 nonce_delta,
468 storage_delta,
469 );
470
471 let serialized = account_delta.to_bytes();
472 let deserialized = AccountDelta::read_from_bytes(&serialized).unwrap();
473 assert_eq!(deserialized, account_delta);
474 }
475
476 #[test]
477 fn valid_account_delta_is_correctly_applied() {
478 let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap();
480 let init_nonce = Felt::new(1);
481 let asset_0 = FungibleAsset::mock(100);
482 let asset_1 = NonFungibleAsset::mock(&[1, 2, 3]);
483
484 let storage_slot_value_0 =
486 StorageSlot::Value([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]);
487 let storage_slot_value_1 =
488 StorageSlot::Value([Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]);
489 let mut storage_map = StorageMap::with_entries([
490 (
491 Digest::new([Felt::new(101), Felt::new(102), Felt::new(103), Felt::new(104)]),
492 [Felt::new(1_u64), Felt::new(2_u64), Felt::new(3_u64), Felt::new(4_u64)],
493 ),
494 (
495 Digest::new([Felt::new(105), Felt::new(106), Felt::new(107), Felt::new(108)]),
496 [Felt::new(5_u64), Felt::new(6_u64), Felt::new(7_u64), Felt::new(8_u64)],
497 ),
498 ])
499 .unwrap();
500 let storage_slot_map = StorageSlot::Map(storage_map.clone());
501
502 let mut account = build_account(
503 vec![asset_0],
504 init_nonce,
505 vec![storage_slot_value_0, storage_slot_value_1, storage_slot_map],
506 );
507
508 let new_map_entry = (
510 Digest::new([Felt::new(101), Felt::new(102), Felt::new(103), Felt::new(104)]),
511 [Felt::new(9_u64), Felt::new(10_u64), Felt::new(11_u64), Felt::new(12_u64)],
512 );
513
514 let updated_map =
515 StorageMapDelta::from_iters([], [(new_map_entry.0.into(), new_map_entry.1)]);
516 storage_map.insert(new_map_entry.0, new_map_entry.1);
517
518 let final_nonce = Felt::new(2);
520 let storage_delta = AccountStorageDeltaBuilder::new()
521 .add_cleared_items([0])
522 .add_updated_values([(1, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])])
523 .add_updated_maps([(2, updated_map)])
524 .build()
525 .unwrap();
526 let account_delta = build_account_delta(
527 account_id,
528 vec![asset_1],
529 vec![asset_0],
530 final_nonce - init_nonce,
531 storage_delta,
532 );
533
534 account.apply_delta(&account_delta).unwrap();
536
537 let final_account = build_account(
538 vec![asset_1],
539 final_nonce,
540 vec![
541 StorageSlot::Value(Word::default()),
542 StorageSlot::Value([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]),
543 StorageSlot::Map(storage_map),
544 ],
545 );
546
547 assert_eq!(account, final_account);
549 }
550
551 #[test]
552 #[should_panic]
553 fn valid_account_delta_with_unchanged_nonce() {
554 let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap();
556 let init_nonce = Felt::new(1);
557 let asset = FungibleAsset::mock(110);
558 let mut account =
559 build_account(vec![asset], init_nonce, vec![StorageSlot::Value(Word::default())]);
560
561 let storage_delta = AccountStorageDeltaBuilder::new()
563 .add_cleared_items([0])
564 .add_updated_values([(1_u8, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])])
565 .build()
566 .unwrap();
567 let account_delta =
568 build_account_delta(account_id, vec![], vec![asset], init_nonce, storage_delta);
569
570 account.apply_delta(&account_delta).unwrap()
572 }
573
574 #[test]
575 #[should_panic]
576 fn valid_account_delta_with_decremented_nonce() {
577 let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap();
579 let init_nonce = Felt::new(2);
580 let asset = FungibleAsset::mock(100);
581 let mut account =
582 build_account(vec![asset], init_nonce, vec![StorageSlot::Value(Word::default())]);
583
584 let final_nonce = Felt::new(1);
586 let storage_delta = AccountStorageDeltaBuilder::new()
587 .add_cleared_items([0])
588 .add_updated_values([(1_u8, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])])
589 .build()
590 .unwrap();
591 let account_delta =
592 build_account_delta(account_id, vec![], vec![asset], final_nonce, storage_delta);
593
594 account.apply_delta(&account_delta).unwrap()
596 }
597
598 #[test]
599 fn empty_account_delta_with_incremented_nonce() {
600 let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap();
602 let init_nonce = Felt::new(1);
603 let word = [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)];
604 let storage_slot = StorageSlot::Value(word);
605 let mut account = build_account(vec![], init_nonce, vec![storage_slot]);
606
607 let nonce_delta = Felt::new(1);
609 let account_delta = AccountDelta::new(
610 account_id,
611 AccountStorageDelta::new(),
612 AccountVaultDelta::default(),
613 nonce_delta,
614 )
615 .unwrap();
616
617 account.apply_delta(&account_delta).unwrap()
619 }
620
621 pub fn build_account_delta(
622 account_id: AccountId,
623 added_assets: Vec<Asset>,
624 removed_assets: Vec<Asset>,
625 nonce_delta: Felt,
626 storage_delta: AccountStorageDelta,
627 ) -> AccountDelta {
628 let vault_delta = AccountVaultDelta::from_iters(added_assets, removed_assets);
629 AccountDelta::new(account_id, storage_delta, vault_delta, nonce_delta).unwrap()
630 }
631
632 pub fn build_account(assets: Vec<Asset>, nonce: Felt, slots: Vec<StorageSlot>) -> Account {
633 let id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap();
634 let code = AccountCode::mock();
635
636 let vault = AssetVault::new(&assets).unwrap();
637
638 let storage = AccountStorage::new(slots).unwrap();
639
640 Account::from_parts(id, vault, storage, code, nonce)
641 }
642
643 #[test]
646 fn test_account_unsupported_component_type() {
647 let code1 = "export.foo add end";
648 let library1 = Assembler::default().assemble_library([code1]).unwrap();
649
650 let component1 = AccountComponent::new(library1, vec![])
652 .unwrap()
653 .with_supported_type(AccountType::FungibleFaucet)
654 .with_supported_type(AccountType::NonFungibleFaucet)
655 .with_supported_type(AccountType::RegularAccountImmutableCode);
656
657 let err = Account::initialize_from_components(
658 AccountType::RegularAccountUpdatableCode,
659 &[component1],
660 )
661 .unwrap_err();
662
663 assert!(matches!(
664 err,
665 AccountError::UnsupportedComponentForAccountType {
666 account_type: AccountType::RegularAccountUpdatableCode,
667 component_index: 0
668 }
669 ))
670 }
671
672 #[test]
675 fn test_account_duplicate_exported_mast_root() {
676 let code1 = "export.foo add eq.1 end";
677 let code2 = "export.bar add eq.1 end";
678
679 let library1 = Assembler::default().assemble_library([code1]).unwrap();
680 let library2 = Assembler::default().assemble_library([code2]).unwrap();
681
682 let auth_component: AccountComponent =
683 NoopAuthComponent::new(Assembler::default()).unwrap().into();
684 let component1 = AccountComponent::new(library1, vec![]).unwrap().with_supports_all_types();
685 let component2 = AccountComponent::new(library2, vec![]).unwrap().with_supports_all_types();
686
687 let err = Account::initialize_from_components(
688 AccountType::RegularAccountUpdatableCode,
689 &[auth_component, component1, component2],
690 )
691 .unwrap_err();
692
693 assert_matches!(err, AccountError::AccountComponentDuplicateProcedureRoot(_))
694 }
695}