miden_standards/note/
pause_action.rs1use alloc::vec::Vec;
2
3use miden_protocol::account::AccountId;
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 PAUSE_ACTION_SCRIPT_PATH: &str = "::miden::standards::notes::pause_action::main";
30
31static PAUSE_ACTION_SCRIPT: LazyLock<NoteScript> = LazyLock::new(|| {
33 let standards_lib = StandardsLib::default();
34 let path = Path::new(PAUSE_ACTION_SCRIPT_PATH);
35 NoteScript::from_library_reference(standards_lib.as_ref(), path)
36 .expect("Standards library contains PAUSE_ACTION note script procedure")
37});
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum PauseAction {
52 Pause,
54 Unpause,
56}
57
58impl PauseAction {
59 const SELECTOR_PAUSE: u8 = 0;
64 const SELECTOR_UNPAUSE: u8 = 1;
65
66 fn to_storage_values(self) -> Vec<Felt> {
68 match self {
69 PauseAction::Pause => vec![Felt::from(Self::SELECTOR_PAUSE)],
70 PauseAction::Unpause => vec![Felt::from(Self::SELECTOR_UNPAUSE)],
71 }
72 }
73}
74
75impl From<PauseAction> for NoteStorage {
76 fn from(action: PauseAction) -> Self {
77 NoteStorage::new(action.to_storage_values())
78 .expect("number of storage items should not exceed max storage items")
79 }
80}
81
82#[derive(Debug, Clone)]
102pub struct PauseActionNote {
103 sender: AccountId,
104 account: AccountId,
105 action: PauseAction,
106 serial_number: Word,
107 attachments: NoteAttachments,
108}
109
110#[bon::bon]
111impl PauseActionNote {
112 #[builder]
119 pub fn new(
120 #[builder(field)] attachments: Vec<NoteAttachment>,
121 sender: AccountId,
122 account: AccountId,
123 action: PauseAction,
124 serial_number: Word,
125 ) -> Result<Self, NoteError> {
126 let attachments = NoteAttachments::new(attachments)?;
127
128 Ok(Self {
129 sender,
130 account,
131 action,
132 serial_number,
133 attachments,
134 })
135 }
136}
137
138impl PauseActionNote {
139 pub const NUM_STORAGE_ITEMS: usize = 1;
144
145 pub fn script() -> NoteScript {
150 PAUSE_ACTION_SCRIPT.clone()
151 }
152
153 pub fn script_root() -> NoteScriptRoot {
155 PAUSE_ACTION_SCRIPT.root()
156 }
157
158 pub fn sender(&self) -> AccountId {
160 self.sender
161 }
162
163 pub fn account(&self) -> AccountId {
165 self.account
166 }
167
168 pub fn action(&self) -> PauseAction {
170 self.action
171 }
172
173 pub fn serial_number(&self) -> Word {
175 self.serial_number
176 }
177
178 pub fn attachments(&self) -> &NoteAttachments {
180 &self.attachments
181 }
182}
183
184impl<S: pause_action_note_builder::State> PauseActionNoteBuilder<S> {
188 pub fn attachment(mut self, attachment: impl Into<NoteAttachment>) -> Self {
190 self.attachments.push(attachment.into());
191 self
192 }
193
194 pub fn attachments(
196 mut self,
197 attachments: impl IntoIterator<Item = impl Into<NoteAttachment>>,
198 ) -> Self {
199 self.attachments.extend(attachments.into_iter().map(Into::into));
200 self
201 }
202}
203
204impl<S: pause_action_note_builder::State> PauseActionNoteBuilder<S>
205where
206 S::SerialNumber: pause_action_note_builder::IsUnset,
207{
208 pub fn generate_serial_number(
210 self,
211 rng: &mut impl FeltRng,
212 ) -> PauseActionNoteBuilder<pause_action_note_builder::SetSerialNumber<S>> {
213 self.serial_number(rng.draw_word())
214 }
215}
216
217impl From<PauseActionNote> for Note {
221 fn from(note: PauseActionNote) -> Self {
222 let metadata = PartialNoteMetadata::new(note.sender, NoteType::Public)
225 .with_tag(NoteTag::with_account_target(note.account));
226 let recipient = NoteRecipient::new(
227 note.serial_number,
228 PauseActionNote::script(),
229 NoteStorage::from(note.action),
230 );
231
232 Note::with_attachments(NoteAssets::default(), metadata, recipient, note.attachments)
233 }
234}
235
236#[cfg(test)]
240mod tests {
241 use miden_protocol::account::AccountType;
242 use miden_protocol::crypto::rand::RandomCoin;
243
244 use super::*;
245
246 fn account_id(seed: u8) -> AccountId {
247 AccountId::builder()
248 .account_type(AccountType::Public)
249 .build_with_seed([seed; 32])
250 }
251
252 #[test]
254 fn builder_builds_pause_action_note() {
255 let mut rng = RandomCoin::new(Word::empty());
256 let managed = account_id(1);
257 let sender = account_id(2);
258
259 let note = PauseActionNote::builder()
260 .sender(sender)
261 .account(managed)
262 .action(PauseAction::Pause)
263 .generate_serial_number(&mut rng)
264 .build()
265 .unwrap();
266
267 assert_eq!(note.sender(), sender);
268 assert_eq!(note.account(), managed);
269
270 let note = Note::from(note);
271 assert_eq!(note.metadata().note_type(), NoteType::Public);
272 assert_eq!(note.metadata().tag(), NoteTag::with_account_target(managed));
273 assert_eq!(note.assets().num_assets(), 0);
274 }
275
276 #[test]
278 fn action_storage_layout() {
279 let pause = NoteStorage::from(PauseAction::Pause);
280 assert_eq!(pause.items(), &[Felt::from(PauseAction::SELECTOR_PAUSE)]);
281
282 let unpause = NoteStorage::from(PauseAction::Unpause);
283 assert_eq!(unpause.items(), &[Felt::from(PauseAction::SELECTOR_UNPAUSE)]);
284 }
285}