miden_standards/note/
faucet_policy_action.rs1use alloc::vec::Vec;
2
3use miden_protocol::account::{AccountId, AccountProcedureRoot};
4use miden_protocol::assembly::Path;
5use miden_protocol::crypto::rand::FeltRng;
6use miden_protocol::errors::NoteError;
7use miden_protocol::note::{
8 Note,
9 NoteAssets,
10 NoteAttachment,
11 NoteAttachments,
12 NoteRecipient,
13 NoteScript,
14 NoteScriptRoot,
15 NoteStorage,
16 NoteTag,
17 NoteType,
18 PartialNoteMetadata,
19};
20use miden_protocol::utils::sync::LazyLock;
21use miden_protocol::{Felt, Word};
22
23use crate::StandardsLib;
24
25const FAUCET_POLICY_ACTION_SCRIPT_PATH: &str =
30 "::miden::standards::notes::faucet_policy_action::main";
31
32static FAUCET_POLICY_ACTION_SCRIPT: LazyLock<NoteScript> = LazyLock::new(|| {
34 let standards_lib = StandardsLib::default();
35 let path = Path::new(FAUCET_POLICY_ACTION_SCRIPT_PATH);
36 NoteScript::from_library_reference(standards_lib.as_ref(), path)
37 .expect("Standards library contains FAUCET_POLICY_ACTION note script procedure")
38});
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum FaucetPolicyAction {
58 SetMintPolicy { policy_root: AccountProcedureRoot },
60 SetBurnPolicy { policy_root: AccountProcedureRoot },
62 SetSendPolicy { policy_root: AccountProcedureRoot },
64 SetReceivePolicy { policy_root: AccountProcedureRoot },
66}
67
68impl FaucetPolicyAction {
69 const SELECTOR_SET_MINT_POLICY: u8 = 0;
75 const SELECTOR_SET_BURN_POLICY: u8 = 1;
76 const SELECTOR_SET_SEND_POLICY: u8 = 2;
77 const SELECTOR_SET_RECEIVE_POLICY: u8 = 3;
78
79 fn parts(self) -> (u8, AccountProcedureRoot) {
81 match self {
82 FaucetPolicyAction::SetMintPolicy { policy_root } => {
83 (Self::SELECTOR_SET_MINT_POLICY, policy_root)
84 },
85 FaucetPolicyAction::SetBurnPolicy { policy_root } => {
86 (Self::SELECTOR_SET_BURN_POLICY, policy_root)
87 },
88 FaucetPolicyAction::SetSendPolicy { policy_root } => {
89 (Self::SELECTOR_SET_SEND_POLICY, policy_root)
90 },
91 FaucetPolicyAction::SetReceivePolicy { policy_root } => {
92 (Self::SELECTOR_SET_RECEIVE_POLICY, policy_root)
93 },
94 }
95 }
96
97 fn to_storage_values(self) -> Vec<Felt> {
99 let (selector, policy_root) = self.parts();
100 let mut values = Vec::with_capacity(FaucetPolicyActionNote::NUM_STORAGE_ITEMS);
101 values.push(Felt::from(selector));
102 values.extend_from_slice(policy_root.as_word().as_elements());
103 values
104 }
105}
106
107impl From<FaucetPolicyAction> for NoteStorage {
108 fn from(action: FaucetPolicyAction) -> Self {
109 NoteStorage::new(action.to_storage_values())
110 .expect("number of storage items should not exceed max storage items")
111 }
112}
113
114#[derive(Debug, Clone)]
135pub struct FaucetPolicyActionNote {
136 sender: AccountId,
137 account: AccountId,
138 action: FaucetPolicyAction,
139 serial_number: Word,
140 attachments: NoteAttachments,
141}
142
143#[bon::bon]
144impl FaucetPolicyActionNote {
145 #[builder]
152 pub fn new(
153 #[builder(field)] attachments: Vec<NoteAttachment>,
154 sender: AccountId,
155 account: AccountId,
156 action: FaucetPolicyAction,
157 serial_number: Word,
158 ) -> Result<Self, NoteError> {
159 let attachments = NoteAttachments::new(attachments)?;
160
161 Ok(Self {
162 sender,
163 account,
164 action,
165 serial_number,
166 attachments,
167 })
168 }
169}
170
171impl FaucetPolicyActionNote {
172 pub const NUM_STORAGE_ITEMS: usize = 5;
177
178 pub fn script() -> NoteScript {
183 FAUCET_POLICY_ACTION_SCRIPT.clone()
184 }
185
186 pub fn script_root() -> NoteScriptRoot {
188 FAUCET_POLICY_ACTION_SCRIPT.root()
189 }
190
191 pub fn sender(&self) -> AccountId {
193 self.sender
194 }
195
196 pub fn account(&self) -> AccountId {
198 self.account
199 }
200
201 pub fn action(&self) -> FaucetPolicyAction {
203 self.action
204 }
205
206 pub fn serial_number(&self) -> Word {
208 self.serial_number
209 }
210
211 pub fn attachments(&self) -> &NoteAttachments {
213 &self.attachments
214 }
215}
216
217impl<S: faucet_policy_action_note_builder::State> FaucetPolicyActionNoteBuilder<S> {
221 pub fn attachment(mut self, attachment: impl Into<NoteAttachment>) -> Self {
223 self.attachments.push(attachment.into());
224 self
225 }
226
227 pub fn attachments(
229 mut self,
230 attachments: impl IntoIterator<Item = impl Into<NoteAttachment>>,
231 ) -> Self {
232 self.attachments.extend(attachments.into_iter().map(Into::into));
233 self
234 }
235}
236
237impl<S: faucet_policy_action_note_builder::State> FaucetPolicyActionNoteBuilder<S>
238where
239 S::SerialNumber: faucet_policy_action_note_builder::IsUnset,
240{
241 pub fn generate_serial_number(
243 self,
244 rng: &mut impl FeltRng,
245 ) -> FaucetPolicyActionNoteBuilder<faucet_policy_action_note_builder::SetSerialNumber<S>> {
246 self.serial_number(rng.draw_word())
247 }
248}
249
250impl From<FaucetPolicyActionNote> for Note {
254 fn from(note: FaucetPolicyActionNote) -> Self {
255 let metadata = PartialNoteMetadata::new(note.sender, NoteType::Public)
258 .with_tag(NoteTag::with_account_target(note.account));
259 let recipient = NoteRecipient::new(
260 note.serial_number,
261 FaucetPolicyActionNote::script(),
262 NoteStorage::from(note.action),
263 );
264
265 Note::with_attachments(NoteAssets::default(), metadata, recipient, note.attachments)
266 }
267}
268
269#[cfg(test)]
273mod tests {
274 use miden_protocol::account::AccountType;
275 use miden_protocol::crypto::rand::RandomCoin;
276
277 use super::*;
278
279 fn account_id(seed: u8) -> AccountId {
280 AccountId::builder()
281 .account_type(AccountType::Public)
282 .build_with_seed([seed; 32])
283 }
284
285 fn policy_root(seed: u32) -> AccountProcedureRoot {
286 AccountProcedureRoot::from_raw(Word::from([seed, seed + 1, seed + 2, seed + 3]))
287 }
288
289 #[test]
291 fn builder_builds_faucet_policy_action_note() {
292 let mut rng = RandomCoin::new(Word::empty());
293 let faucet = account_id(1);
294 let sender = account_id(2);
295
296 let note = FaucetPolicyActionNote::builder()
297 .sender(sender)
298 .account(faucet)
299 .action(FaucetPolicyAction::SetMintPolicy { policy_root: policy_root(10) })
300 .generate_serial_number(&mut rng)
301 .build()
302 .unwrap();
303
304 assert_eq!(note.sender(), sender);
305 assert_eq!(note.account(), faucet);
306
307 let note = Note::from(note);
308 assert_eq!(note.metadata().note_type(), NoteType::Public);
309 assert_eq!(note.metadata().tag(), NoteTag::with_account_target(faucet));
310 assert_eq!(note.assets().num_assets(), 0);
311 }
312
313 #[test]
315 fn storage_layout() {
316 let root = policy_root(10);
317
318 let cases = [
319 (
320 FaucetPolicyAction::SetMintPolicy { policy_root: root },
321 FaucetPolicyAction::SELECTOR_SET_MINT_POLICY,
322 ),
323 (
324 FaucetPolicyAction::SetBurnPolicy { policy_root: root },
325 FaucetPolicyAction::SELECTOR_SET_BURN_POLICY,
326 ),
327 (
328 FaucetPolicyAction::SetSendPolicy { policy_root: root },
329 FaucetPolicyAction::SELECTOR_SET_SEND_POLICY,
330 ),
331 (
332 FaucetPolicyAction::SetReceivePolicy { policy_root: root },
333 FaucetPolicyAction::SELECTOR_SET_RECEIVE_POLICY,
334 ),
335 ];
336
337 for (action, selector) in cases {
338 let storage = NoteStorage::from(action);
339 let mut expected = alloc::vec![Felt::from(selector)];
340 expected.extend_from_slice(root.as_word().as_elements());
341 assert_eq!(storage.items(), expected.as_slice());
342 assert_eq!(storage.items().len(), FaucetPolicyActionNote::NUM_STORAGE_ITEMS);
343 }
344 }
345}