1use alloc::boxed::Box;
2use alloc::string::ToString;
3use core::error::Error;
4
5use miden_protocol::account::AccountId;
6use miden_protocol::block::BlockNumber;
7use miden_protocol::note::{Note, NoteScript, NoteScriptRoot};
8
9mod burn;
10pub use burn::BurnNote;
11
12mod faucet_policy_action;
13pub use faucet_policy_action::{FaucetPolicyAction, FaucetPolicyActionNote};
14
15mod execution_hint;
16pub use execution_hint::NoteExecutionHint;
17
18mod file;
19pub use file::{NoteFile, NoteSyncHint};
20
21mod mint;
22pub use mint::{MintNote, MintNoteStorage};
23
24mod owner_action;
25pub use owner_action::{OwnerAction, OwnerActionNote};
26
27mod p2id;
28pub use p2id::{P2idNote, P2idNoteStorage};
29
30mod p2ide;
31pub use p2ide::{P2ideNote, P2ideNoteStorage};
32
33mod pause_action;
34pub use pause_action::{PauseAction, PauseActionNote};
35
36mod pswap;
37pub use pswap::{PswapNote, PswapNoteAttachment, PswapNoteStorage};
38
39mod rbac_action;
40pub use rbac_action::{RbacAction, RbacActionNote};
41
42mod swap;
43pub use swap::{SwapNote, SwapNoteStorage, SwapPayback, payback_serial_from_swap};
44
45mod network_account_target;
46pub use network_account_target::{NetworkAccountTarget, NetworkAccountTargetError};
47
48mod network_note;
49pub use network_note::{AccountTargetNetworkNote, NetworkNoteExt};
50
51mod standard_note_attachment;
52use miden_protocol::errors::NoteError;
53pub use standard_note_attachment::StandardNoteAttachment;
54#[allow(non_camel_case_types)]
59pub enum StandardNote {
60 P2ID,
61 P2IDE,
62 SWAP,
63 PSWAP,
64 MINT,
65 BURN,
66 FAUCET_POLICY_ACTION,
67 PAUSE_ACTION,
68 OWNER_ACTION,
69 RBAC_ACTION,
70}
71
72impl StandardNote {
73 pub fn from_script(script: &NoteScript) -> Option<Self> {
79 Self::from_script_root(script.root())
80 }
81
82 pub fn from_script_root(root: NoteScriptRoot) -> Option<Self> {
85 if root == P2idNote::script_root() {
86 return Some(Self::P2ID);
87 }
88 if root == P2ideNote::script_root() {
89 return Some(Self::P2IDE);
90 }
91 if root == SwapNote::script_root() {
92 return Some(Self::SWAP);
93 }
94 if root == PswapNote::script_root() {
95 return Some(Self::PSWAP);
96 }
97 if root == MintNote::script_root() {
98 return Some(Self::MINT);
99 }
100 if root == BurnNote::script_root() {
101 return Some(Self::BURN);
102 }
103 if root == FaucetPolicyActionNote::script_root() {
104 return Some(Self::FAUCET_POLICY_ACTION);
105 }
106 if root == PauseActionNote::script_root() {
107 return Some(Self::PAUSE_ACTION);
108 }
109 if root == OwnerActionNote::script_root() {
110 return Some(Self::OWNER_ACTION);
111 }
112 if root == RbacActionNote::script_root() {
113 return Some(Self::RBAC_ACTION);
114 }
115
116 None
117 }
118
119 pub fn name(&self) -> &'static str {
124 match self {
125 Self::P2ID => "P2ID",
126 Self::P2IDE => "P2IDE",
127 Self::SWAP => "SWAP",
128 Self::PSWAP => "PSWAP",
129 Self::MINT => "MINT",
130 Self::BURN => "BURN",
131 Self::FAUCET_POLICY_ACTION => "FAUCET_POLICY_ACTION",
132 Self::PAUSE_ACTION => "PAUSE_ACTION",
133 Self::OWNER_ACTION => "OWNER_ACTION",
134 Self::RBAC_ACTION => "RBAC_ACTION",
135 }
136 }
137
138 pub fn expected_num_storage_items(&self) -> usize {
140 match self {
141 Self::P2ID => P2idNote::NUM_STORAGE_ITEMS,
142 Self::P2IDE => P2ideNote::NUM_STORAGE_ITEMS,
143 Self::SWAP => SwapNote::NUM_STORAGE_ITEMS,
144 Self::PSWAP => PswapNote::NUM_STORAGE_ITEMS,
145 Self::MINT => MintNote::NUM_STORAGE_ITEMS_PRIVATE,
146 Self::BURN => BurnNote::NUM_STORAGE_ITEMS,
147 Self::FAUCET_POLICY_ACTION => FaucetPolicyActionNote::NUM_STORAGE_ITEMS,
148 Self::PAUSE_ACTION => PauseActionNote::NUM_STORAGE_ITEMS,
149 Self::OWNER_ACTION => OwnerActionNote::MAX_NUM_STORAGE_ITEMS,
151 Self::RBAC_ACTION => RbacActionNote::MAX_NUM_STORAGE_ITEMS,
153 }
154 }
155
156 pub fn script(&self) -> NoteScript {
158 match self {
159 Self::P2ID => P2idNote::script(),
160 Self::P2IDE => P2ideNote::script(),
161 Self::SWAP => SwapNote::script(),
162 Self::PSWAP => PswapNote::script(),
163 Self::MINT => MintNote::script(),
164 Self::BURN => BurnNote::script(),
165 Self::FAUCET_POLICY_ACTION => FaucetPolicyActionNote::script(),
166 Self::PAUSE_ACTION => PauseActionNote::script(),
167 Self::OWNER_ACTION => OwnerActionNote::script(),
168 Self::RBAC_ACTION => RbacActionNote::script(),
169 }
170 }
171
172 pub fn script_root(&self) -> NoteScriptRoot {
174 match self {
175 Self::P2ID => P2idNote::script_root(),
176 Self::P2IDE => P2ideNote::script_root(),
177 Self::SWAP => SwapNote::script_root(),
178 Self::PSWAP => PswapNote::script_root(),
179 Self::MINT => MintNote::script_root(),
180 Self::BURN => BurnNote::script_root(),
181 Self::FAUCET_POLICY_ACTION => FaucetPolicyActionNote::script_root(),
182 Self::PAUSE_ACTION => PauseActionNote::script_root(),
183 Self::OWNER_ACTION => OwnerActionNote::script_root(),
184 Self::RBAC_ACTION => RbacActionNote::script_root(),
185 }
186 }
187
188 pub fn is_consumable(
197 &self,
198 note: &Note,
199 target_account_id: AccountId,
200 block_ref: BlockNumber,
201 ) -> Option<NoteConsumptionStatus> {
202 match self.is_consumable_inner(note, target_account_id, block_ref) {
203 Ok(status) => status,
204 Err(err) => {
205 let err: Box<dyn Error + Send + Sync + 'static> = Box::from(err);
206 Some(NoteConsumptionStatus::NeverConsumable(err))
207 },
208 }
209 }
210
211 fn is_consumable_inner(
226 &self,
227 note: &Note,
228 target_account_id: AccountId,
229 block_ref: BlockNumber,
230 ) -> Result<Option<NoteConsumptionStatus>, NoteError> {
231 match self {
232 StandardNote::P2ID => {
233 let input_account_id = P2idNoteStorage::try_from(note.storage().items())
234 .map_err(|e| NoteError::other_with_source("invalid P2ID note storage", e))?;
235
236 if input_account_id.target() == target_account_id {
237 Ok(Some(NoteConsumptionStatus::ConsumableWithAuthorization))
238 } else {
239 Ok(Some(NoteConsumptionStatus::NeverConsumable("account ID provided to the P2ID note storage doesn't match the target account ID".into())))
240 }
241 },
242 StandardNote::P2IDE => {
243 let storage = P2ideNoteStorage::try_from(note.storage().items())
244 .map_err(|e| NoteError::other_with_source("invalid P2IDE note storage", e))?;
245
246 let reclaimer_account_id = storage.reclaimer();
247 let receiver_account_id = storage.target();
248
249 let current_block_height = block_ref.as_u32();
250 let reclaim_height = storage.reclaim_height().unwrap_or_default().as_u32();
251 let timelock_height = storage.timelock_height().unwrap_or_default().as_u32();
252
253 let consumable_after = reclaim_height.max(timelock_height);
255
256 if target_account_id == reclaimer_account_id {
258 if current_block_height >= consumable_after {
261 Ok(Some(NoteConsumptionStatus::ConsumableWithAuthorization))
262 } else {
263 Ok(Some(NoteConsumptionStatus::ConsumableAfter(BlockNumber::from(
264 consumable_after,
265 ))))
266 }
267 } else if target_account_id == receiver_account_id {
269 if current_block_height >= timelock_height {
273 Ok(Some(NoteConsumptionStatus::ConsumableWithAuthorization))
274 } else {
275 Ok(Some(NoteConsumptionStatus::ConsumableAfter(BlockNumber::from(
276 timelock_height,
277 ))))
278 }
279 } else {
282 Ok(Some(NoteConsumptionStatus::NeverConsumable(
283 "target account of the transaction does not match neither the receiver account specified by the P2IDE storage, nor the reclaimer account".into()
284 )))
285 }
286 },
287
288 _ => Ok(None),
291 }
292 }
293}
294
295#[derive(Debug)]
307pub enum NoteConsumptionStatus {
308 Consumable,
310 ConsumableAfter(BlockNumber),
312 ConsumableWithAuthorization,
314 UnconsumableConditions,
317 NeverConsumable(Box<dyn Error + Send + Sync + 'static>),
319}
320
321impl Clone for NoteConsumptionStatus {
322 fn clone(&self) -> Self {
323 match self {
324 NoteConsumptionStatus::Consumable => NoteConsumptionStatus::Consumable,
325 NoteConsumptionStatus::ConsumableAfter(block_height) => {
326 NoteConsumptionStatus::ConsumableAfter(*block_height)
327 },
328 NoteConsumptionStatus::ConsumableWithAuthorization => {
329 NoteConsumptionStatus::ConsumableWithAuthorization
330 },
331 NoteConsumptionStatus::UnconsumableConditions => {
332 NoteConsumptionStatus::UnconsumableConditions
333 },
334 NoteConsumptionStatus::NeverConsumable(error) => {
335 let err = error.to_string();
336 NoteConsumptionStatus::NeverConsumable(err.into())
337 },
338 }
339 }
340}