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, AccountIdAnchor, 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(
135 account_type: AccountType,
136 components: &[AccountComponent],
137 ) -> Result<(AccountCode, AccountStorage), AccountError> {
138 validate_components_support_account_type(components, account_type)?;
139
140 let code = AccountCode::from_components_unchecked(components, account_type)?;
141 let storage = AccountStorage::from_components(components, account_type)?;
142
143 Ok((code, storage))
144 }
145
146 pub fn builder(init_seed: [u8; 32]) -> AccountBuilder {
151 AccountBuilder::new(init_seed)
152 }
153
154 pub fn commitment(&self) -> Digest {
163 hash_account(
164 self.id,
165 self.nonce,
166 self.vault.root(),
167 self.storage.commitment(),
168 self.code.commitment(),
169 )
170 }
171
172 pub fn init_commitment(&self) -> Digest {
182 if self.is_new() {
183 Digest::default()
184 } else {
185 self.commitment()
186 }
187 }
188
189 pub fn id(&self) -> AccountId {
191 self.id
192 }
193
194 pub fn account_type(&self) -> AccountType {
196 self.id.account_type()
197 }
198
199 pub fn vault(&self) -> &AssetVault {
201 &self.vault
202 }
203
204 pub fn storage(&self) -> &AccountStorage {
206 &self.storage
207 }
208
209 pub fn code(&self) -> &AccountCode {
211 &self.code
212 }
213
214 pub fn nonce(&self) -> Felt {
216 self.nonce
217 }
218
219 pub fn is_faucet(&self) -> bool {
221 self.id.is_faucet()
222 }
223
224 pub fn is_regular_account(&self) -> bool {
226 self.id.is_regular_account()
227 }
228
229 pub fn is_onchain(&self) -> bool {
232 self.id().is_onchain()
233 }
234
235 pub fn is_public(&self) -> bool {
237 self.id().is_public()
238 }
239
240 pub fn is_private(&self) -> bool {
242 self.id().is_private()
243 }
244
245 pub fn is_network(&self) -> bool {
247 self.id().is_network()
248 }
249
250 pub fn is_new(&self) -> bool {
252 self.nonce == ZERO
253 }
254
255 pub fn into_parts(self) -> (AccountId, AssetVault, AccountStorage, AccountCode, Felt) {
257 (self.id, self.vault, self.storage, self.code, self.nonce)
258 }
259
260 pub fn apply_delta(&mut self, delta: &AccountDelta) -> Result<(), AccountError> {
273 self.vault
276 .apply_delta(delta.vault())
277 .map_err(AccountError::AssetVaultUpdateError)?;
278
279 self.storage.apply_delta(delta.storage())?;
281
282 if let Some(nonce) = delta.nonce() {
284 self.set_nonce(nonce)?;
285 }
286
287 Ok(())
288 }
289
290 pub fn set_nonce(&mut self, nonce: Felt) -> Result<(), AccountError> {
297 if self.nonce.as_int() >= nonce.as_int() {
298 return Err(AccountError::NonceNotMonotonicallyIncreasing {
299 current: self.nonce.as_int(),
300 new: nonce.as_int(),
301 });
302 }
303
304 self.nonce = nonce;
305
306 Ok(())
307 }
308
309 #[cfg(any(feature = "testing", test))]
313 pub fn vault_mut(&mut self) -> &mut AssetVault {
315 &mut self.vault
316 }
317
318 #[cfg(any(feature = "testing", test))]
319 pub fn storage_mut(&mut self) -> &mut AccountStorage {
321 &mut self.storage
322 }
323}
324
325impl Serializable for Account {
329 fn write_into<W: ByteWriter>(&self, target: &mut W) {
330 let Account { id, vault, storage, code, nonce } = self;
331
332 id.write_into(target);
333 vault.write_into(target);
334 storage.write_into(target);
335 code.write_into(target);
336 nonce.write_into(target);
337 }
338
339 fn get_size_hint(&self) -> usize {
340 self.id.get_size_hint()
341 + self.vault.get_size_hint()
342 + self.storage.get_size_hint()
343 + self.code.get_size_hint()
344 + self.nonce.get_size_hint()
345 }
346}
347
348impl Deserializable for Account {
349 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
350 let id = AccountId::read_from(source)?;
351 let vault = AssetVault::read_from(source)?;
352 let storage = AccountStorage::read_from(source)?;
353 let code = AccountCode::read_from(source)?;
354 let nonce = Felt::read_from(source)?;
355
356 Ok(Self::from_parts(id, vault, storage, code, nonce))
357 }
358}
359
360pub fn hash_account(
369 id: AccountId,
370 nonce: Felt,
371 vault_root: Digest,
372 storage_commitment: Digest,
373 code_commitment: Digest,
374) -> Digest {
375 let mut elements = [ZERO; 16];
376 elements[0] = id.suffix();
377 elements[1] = id.prefix().as_felt();
378 elements[3] = nonce;
379 elements[4..8].copy_from_slice(&*vault_root);
380 elements[8..12].copy_from_slice(&*storage_commitment);
381 elements[12..].copy_from_slice(&*code_commitment);
382 Hasher::hash_elements(&elements)
383}
384
385fn validate_components_support_account_type(
387 components: &[AccountComponent],
388 account_type: AccountType,
389) -> Result<(), AccountError> {
390 for (component_index, component) in components.iter().enumerate() {
391 if !component.supports_type(account_type) {
392 return Err(AccountError::UnsupportedComponentForAccountType {
393 account_type,
394 component_index,
395 });
396 }
397 }
398
399 Ok(())
400}
401
402#[cfg(test)]
406mod tests {
407 use alloc::vec::Vec;
408
409 use assembly::Assembler;
410 use assert_matches::assert_matches;
411 use miden_crypto::{
412 Felt, Word,
413 utils::{Deserializable, Serializable},
414 };
415 use vm_processor::Digest;
416
417 use super::{
418 AccountCode, AccountDelta, AccountId, AccountStorage, AccountStorageDelta,
419 AccountVaultDelta,
420 };
421 use crate::{
422 AccountError,
423 account::{
424 Account, AccountComponent, AccountType, StorageMap, StorageMapDelta, StorageSlot,
425 },
426 asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset},
427 testing::{
428 account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
429 storage::AccountStorageDeltaBuilder,
430 },
431 };
432
433 #[test]
434 fn test_serde_account() {
435 let init_nonce = Felt::new(1);
436 let asset_0 = FungibleAsset::mock(99);
437 let word = [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)];
438 let storage_slot = StorageSlot::Value(word);
439 let account = build_account(vec![asset_0], init_nonce, vec![storage_slot]);
440
441 let serialized = account.to_bytes();
442 let deserialized = Account::read_from_bytes(&serialized).unwrap();
443 assert_eq!(deserialized, account);
444 }
445
446 #[test]
447 fn test_serde_account_delta() {
448 let final_nonce = Felt::new(2);
449 let asset_0 = FungibleAsset::mock(15);
450 let asset_1 = NonFungibleAsset::mock(&[5, 5, 5]);
451 let storage_delta = AccountStorageDeltaBuilder::default()
452 .add_cleared_items([0])
453 .add_updated_values([(1_u8, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])])
454 .build()
455 .unwrap();
456 let account_delta =
457 build_account_delta(vec![asset_1], vec![asset_0], final_nonce, storage_delta);
458
459 let serialized = account_delta.to_bytes();
460 let deserialized = AccountDelta::read_from_bytes(&serialized).unwrap();
461 assert_eq!(deserialized, account_delta);
462 }
463
464 #[test]
465 fn valid_account_delta_is_correctly_applied() {
466 let init_nonce = Felt::new(1);
468 let asset_0 = FungibleAsset::mock(100);
469 let asset_1 = NonFungibleAsset::mock(&[1, 2, 3]);
470
471 let storage_slot_value_0 =
473 StorageSlot::Value([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]);
474 let storage_slot_value_1 =
475 StorageSlot::Value([Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]);
476 let mut storage_map = StorageMap::with_entries([
477 (
478 Digest::new([Felt::new(101), Felt::new(102), Felt::new(103), Felt::new(104)]),
479 [Felt::new(1_u64), Felt::new(2_u64), Felt::new(3_u64), Felt::new(4_u64)],
480 ),
481 (
482 Digest::new([Felt::new(105), Felt::new(106), Felt::new(107), Felt::new(108)]),
483 [Felt::new(5_u64), Felt::new(6_u64), Felt::new(7_u64), Felt::new(8_u64)],
484 ),
485 ])
486 .unwrap();
487 let storage_slot_map = StorageSlot::Map(storage_map.clone());
488
489 let mut account = build_account(
490 vec![asset_0],
491 init_nonce,
492 vec![storage_slot_value_0, storage_slot_value_1, storage_slot_map],
493 );
494
495 let new_map_entry = (
497 Digest::new([Felt::new(101), Felt::new(102), Felt::new(103), Felt::new(104)]),
498 [Felt::new(9_u64), Felt::new(10_u64), Felt::new(11_u64), Felt::new(12_u64)],
499 );
500
501 let updated_map =
502 StorageMapDelta::from_iters([], [(new_map_entry.0.into(), new_map_entry.1)]);
503 storage_map.insert(new_map_entry.0, new_map_entry.1);
504
505 let final_nonce = Felt::new(2);
507 let storage_delta = AccountStorageDeltaBuilder::default()
508 .add_cleared_items([0])
509 .add_updated_values([(1, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])])
510 .add_updated_maps([(2, updated_map)])
511 .build()
512 .unwrap();
513 let account_delta =
514 build_account_delta(vec![asset_1], vec![asset_0], final_nonce, storage_delta);
515
516 account.apply_delta(&account_delta).unwrap();
518
519 let final_account = build_account(
520 vec![asset_1],
521 final_nonce,
522 vec![
523 StorageSlot::Value(Word::default()),
524 StorageSlot::Value([Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]),
525 StorageSlot::Map(storage_map),
526 ],
527 );
528
529 assert_eq!(account, final_account);
531 }
532
533 #[test]
534 #[should_panic]
535 fn valid_account_delta_with_unchanged_nonce() {
536 let init_nonce = Felt::new(1);
538 let asset = FungibleAsset::mock(110);
539 let mut account =
540 build_account(vec![asset], init_nonce, vec![StorageSlot::Value(Word::default())]);
541
542 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], init_nonce, storage_delta);
549
550 account.apply_delta(&account_delta).unwrap()
552 }
553
554 #[test]
555 #[should_panic]
556 fn valid_account_delta_with_decremented_nonce() {
557 let init_nonce = Felt::new(2);
559 let asset = FungibleAsset::mock(100);
560 let mut account =
561 build_account(vec![asset], init_nonce, vec![StorageSlot::Value(Word::default())]);
562
563 let final_nonce = Felt::new(1);
565 let storage_delta = AccountStorageDeltaBuilder::default()
566 .add_cleared_items([0])
567 .add_updated_values([(1_u8, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)])])
568 .build()
569 .unwrap();
570 let account_delta = build_account_delta(vec![], vec![asset], final_nonce, storage_delta);
571
572 account.apply_delta(&account_delta).unwrap()
574 }
575
576 #[test]
577 fn empty_account_delta_with_incremented_nonce() {
578 let init_nonce = Felt::new(1);
580 let word = [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)];
581 let storage_slot = StorageSlot::Value(word);
582 let mut account = build_account(vec![], init_nonce, vec![storage_slot]);
583
584 let final_nonce = Felt::new(2);
586 let account_delta = AccountDelta::new(
587 AccountStorageDelta::default(),
588 AccountVaultDelta::default(),
589 Some(final_nonce),
590 )
591 .unwrap();
592
593 account.apply_delta(&account_delta).unwrap()
595 }
596
597 pub fn build_account_delta(
598 added_assets: Vec<Asset>,
599 removed_assets: Vec<Asset>,
600 nonce: Felt,
601 storage_delta: AccountStorageDelta,
602 ) -> AccountDelta {
603 let vault_delta = AccountVaultDelta::from_iters(added_assets, removed_assets);
604 AccountDelta::new(storage_delta, vault_delta, Some(nonce)).unwrap()
605 }
606
607 pub fn build_account(assets: Vec<Asset>, nonce: Felt, slots: Vec<StorageSlot>) -> Account {
608 let id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap();
609 let code = AccountCode::mock();
610
611 let vault = AssetVault::new(&assets).unwrap();
612
613 let storage = AccountStorage::new(slots).unwrap();
614
615 Account::from_parts(id, vault, storage, code, nonce)
616 }
617
618 #[test]
621 fn test_account_unsupported_component_type() {
622 let code1 = "export.foo add end";
623 let library1 = Assembler::default().assemble_library([code1]).unwrap();
624
625 let component1 = AccountComponent::new(library1, vec![])
627 .unwrap()
628 .with_supported_type(AccountType::FungibleFaucet)
629 .with_supported_type(AccountType::NonFungibleFaucet)
630 .with_supported_type(AccountType::RegularAccountImmutableCode);
631
632 let err = Account::initialize_from_components(
633 AccountType::RegularAccountUpdatableCode,
634 &[component1],
635 )
636 .unwrap_err();
637
638 assert!(matches!(
639 err,
640 AccountError::UnsupportedComponentForAccountType {
641 account_type: AccountType::RegularAccountUpdatableCode,
642 component_index: 0
643 }
644 ))
645 }
646
647 #[test]
650 fn test_account_duplicate_exported_mast_root() {
651 let code1 = "export.foo add eq.1 end";
652 let code2 = "export.bar add eq.1 end";
653
654 let library1 = Assembler::default().assemble_library([code1]).unwrap();
655 let library2 = Assembler::default().assemble_library([code2]).unwrap();
656
657 let component1 = AccountComponent::new(library1, vec![]).unwrap().with_supports_all_types();
658 let component2 = AccountComponent::new(library2, vec![]).unwrap().with_supports_all_types();
659
660 let err = Account::initialize_from_components(
661 AccountType::RegularAccountUpdatableCode,
662 &[component1, component2],
663 )
664 .unwrap_err();
665
666 assert_matches!(err, AccountError::AccountComponentDuplicateProcedureRoot(_))
667 }
668}