miden_standards/account/interface/
extension.rs1use alloc::collections::BTreeSet;
2use alloc::sync::Arc;
3use alloc::vec::Vec;
4
5use miden_processor::MastNodeExt;
6use miden_protocol::Word;
7use miden_protocol::account::{Account, AccountCode, AccountId, AccountProcedureRoot};
8use miden_protocol::assembly::mast::{MastForest, MastNode, MastNodeId};
9use miden_protocol::note::{Note, NoteScript};
10
11use crate::AuthMethod;
12use crate::account::components::{
13 StandardAccountComponent,
14 basic_fungible_faucet_library,
15 basic_wallet_library,
16 multisig_library,
17 network_fungible_faucet_library,
18 no_auth_library,
19 singlesig_acl_library,
20 singlesig_library,
21};
22use crate::account::interface::{
23 AccountComponentInterface,
24 AccountInterface,
25 NoteAccountCompatibility,
26};
27use crate::note::StandardNote;
28
29pub trait AccountInterfaceExt {
34 fn from_code(account_id: AccountId, auth: Vec<AuthMethod>, code: &AccountCode) -> Self;
37
38 fn from_account(account: &Account) -> Self;
40
41 fn is_compatible_with(&self, note: &Note) -> NoteAccountCompatibility;
44
45 fn get_procedure_digests(&self) -> BTreeSet<Word>;
47}
48
49impl AccountInterfaceExt for AccountInterface {
50 fn from_code(account_id: AccountId, auth: Vec<AuthMethod>, code: &AccountCode) -> Self {
51 let components = AccountComponentInterface::from_procedures(code.procedures());
52
53 Self::new(account_id, auth, components)
54 }
55
56 fn from_account(account: &Account) -> Self {
57 let components = AccountComponentInterface::from_procedures(account.code().procedures());
58 let mut auth = Vec::new();
59
60 for component in components.iter() {
63 if component.is_auth_component() {
64 auth = component.get_auth_methods(account.storage());
65 break;
66 }
67 }
68
69 Self::new(account.id(), auth, components)
70 }
71
72 fn is_compatible_with(&self, note: &Note) -> NoteAccountCompatibility {
75 if let Some(standard_note) = StandardNote::from_script_root(note.script().root()) {
76 if standard_note.is_compatible_with(self) {
77 NoteAccountCompatibility::Maybe
78 } else {
79 NoteAccountCompatibility::No
80 }
81 } else {
82 verify_note_script_compatibility(note.script(), self.get_procedure_digests())
83 }
84 }
85
86 fn get_procedure_digests(&self) -> BTreeSet<Word> {
87 let mut component_proc_digests = BTreeSet::new();
88 for component in self.components.iter() {
89 match component {
90 AccountComponentInterface::BasicWallet => {
91 component_proc_digests
92 .extend(basic_wallet_library().mast_forest().procedure_digests());
93 },
94 AccountComponentInterface::BasicFungibleFaucet => {
95 component_proc_digests
96 .extend(basic_fungible_faucet_library().mast_forest().procedure_digests());
97 },
98 AccountComponentInterface::NetworkFungibleFaucet => {
99 component_proc_digests.extend(
100 network_fungible_faucet_library().mast_forest().procedure_digests(),
101 );
102 },
103 AccountComponentInterface::AuthSingleSig => {
104 component_proc_digests
105 .extend(singlesig_library().mast_forest().procedure_digests());
106 },
107 AccountComponentInterface::AuthSingleSigAcl => {
108 component_proc_digests
109 .extend(singlesig_acl_library().mast_forest().procedure_digests());
110 },
111 AccountComponentInterface::AuthMultisig => {
112 component_proc_digests
113 .extend(multisig_library().mast_forest().procedure_digests());
114 },
115 AccountComponentInterface::AuthNoAuth => {
116 component_proc_digests
117 .extend(no_auth_library().mast_forest().procedure_digests());
118 },
119 AccountComponentInterface::Custom(custom_procs) => {
120 component_proc_digests
121 .extend(custom_procs.iter().map(|info| *info.mast_root()));
122 },
123 }
124 }
125
126 component_proc_digests
127 }
128}
129
130pub trait AccountComponentInterfaceExt {
133 fn from_procedures(procedures: &[AccountProcedureRoot]) -> Vec<AccountComponentInterface>;
136}
137
138impl AccountComponentInterfaceExt for AccountComponentInterface {
139 fn from_procedures(procedures: &[AccountProcedureRoot]) -> Vec<Self> {
140 let mut component_interface_vec = Vec::new();
141
142 let mut procedures = BTreeSet::from_iter(procedures.iter().copied());
143
144 StandardAccountComponent::extract_standard_components(
150 &mut procedures,
151 &mut component_interface_vec,
152 );
153
154 component_interface_vec
159 .push(AccountComponentInterface::Custom(procedures.into_iter().collect()));
160
161 component_interface_vec
162 }
163}
164
165fn verify_note_script_compatibility(
176 note_script: &NoteScript,
177 account_procedures: BTreeSet<Word>,
178) -> NoteAccountCompatibility {
179 let branches = collect_call_branches(note_script);
181
182 if !branches.iter().any(|call_targets| call_targets.is_subset(&account_procedures)) {
184 return NoteAccountCompatibility::No;
185 }
186
187 NoteAccountCompatibility::Maybe
188}
189
190fn collect_call_branches(note_script: &NoteScript) -> Vec<BTreeSet<Word>> {
193 let mut branches = vec![BTreeSet::new()];
194
195 let entry_node = note_script.entrypoint();
196 recursively_collect_call_branches(entry_node, &mut branches, ¬e_script.mast());
197 branches
198}
199
200fn recursively_collect_call_branches(
202 mast_node_id: MastNodeId,
203 branches: &mut Vec<BTreeSet<Word>>,
204 note_script_forest: &Arc<MastForest>,
205) {
206 let mast_node = ¬e_script_forest[mast_node_id];
207
208 match mast_node {
209 MastNode::Block(_) => {},
210 MastNode::Join(join_node) => {
211 recursively_collect_call_branches(join_node.first(), branches, note_script_forest);
212 recursively_collect_call_branches(join_node.second(), branches, note_script_forest);
213 },
214 MastNode::Split(split_node) => {
215 let current_branch = branches.last().expect("at least one execution branch").clone();
216 recursively_collect_call_branches(split_node.on_false(), branches, note_script_forest);
217
218 if branches.last().expect("at least one execution branch").len() > current_branch.len()
220 {
221 branches.push(current_branch);
222 }
223
224 recursively_collect_call_branches(split_node.on_true(), branches, note_script_forest);
225 },
226 MastNode::Loop(loop_node) => {
227 recursively_collect_call_branches(loop_node.body(), branches, note_script_forest);
228 },
229 MastNode::Call(call_node) => {
230 if call_node.is_syscall() {
231 return;
232 }
233
234 let callee_digest = note_script_forest[call_node.callee()].digest();
235
236 branches
237 .last_mut()
238 .expect("at least one execution branch")
239 .insert(callee_digest);
240 },
241 MastNode::Dyn(_) => {},
242 MastNode::External(_) => {},
243 }
244}