miden_standards/note/config/
faucet_policy_config.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;
24use crate::note::NetworkAccountTarget;
25use crate::note::costs::{FAUCET_POLICY_CONFIG_CONSUMPTION_CYCLES, NoteConsumptionCost};
26
27const FAUCET_POLICY_CONFIG_SCRIPT_PATH: &str =
32 "::miden::standards::notes::faucet_policy_config::main";
33
34static FAUCET_POLICY_CONFIG_SCRIPT: LazyLock<NoteScript> = LazyLock::new(|| {
36 let standards_lib = StandardsLib::default();
37 let path = Path::new(FAUCET_POLICY_CONFIG_SCRIPT_PATH);
38 NoteScript::from_package_reference(standards_lib.as_ref(), path)
39 .expect("Standards library contains FAUCET_POLICY_CONFIG note script procedure")
40});
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum FaucetPolicyConfig {
60 SetMintPolicy { policy_root: AccountProcedureRoot },
62 SetBurnPolicy { policy_root: AccountProcedureRoot },
64 SetSendPolicy { policy_root: AccountProcedureRoot },
66 SetReceivePolicy { policy_root: AccountProcedureRoot },
68}
69
70impl FaucetPolicyConfig {
71 const VARIANT_SET_MINT_POLICY: u8 = 0;
77 const VARIANT_SET_BURN_POLICY: u8 = 1;
78 const VARIANT_SET_SEND_POLICY: u8 = 2;
79 const VARIANT_SET_RECEIVE_POLICY: u8 = 3;
80
81 fn parts(self) -> (u8, AccountProcedureRoot) {
83 match self {
84 FaucetPolicyConfig::SetMintPolicy { policy_root } => {
85 (Self::VARIANT_SET_MINT_POLICY, policy_root)
86 },
87 FaucetPolicyConfig::SetBurnPolicy { policy_root } => {
88 (Self::VARIANT_SET_BURN_POLICY, policy_root)
89 },
90 FaucetPolicyConfig::SetSendPolicy { policy_root } => {
91 (Self::VARIANT_SET_SEND_POLICY, policy_root)
92 },
93 FaucetPolicyConfig::SetReceivePolicy { policy_root } => {
94 (Self::VARIANT_SET_RECEIVE_POLICY, policy_root)
95 },
96 }
97 }
98
99 fn to_storage_values(self) -> Vec<Felt> {
101 let (variant, policy_root) = self.parts();
102 let mut values = Vec::with_capacity(FaucetPolicyConfigNote::NUM_STORAGE_ITEMS);
103 values.extend_from_slice(policy_root.as_word().as_elements());
104 values.push(Felt::from(variant));
105 values
106 }
107}
108
109impl From<FaucetPolicyConfig> for NoteStorage {
110 fn from(config: FaucetPolicyConfig) -> Self {
111 NoteStorage::new(config.to_storage_values())
112 .expect("number of storage items should not exceed max storage items")
113 }
114}
115
116#[derive(Debug, Clone)]
142pub struct FaucetPolicyConfigNote {
143 sender: AccountId,
144 target: AccountId,
145 config: FaucetPolicyConfig,
146 serial_number: Word,
147 attachments: NoteAttachments,
148}
149
150#[bon::bon]
151impl FaucetPolicyConfigNote {
152 #[builder]
163 pub fn new(
164 #[builder(field)] mut attachments: Vec<NoteAttachment>,
165 sender: AccountId,
166 target: AccountId,
167 config: FaucetPolicyConfig,
168 serial_number: Word,
169 ) -> Result<Self, NoteError> {
170 NetworkAccountTarget::ensure_presence(&mut attachments, target).map_err(|err| {
173 NoteError::other_with_source(
174 "failed to bind the FaucetPolicyConfig note to its target account",
175 err,
176 )
177 })?;
178
179 let attachments = NoteAttachments::new(attachments)?;
180
181 Ok(Self {
182 sender,
183 target,
184 config,
185 serial_number,
186 attachments,
187 })
188 }
189}
190
191impl FaucetPolicyConfigNote {
192 pub const NUM_STORAGE_ITEMS: usize = 5;
197
198 pub fn script() -> NoteScript {
203 FAUCET_POLICY_CONFIG_SCRIPT.clone()
204 }
205
206 pub fn script_root() -> NoteScriptRoot {
208 FAUCET_POLICY_CONFIG_SCRIPT.root()
209 }
210
211 pub fn sender(&self) -> AccountId {
214 self.sender
215 }
216
217 pub fn target(&self) -> AccountId {
219 self.target
220 }
221
222 pub fn config(&self) -> FaucetPolicyConfig {
224 self.config
225 }
226
227 pub fn serial_number(&self) -> Word {
229 self.serial_number
230 }
231
232 pub fn attachments(&self) -> &NoteAttachments {
234 &self.attachments
235 }
236}
237
238impl<S: faucet_policy_config_note_builder::State> FaucetPolicyConfigNoteBuilder<S> {
242 pub fn attachment(mut self, attachment: impl Into<NoteAttachment>) -> Self {
244 self.attachments.push(attachment.into());
245 self
246 }
247
248 pub fn attachments(
250 mut self,
251 attachments: impl IntoIterator<Item = impl Into<NoteAttachment>>,
252 ) -> Self {
253 self.attachments.extend(attachments.into_iter().map(Into::into));
254 self
255 }
256}
257
258impl<S: faucet_policy_config_note_builder::State> FaucetPolicyConfigNoteBuilder<S>
259where
260 S::SerialNumber: faucet_policy_config_note_builder::IsUnset,
261{
262 pub fn generate_serial_number(
264 self,
265 rng: &mut impl FeltRng,
266 ) -> FaucetPolicyConfigNoteBuilder<faucet_policy_config_note_builder::SetSerialNumber<S>> {
267 self.serial_number(rng.draw_word())
268 }
269}
270
271impl From<FaucetPolicyConfigNote> for Note {
275 fn from(note: FaucetPolicyConfigNote) -> Self {
276 let metadata = PartialNoteMetadata::new(note.sender, NoteType::Public)
279 .with_tag(NoteTag::with_account_target(note.target));
280 let recipient = NoteRecipient::new(
281 note.serial_number,
282 FaucetPolicyConfigNote::script(),
283 NoteStorage::from(note.config),
284 );
285
286 Note::with_attachments(NoteAssets::default(), metadata, recipient, note.attachments)
287 }
288}
289
290impl NoteConsumptionCost for FaucetPolicyConfigNote {
294 fn consumption_cycles() -> u32 {
295 FAUCET_POLICY_CONFIG_CONSUMPTION_CYCLES
296 }
297}
298
299#[cfg(test)]
303mod tests {
304 use miden_protocol::account::AccountType;
305 use miden_protocol::crypto::rand::RandomCoin;
306
307 use super::*;
308
309 fn account_id(seed: u8) -> AccountId {
310 AccountId::builder()
311 .account_type(AccountType::Public)
312 .build_with_seed([seed; 32])
313 }
314
315 fn policy_root(seed: u32) -> AccountProcedureRoot {
316 AccountProcedureRoot::from_raw(Word::from([seed, seed + 1, seed + 2, seed + 3]))
317 }
318
319 #[test]
321 fn builder_builds_faucet_policy_config_note() {
322 let mut rng = RandomCoin::new(Word::empty());
323 let faucet = account_id(1);
324 let sender = account_id(2);
325
326 let note = FaucetPolicyConfigNote::builder()
327 .sender(sender)
328 .target(faucet)
329 .config(FaucetPolicyConfig::SetMintPolicy { policy_root: policy_root(10) })
330 .generate_serial_number(&mut rng)
331 .build()
332 .unwrap();
333
334 assert_eq!(note.sender(), sender);
335 assert_eq!(note.target(), faucet);
336
337 let note = Note::from(note);
338 assert_eq!(note.metadata().note_type(), NoteType::Public);
339 assert_eq!(note.metadata().tag(), NoteTag::with_account_target(faucet));
340 assert_eq!(note.assets().num_assets(), 0);
341 }
342
343 #[test]
345 fn storage_layout() {
346 let root = policy_root(10);
347
348 let cases = [
349 (
350 FaucetPolicyConfig::SetMintPolicy { policy_root: root },
351 FaucetPolicyConfig::VARIANT_SET_MINT_POLICY,
352 ),
353 (
354 FaucetPolicyConfig::SetBurnPolicy { policy_root: root },
355 FaucetPolicyConfig::VARIANT_SET_BURN_POLICY,
356 ),
357 (
358 FaucetPolicyConfig::SetSendPolicy { policy_root: root },
359 FaucetPolicyConfig::VARIANT_SET_SEND_POLICY,
360 ),
361 (
362 FaucetPolicyConfig::SetReceivePolicy { policy_root: root },
363 FaucetPolicyConfig::VARIANT_SET_RECEIVE_POLICY,
364 ),
365 ];
366
367 for (action, variant) in cases {
368 let storage = NoteStorage::from(action);
369 let mut expected = Vec::from(root.as_word().as_elements());
370 expected.push(Felt::from(variant));
371 assert_eq!(storage.items(), expected.as_slice());
372 assert_eq!(storage.items().len(), FaucetPolicyConfigNote::NUM_STORAGE_ITEMS);
373 }
374 }
375}