miden_lib/account/wallets/
mod.rs

1use alloc::string::ToString;
2
3use miden_objects::{
4    AccountError, Word,
5    account::{
6        Account, AccountBuilder, AccountComponent, AccountIdAnchor, AccountStorageMode, AccountType,
7    },
8};
9
10use super::AuthScheme;
11use crate::account::{auth::RpoFalcon512, components::basic_wallet_library};
12
13// BASIC WALLET
14// ================================================================================================
15
16/// An [`AccountComponent`] implementing a basic wallet.
17///
18/// It reexports the procedures from `miden::contracts::wallets::basic`. When linking against this
19/// component, the `miden` library (i.e. [`MidenLib`](crate::MidenLib)) must be available to the
20/// assembler which is the case when using [`TransactionKernel::assembler()`][kasm]. The procedures
21/// of this component are:
22/// - `receive_asset`, which can be used to add an asset to the account.
23/// - `create_note`, which can be used to create a new note without any assets attached to it.
24/// - `move_asset_to_note`, which can be used to remove the specified asset from the account and add
25///   it to the output note with the specified index.
26///
27/// All methods require authentication. Thus, this component must be combined with a component
28/// providing authentication.
29///
30/// This component supports all account types.
31///
32/// [kasm]: crate::transaction::TransactionKernel::assembler
33pub struct BasicWallet;
34
35impl From<BasicWallet> for AccountComponent {
36    fn from(_: BasicWallet) -> Self {
37        AccountComponent::new(basic_wallet_library(), vec![])
38          .expect("basic wallet component should satisfy the requirements of a valid account component")
39          .with_supports_all_types()
40    }
41}
42
43/// Creates a new account with basic wallet interface, the specified authentication scheme and the
44/// account storage type. Basic wallets can be specified to have either mutable or immutable code.
45///
46/// The basic wallet interface exposes three procedures:
47/// - `receive_asset`, which can be used to add an asset to the account.
48/// - `create_note`, which can be used to create a new note without any assets attached to it.
49/// - `move_asset_to_note`, which can be used to remove the specified asset from the account and add
50///   it to the output note with the specified index.
51///
52/// All methods require authentication. The authentication procedure is defined by the specified
53/// authentication scheme.
54pub fn create_basic_wallet(
55    init_seed: [u8; 32],
56    id_anchor: AccountIdAnchor,
57    auth_scheme: AuthScheme,
58    account_type: AccountType,
59    account_storage_mode: AccountStorageMode,
60) -> Result<(Account, Word), AccountError> {
61    if matches!(account_type, AccountType::FungibleFaucet | AccountType::NonFungibleFaucet) {
62        return Err(AccountError::AssumptionViolated(
63            "basic wallet accounts cannot have a faucet account type".to_string(),
64        ));
65    }
66
67    let auth_component: RpoFalcon512 = match auth_scheme {
68        AuthScheme::RpoFalcon512 { pub_key } => RpoFalcon512::new(pub_key),
69    };
70
71    let (account, account_seed) = AccountBuilder::new(init_seed)
72        .anchor(id_anchor)
73        .account_type(account_type)
74        .storage_mode(account_storage_mode)
75        .with_component(auth_component)
76        .with_component(BasicWallet)
77        .build()?;
78
79    Ok((account, account_seed))
80}
81
82// TESTS
83// ================================================================================================
84
85#[cfg(test)]
86mod tests {
87
88    use miden_objects::{ONE, block::BlockHeader, crypto::dsa::rpo_falcon512, digest};
89    use vm_processor::utils::{Deserializable, Serializable};
90
91    use super::{Account, AccountStorageMode, AccountType, AuthScheme, create_basic_wallet};
92
93    #[test]
94    fn test_create_basic_wallet() {
95        let anchor_block_header_mock = BlockHeader::mock(
96            0,
97            Some(digest!("0xaa")),
98            Some(digest!("0xbb")),
99            &[],
100            digest!("0xcc"),
101        );
102
103        let pub_key = rpo_falcon512::PublicKey::new([ONE; 4]);
104        let wallet = create_basic_wallet(
105            [1; 32],
106            (&anchor_block_header_mock).try_into().unwrap(),
107            AuthScheme::RpoFalcon512 { pub_key },
108            AccountType::RegularAccountImmutableCode,
109            AccountStorageMode::Public,
110        );
111
112        wallet.unwrap_or_else(|err| {
113            panic!("{}", err);
114        });
115    }
116
117    #[test]
118    fn test_serialize_basic_wallet() {
119        let anchor_block_header_mock = BlockHeader::mock(
120            0,
121            Some(digest!("0xaa")),
122            Some(digest!("0xbb")),
123            &[],
124            digest!("0xcc"),
125        );
126
127        let pub_key = rpo_falcon512::PublicKey::new([ONE; 4]);
128        let wallet = create_basic_wallet(
129            [1; 32],
130            (&anchor_block_header_mock).try_into().unwrap(),
131            AuthScheme::RpoFalcon512 { pub_key },
132            AccountType::RegularAccountImmutableCode,
133            AccountStorageMode::Public,
134        )
135        .unwrap()
136        .0;
137
138        let bytes = wallet.to_bytes();
139        let deserialized_wallet = Account::read_from_bytes(&bytes).unwrap();
140        assert_eq!(wallet, deserialized_wallet);
141    }
142}