Skip to main content

miden_base_sys/bindings/
active_note.rs

1extern crate alloc;
2use alloc::vec::Vec;
3
4use miden_stdlib_sys::{Felt, Word, WordAligned};
5
6use super::{
7    AccountId, Asset, MAX_ATTACHMENT_WORDS, MAX_ATTACHMENTS_PER_NOTE, NoteId, NoteMetadata,
8    RawAccountId, RawCommitmentWithCount, RawFoundIndex, Recipient, assert_attachment_count,
9    assert_attachment_word_count,
10};
11
12#[allow(improper_ctypes)]
13unsafe extern "C" {
14    // NOTE: In protocol v0.14, note "inputs" are exposed via `active_note::get_storage`.
15    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
16    #[link_name = "miden::protocol::active_note::get_storage"]
17    fn extern_note_get_storage(ptr: *mut Felt) -> usize;
18    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
19    #[link_name = "miden::protocol::active_note::get_initial_assets"]
20    fn extern_note_get_initial_assets(ptr: *mut Felt) -> usize;
21    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
22    #[link_name = "miden::protocol::active_note::get_sender"]
23    fn extern_note_get_sender(ptr: *mut RawAccountId);
24    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
25    #[link_name = "miden::protocol::active_note::get_recipient"]
26    fn extern_note_get_recipient(ptr: *mut Recipient);
27    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
28    #[link_name = "miden::protocol::active_note::get_script_root"]
29    fn extern_note_get_script_root(ptr: *mut Word);
30    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
31    #[link_name = "miden::protocol::active_note::get_serial_number"]
32    fn extern_note_get_serial_number(ptr: *mut Word);
33    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
34    #[link_name = "miden::protocol::active_note::get_metadata"]
35    fn extern_note_get_metadata(ptr: *mut NoteMetadata);
36    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
37    #[link_name = "miden::protocol::active_note::is_public"]
38    fn extern_note_is_public() -> Felt;
39    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
40    #[link_name = "miden::protocol::active_note::is_private"]
41    fn extern_note_is_private() -> Felt;
42    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
43    #[link_name = "miden::protocol::active_note::get_attachments_commitment"]
44    fn extern_note_get_attachments_commitment(ptr: *mut Word);
45    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
46    #[link_name = "miden::protocol::active_note::write_attachment_commitments_to_memory"]
47    fn extern_note_write_attachment_commitments_to_memory(dest_ptr: *mut Felt) -> usize;
48    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
49    #[link_name = "miden::protocol::active_note::write_attachment_to_memory"]
50    fn extern_note_write_attachment_to_memory(dest_ptr: *mut Felt, attachment_idx: Felt) -> usize;
51    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
52    #[link_name = "miden::protocol::active_note::find_attachment"]
53    fn extern_note_find_attachment(attachment_scheme: Felt, ptr: *mut RawFoundIndex);
54    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
55    #[link_name = "miden::protocol::active_note::get_initial_assets_info"]
56    fn extern_active_note_get_initial_assets_info(ptr: *mut RawCommitmentWithCount);
57    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
58    #[link_name = "miden::protocol::active_note::get_initial_num_assets"]
59    fn extern_active_note_get_initial_num_assets() -> Felt;
60    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
61    #[link_name = "miden::protocol::active_note::get_asset"]
62    fn extern_active_note_get_asset(asset_index: Felt, ptr: *mut Asset);
63    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
64    #[link_name = "miden::protocol::active_note::remove_asset"]
65    fn extern_active_note_remove_asset(
66        asset_id_0: Felt,
67        asset_id_1: Felt,
68        asset_id_2: Felt,
69        asset_id_3: Felt,
70        asset_value_0: Felt,
71        asset_value_1: Felt,
72        asset_value_2: Felt,
73        asset_value_3: Felt,
74        ptr: *mut Word,
75    );
76    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
77    #[link_name = "miden::protocol::active_note::get_note_id"]
78    fn extern_active_note_get_note_id(ptr: *mut NoteId);
79    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
80    #[link_name = "miden::protocol::active_note::get_storage_info"]
81    fn extern_active_note_get_storage_info(ptr: *mut RawCommitmentWithCount);
82}
83
84/// Contains summary information about the assets the active note was created with.
85pub struct ActiveNoteAssetsInfo {
86    /// The commitment over the assets the note was created with.
87    pub commitment: Word,
88    /// The number of assets the note was created with.
89    pub num_assets: u32,
90}
91
92/// Contains summary information about the storage stored in the active note.
93pub struct ActiveNoteStorageInfo {
94    /// The commitment over the note's storage.
95    pub commitment: Word,
96    /// The number of storage items the note was created with.
97    pub num_storage_items: u32,
98}
99
100/// Returns the storage of the currently executing note.
101///
102/// # Examples
103///
104/// Parse a note storage layout into domain types:
105///
106/// ```rust,ignore
107/// use miden::{active_note, AccountId, Asset};
108///
109/// let storage = active_note::get_storage();
110///
111/// // Example layout: first two values store a target `AccountId`.
112/// let target = AccountId::from(storage[0], storage[1]);
113/// ```
114pub fn get_storage() -> Vec<Felt> {
115    const MAX_INPUTS: usize = 1024;
116    let mut inputs: Vec<Felt> = Vec::with_capacity(MAX_INPUTS);
117    let num_inputs = unsafe {
118        // Ensure the pointer is a valid Miden pointer
119        //
120        // NOTE: This relies on the fact that BumpAlloc makes all allocations
121        // minimally word-aligned. Each word consists of 4 elements of 4 bytes.
122        // Since Miden VM is field element-addressable, to get a Miden address from a Rust address,
123        // we divide it by 4 to get the address in field elements.
124        let ptr = (inputs.as_mut_ptr() as usize) / 4;
125        // The protocol `active_note::get_storage` procedure writes the note's storage into memory
126        // starting at `dest_ptr` and returns the number of storage items written.
127        extern_note_get_storage(ptr as *mut Felt)
128    };
129    unsafe {
130        inputs.set_len(num_inputs);
131    }
132    inputs
133}
134
135/// Get the initial assets of the currently executing note.
136///
137/// These are the note's assets at creation time, unaffected by in-transaction removal.
138pub fn get_initial_assets() -> Vec<Asset> {
139    const MAX_INPUTS: usize = 256;
140    let mut inputs: Vec<Asset> = Vec::with_capacity(MAX_INPUTS);
141    let num_inputs = unsafe {
142        let ptr = (inputs.as_mut_ptr() as usize) / 4;
143        extern_note_get_initial_assets(ptr as *mut Felt)
144    };
145    unsafe {
146        inputs.set_len(num_inputs);
147    }
148    inputs
149}
150
151/// Returns the sender [`AccountId`] of the note that is currently executing.
152pub fn get_sender() -> AccountId {
153    unsafe {
154        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<RawAccountId>::uninit());
155        extern_note_get_sender(ret_area.as_mut_ptr());
156        ret_area.into_inner().assume_init().into_account_id()
157    }
158}
159
160/// Returns the recipient of the note that is currently executing.
161pub fn get_recipient() -> Recipient {
162    unsafe {
163        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<Recipient>::uninit());
164        extern_note_get_recipient(ret_area.as_mut_ptr());
165        ret_area.into_inner().assume_init()
166    }
167}
168
169/// Returns the script root of the currently executing note.
170pub fn get_script_root() -> Word {
171    unsafe {
172        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<Word>::uninit());
173        extern_note_get_script_root(ret_area.as_mut_ptr());
174        ret_area.into_inner().assume_init()
175    }
176}
177
178/// Returns the serial number of the currently executing note.
179pub fn get_serial_number() -> Word {
180    unsafe {
181        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<Word>::uninit());
182        extern_note_get_serial_number(ret_area.as_mut_ptr());
183        ret_area.into_inner().assume_init()
184    }
185}
186
187/// Returns the metadata header of the note that is currently executing.
188pub fn get_metadata() -> NoteMetadata {
189    unsafe {
190        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<NoteMetadata>::uninit());
191        extern_note_get_metadata(ret_area.as_mut_ptr());
192        ret_area.into_inner().assume_init()
193    }
194}
195
196/// Returns whether the note currently executing is public.
197#[inline]
198pub fn is_public() -> bool {
199    unsafe { extern_note_is_public() != Felt::new(0).unwrap() }
200}
201
202/// Returns whether the note currently executing is private.
203#[inline]
204pub fn is_private() -> bool {
205    unsafe { extern_note_is_private() != Felt::new(0).unwrap() }
206}
207
208/// Returns the commitment over all attachments of the note currently executing.
209pub fn get_attachments_commitment() -> Word {
210    unsafe {
211        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<Word>::uninit());
212        extern_note_get_attachments_commitment(ret_area.as_mut_ptr());
213        ret_area.into_inner().assume_init()
214    }
215}
216
217/// Returns the attachment commitments of the active note.
218///
219/// The name mirrors the kernel procedure, which fills the buffer this function returns.
220pub fn write_attachment_commitments_to_memory() -> Vec<Word> {
221    let mut commitments: Vec<Word> = Vec::with_capacity(MAX_ATTACHMENTS_PER_NOTE);
222    let num_attachments = unsafe {
223        let ptr = (commitments.as_mut_ptr() as usize) / 4;
224        extern_note_write_attachment_commitments_to_memory(ptr as *mut Felt)
225    };
226    assert_attachment_count(num_attachments);
227    unsafe {
228        commitments.set_len(num_attachments);
229    }
230    commitments
231}
232
233/// Returns the attachment at `attachment_idx` of the active note as protocol words.
234///
235/// The name mirrors the kernel procedure, which fills the buffer this function returns.
236pub fn write_attachment_to_memory(attachment_idx: u32) -> Vec<Word> {
237    let mut attachment: Vec<Word> = Vec::with_capacity(MAX_ATTACHMENT_WORDS);
238    let num_words = unsafe {
239        let ptr = (attachment.as_mut_ptr() as usize) / 4;
240        extern_note_write_attachment_to_memory(ptr as *mut Felt, Felt::from_u32(attachment_idx))
241    };
242    assert_attachment_word_count(num_words);
243    unsafe {
244        attachment.set_len(num_words);
245    }
246    attachment
247}
248
249/// Searches the active note metadata for `attachment_scheme`.
250pub fn find_attachment(attachment_scheme: Felt) -> Option<u32> {
251    unsafe {
252        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<RawFoundIndex>::uninit());
253        extern_note_find_attachment(attachment_scheme, ret_area.as_mut_ptr());
254        ret_area.into_inner().assume_init().into_attachment_index()
255    }
256}
257
258/// Returns the initial assets commitment and asset count of the active note.
259///
260/// These describe the note's assets at creation time, unaffected by in-transaction removal.
261pub fn get_initial_assets_info() -> ActiveNoteAssetsInfo {
262    unsafe {
263        let mut ret_area =
264            WordAligned::new(::core::mem::MaybeUninit::<RawCommitmentWithCount>::uninit());
265        extern_active_note_get_initial_assets_info(ret_area.as_mut_ptr());
266        let raw = ret_area.into_inner().assume_init();
267        ActiveNoteAssetsInfo {
268            commitment: raw.commitment,
269            num_assets: raw.num_items(),
270        }
271    }
272}
273
274/// Returns the number of assets the active note was created with.
275///
276/// The count is unaffected by in-transaction removal.
277#[inline]
278pub fn get_initial_num_assets() -> u32 {
279    // The transaction kernel guarantees asset counts fit in a u32.
280    let count = unsafe { extern_active_note_get_initial_num_assets() };
281    count.as_canonical_u64() as u32
282}
283
284/// Returns the asset at `asset_index` in the active note.
285///
286/// The asset is returned as it currently is: an asset that was already removed from the note reads
287/// back with both words empty.
288///
289/// # Panics
290///
291/// Panics if `asset_index` is out of bounds for the note.
292pub fn get_asset(asset_index: u32) -> Asset {
293    unsafe {
294        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<Asset>::uninit());
295        extern_active_note_get_asset(Felt::from_u32(asset_index), ret_area.as_mut_ptr());
296        ret_area.into_inner().assume_init()
297    }
298}
299
300/// Removes `asset` from the active note and returns the asset value left in the note.
301///
302/// The returned value is empty when the entire asset was removed.
303///
304/// # Panics
305///
306/// Panics if the asset is not present in the note, if a non-composable asset is not present with
307/// the exact value, if the note holds less of a fungible asset than is removed, if the asset id is
308/// empty or malformed, or if the asset's composition is `Custom`.
309pub fn remove_asset(asset: Asset) -> Word {
310    unsafe {
311        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<Word>::uninit());
312        let id = asset.id.inner;
313        extern_active_note_remove_asset(
314            id[0],
315            id[1],
316            id[2],
317            id[3],
318            asset.value[0],
319            asset.value[1],
320            asset.value[2],
321            asset.value[3],
322            ret_area.as_mut_ptr(),
323        );
324        ret_area.into_inner().assume_init()
325    }
326}
327
328/// Returns the ID of the active note, as cached by the transaction prologue.
329pub fn get_note_id() -> NoteId {
330    unsafe {
331        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<NoteId>::uninit());
332        extern_active_note_get_note_id(ret_area.as_mut_ptr());
333        ret_area.into_inner().assume_init()
334    }
335}
336
337/// Returns the storage commitment and storage item count of the active note.
338pub fn get_storage_info() -> ActiveNoteStorageInfo {
339    unsafe {
340        let mut ret_area =
341            WordAligned::new(::core::mem::MaybeUninit::<RawCommitmentWithCount>::uninit());
342        extern_active_note_get_storage_info(ret_area.as_mut_ptr());
343        let raw = ret_area.into_inner().assume_init();
344        ActiveNoteStorageInfo {
345            commitment: raw.commitment,
346            num_storage_items: raw.num_items(),
347        }
348    }
349}
350
351/// Trait that provides active-note operations for note scripts.
352///
353/// This trait is automatically implemented for the note input struct marked with the `#[note]`
354/// macro, so a `#[note_script]` entrypoint can call the operations directly on `self`, e.g.
355/// `self.get_sender()`.
356///
357/// The operations read the note that is currently executing. Call them only during note-script
358/// execution: a note value constructed outside of it (for example in a `#[note_constructor]`)
359/// has no active note, and the transaction kernel rejects the calls at run time.
360///
361/// `get_storage` is intentionally not part of this trait: the `#[note]` macro decodes the note
362/// storage into the struct fields, so the values are available directly on `self`.
363///
364/// An inherent method of the note struct with the same name shadows the trait method; the trait
365/// method stays reachable with UFCS, e.g. `<MyNote as ActiveNote>::get_sender(&note)`.
366pub trait ActiveNote {
367    /// Get the initial assets of the currently executing note.
368    ///
369    /// These are the note's assets at creation time, unaffected by in-transaction removal.
370    #[inline]
371    fn get_initial_assets(&self) -> Vec<Asset> {
372        get_initial_assets()
373    }
374
375    /// Returns the sender [`AccountId`] of the note that is currently executing.
376    #[inline]
377    fn get_sender(&self) -> AccountId {
378        get_sender()
379    }
380
381    /// Returns the recipient of the note that is currently executing.
382    #[inline]
383    fn get_recipient(&self) -> Recipient {
384        get_recipient()
385    }
386
387    /// Returns the script root of the currently executing note.
388    #[inline]
389    fn get_script_root(&self) -> Word {
390        get_script_root()
391    }
392
393    /// Returns the serial number of the currently executing note.
394    #[inline]
395    fn get_serial_number(&self) -> Word {
396        get_serial_number()
397    }
398
399    /// Returns the metadata header of the note that is currently executing.
400    #[inline]
401    fn get_metadata(&self) -> NoteMetadata {
402        get_metadata()
403    }
404
405    /// Returns whether the note currently executing is public.
406    #[inline]
407    fn is_public(&self) -> bool {
408        is_public()
409    }
410
411    /// Returns whether the note currently executing is private.
412    #[inline]
413    fn is_private(&self) -> bool {
414        is_private()
415    }
416
417    /// Returns the commitment over all attachments of the note currently executing.
418    #[inline]
419    fn get_attachments_commitment(&self) -> Word {
420        get_attachments_commitment()
421    }
422
423    /// Returns the attachment commitments of the active note.
424    #[inline]
425    fn write_attachment_commitments_to_memory(&self) -> Vec<Word> {
426        write_attachment_commitments_to_memory()
427    }
428
429    /// Returns the attachment at `attachment_idx` of the active note as protocol words.
430    #[inline]
431    fn write_attachment_to_memory(&self, attachment_idx: u32) -> Vec<Word> {
432        write_attachment_to_memory(attachment_idx)
433    }
434
435    /// Searches the active note metadata for `attachment_scheme`.
436    #[inline]
437    fn find_attachment(&self, attachment_scheme: Felt) -> Option<u32> {
438        find_attachment(attachment_scheme)
439    }
440
441    /// Returns the initial assets commitment and asset count of the note that is currently
442    /// executing.
443    ///
444    /// These describe the note's assets at creation time, unaffected by in-transaction removal.
445    #[inline]
446    fn get_initial_assets_info(&self) -> ActiveNoteAssetsInfo {
447        get_initial_assets_info()
448    }
449
450    /// Returns the number of assets the currently executing note was created with.
451    ///
452    /// The count is unaffected by in-transaction removal.
453    #[inline]
454    fn get_initial_num_assets(&self) -> u32 {
455        get_initial_num_assets()
456    }
457
458    /// Returns the asset at `asset_index` in the note that is currently executing.
459    ///
460    /// The asset is returned as it currently is: an asset that was already removed from the note
461    /// reads back with both words empty.
462    ///
463    /// # Panics
464    ///
465    /// Panics if `asset_index` is out of bounds for the note.
466    #[inline]
467    fn get_asset(&self, asset_index: u32) -> Asset {
468        get_asset(asset_index)
469    }
470
471    /// Returns the ID of the note that is currently executing.
472    #[inline]
473    fn get_note_id(&self) -> NoteId {
474        get_note_id()
475    }
476
477    /// Returns the storage commitment and storage item count of the currently executing note.
478    #[inline]
479    fn get_storage_info(&self) -> ActiveNoteStorageInfo {
480        get_storage_info()
481    }
482
483    /// Removes `asset` from the currently executing note and returns the asset value left in it.
484    ///
485    /// The returned value is empty when the entire asset was removed. This mutates the note's
486    /// assets in the transaction kernel, so the note script needs a `mut self` receiver to call it.
487    ///
488    /// # Panics
489    ///
490    /// Panics under the same conditions as [`remove_asset`].
491    #[inline]
492    fn remove_asset(&mut self, asset: Asset) -> Word {
493        remove_asset(asset)
494    }
495}