miden_standards/account/interface/
component.rs1use alloc::string::{String, ToString};
2use alloc::vec::Vec;
3
4use miden_protocol::account::auth::PublicKeyCommitment;
5use miden_protocol::account::{AccountId, AccountProcedureRoot, AccountStorage, StorageSlotName};
6use miden_protocol::note::PartialNote;
7use miden_protocol::{Felt, FieldElement, Word};
8
9use crate::AuthScheme;
10use crate::account::auth::{
11 AuthEcdsaK256Keccak,
12 AuthEcdsaK256KeccakAcl,
13 AuthEcdsaK256KeccakMultisig,
14 AuthFalcon512Rpo,
15 AuthFalcon512RpoAcl,
16 AuthFalcon512RpoMultisig,
17};
18use crate::account::interface::AccountInterfaceError;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum AccountComponentInterface {
26 BasicWallet,
28 BasicFungibleFaucet,
31 NetworkFungibleFaucet,
34 AuthEcdsaK256Keccak,
37 AuthEcdsaK256KeccakAcl,
40 AuthEcdsaK256KeccakMultisig,
43 AuthFalcon512Rpo,
46 AuthFalcon512RpoAcl,
49 AuthFalcon512RpoMultisig,
52 AuthNoAuth,
57 Custom(Vec<AccountProcedureRoot>),
62}
63
64impl AccountComponentInterface {
65 pub fn name(&self) -> String {
71 match self {
72 AccountComponentInterface::BasicWallet => "Basic Wallet".to_string(),
73 AccountComponentInterface::BasicFungibleFaucet => "Basic Fungible Faucet".to_string(),
74 AccountComponentInterface::NetworkFungibleFaucet => {
75 "Network Fungible Faucet".to_string()
76 },
77 AccountComponentInterface::AuthEcdsaK256Keccak => "ECDSA K256 Keccak".to_string(),
78 AccountComponentInterface::AuthEcdsaK256KeccakAcl => {
79 "ECDSA K256 Keccak ACL".to_string()
80 },
81 AccountComponentInterface::AuthEcdsaK256KeccakMultisig => {
82 "ECDSA K256 Keccak Multisig".to_string()
83 },
84 AccountComponentInterface::AuthFalcon512Rpo => "Falcon512 RPO".to_string(),
85 AccountComponentInterface::AuthFalcon512RpoAcl => "Falcon512 RPO ACL".to_string(),
86 AccountComponentInterface::AuthFalcon512RpoMultisig => {
87 "Falcon512 RPO Multisig".to_string()
88 },
89
90 AccountComponentInterface::AuthNoAuth => "No Auth".to_string(),
91 AccountComponentInterface::Custom(proc_root_vec) => {
92 let result = proc_root_vec
93 .iter()
94 .map(|proc_root| proc_root.mast_root().to_hex()[..9].to_string())
95 .collect::<Vec<_>>()
96 .join(", ");
97 format!("Custom([{result}])")
98 },
99 }
100 }
101
102 pub fn is_auth_component(&self) -> bool {
106 matches!(
107 self,
108 AccountComponentInterface::AuthEcdsaK256Keccak
109 | AccountComponentInterface::AuthEcdsaK256KeccakAcl
110 | AccountComponentInterface::AuthEcdsaK256KeccakMultisig
111 | AccountComponentInterface::AuthFalcon512Rpo
112 | AccountComponentInterface::AuthFalcon512RpoAcl
113 | AccountComponentInterface::AuthFalcon512RpoMultisig
114 | AccountComponentInterface::AuthNoAuth
115 )
116 }
117
118 pub fn get_auth_schemes(&self, storage: &AccountStorage) -> Vec<AuthScheme> {
120 match self {
121 AccountComponentInterface::AuthEcdsaK256Keccak => {
122 vec![AuthScheme::EcdsaK256Keccak {
123 pub_key: PublicKeyCommitment::from(
124 storage
125 .get_item(AuthEcdsaK256Keccak::public_key_slot())
126 .expect("invalid storage index of the public key"),
127 ),
128 }]
129 },
130 AccountComponentInterface::AuthEcdsaK256KeccakAcl => {
131 vec![AuthScheme::EcdsaK256Keccak {
132 pub_key: PublicKeyCommitment::from(
133 storage
134 .get_item(AuthEcdsaK256KeccakAcl::public_key_slot())
135 .expect("invalid storage index of the public key"),
136 ),
137 }]
138 },
139 AccountComponentInterface::AuthEcdsaK256KeccakMultisig => {
140 vec![extract_multisig_auth_scheme(
141 storage,
142 AuthEcdsaK256KeccakMultisig::threshold_config_slot(),
143 AuthEcdsaK256KeccakMultisig::approver_public_keys_slot(),
144 )]
145 },
146 AccountComponentInterface::AuthFalcon512Rpo => {
147 vec![AuthScheme::Falcon512Rpo {
148 pub_key: PublicKeyCommitment::from(
149 storage
150 .get_item(AuthFalcon512Rpo::public_key_slot())
151 .expect("invalid slot name of the AuthFalcon512Rpo public key"),
152 ),
153 }]
154 },
155 AccountComponentInterface::AuthFalcon512RpoAcl => {
156 vec![AuthScheme::Falcon512Rpo {
157 pub_key: PublicKeyCommitment::from(
158 storage
159 .get_item(AuthFalcon512RpoAcl::public_key_slot())
160 .expect("invalid slot name of the AuthFalcon512RpoAcl public key"),
161 ),
162 }]
163 },
164 AccountComponentInterface::AuthFalcon512RpoMultisig => {
165 vec![extract_multisig_auth_scheme(
166 storage,
167 AuthFalcon512RpoMultisig::threshold_config_slot(),
168 AuthFalcon512RpoMultisig::approver_public_keys_slot(),
169 )]
170 },
171 AccountComponentInterface::AuthNoAuth => vec![AuthScheme::NoAuth],
172 _ => vec![], }
174 }
175
176 pub(crate) fn send_note_body(
215 &self,
216 sender_account_id: AccountId,
217 notes: &[PartialNote],
218 ) -> Result<String, AccountInterfaceError> {
219 let mut body = String::new();
220
221 for partial_note in notes {
222 if partial_note.metadata().sender() != sender_account_id {
223 return Err(AccountInterfaceError::InvalidSenderAccount(
224 partial_note.metadata().sender(),
225 ));
226 }
227
228 body.push_str(&format!(
229 "
230 push.{recipient}
231 push.{note_type}
232 push.{tag}
233 # => [tag, note_type, RECIPIENT, pad(16)]
234 ",
235 recipient = partial_note.recipient_digest(),
236 note_type = Felt::from(partial_note.metadata().note_type()),
237 tag = Felt::from(partial_note.metadata().tag()),
238 ));
239
240 match self {
241 AccountComponentInterface::BasicFungibleFaucet => {
242 if partial_note.assets().num_assets() != 1 {
243 return Err(AccountInterfaceError::FaucetNoteWithoutAsset);
244 }
245
246 let asset =
248 partial_note.assets().iter().next().expect("note should contain an asset");
249
250 if asset.faucet_id_prefix() != sender_account_id.prefix() {
251 return Err(AccountInterfaceError::IssuanceFaucetMismatch(
252 asset.faucet_id_prefix(),
253 ));
254 }
255
256 body.push_str(&format!(
257 "
258 push.{amount}
259 call.::miden::standards::faucets::basic_fungible::distribute
260 # => [note_idx, pad(25)]
261 swapdw dropw dropw swap drop
262 # => [note_idx, pad(16)]\n
263 ",
264 amount = asset.unwrap_fungible().amount()
265 ));
266 },
267 AccountComponentInterface::BasicWallet => {
268 body.push_str(
269 "
270 exec.::miden::protocol::output_note::create
271 # => [note_idx, pad(16)]\n
272 ",
273 );
274
275 for asset in partial_note.assets().iter() {
276 body.push_str(&format!(
277 "
278 push.{asset}
279 # => [ASSET, note_idx, pad(16)]
280 call.::miden::standards::wallets::basic::move_asset_to_note
281 dropw
282 # => [note_idx, pad(16)]\n
283 ",
284 asset = Word::from(*asset)
285 ));
286 }
287 },
288 _ => {
289 return Err(AccountInterfaceError::UnsupportedInterface {
290 interface: self.clone(),
291 });
292 },
293 }
294
295 body.push_str(&format!(
296 "
297 push.{ATTACHMENT}
298 push.{attachment_kind}
299 push.{attachment_scheme}
300 movup.6
301 # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT, pad(16)]
302 exec.::miden::protocol::output_note::set_attachment
303 # => [pad(16)]
304 ",
305 ATTACHMENT = partial_note.metadata().to_attachment_word(),
306 attachment_scheme =
307 partial_note.metadata().attachment().attachment_scheme().as_u32(),
308 attachment_kind = partial_note.metadata().attachment().attachment_kind().as_u8(),
309 ));
310 }
311
312 Ok(body)
313 }
314}
315
316fn extract_multisig_auth_scheme(
321 storage: &AccountStorage,
322 config_slot: &StorageSlotName,
323 approver_public_keys_slot: &StorageSlotName,
324) -> AuthScheme {
325 let config = storage
328 .get_item(config_slot)
329 .expect("invalid slot name of the multisig configuration");
330
331 let threshold = config[0].as_int() as u32;
332 let num_approvers = config[1].as_int() as u8;
333
334 let mut pub_keys = Vec::new();
335
336 for key_index in 0..num_approvers {
338 let map_key = [Felt::new(key_index as u64), Felt::ZERO, Felt::ZERO, Felt::ZERO];
340
341 match storage.get_map_item(approver_public_keys_slot, map_key.into()) {
342 Ok(pub_key) => {
343 pub_keys.push(PublicKeyCommitment::from(pub_key));
344 },
345 Err(_) => {
346 panic!(
348 "Failed to read public key {} from multisig configuration at storage slot {}. \
349 Expected key pattern [index, 0, 0, 0]. \
350 This indicates corrupted multisig storage or incorrect storage layout.",
351 key_index, approver_public_keys_slot
352 );
353 },
354 }
355 }
356
357 AuthScheme::Falcon512RpoMultisig { threshold, pub_keys }
358}