Skip to main content

miden_base_sys/bindings/
note.rs

1extern crate alloc;
2
3use alloc::vec::Vec;
4
5use miden_stdlib_sys::{Felt, Word, WordAligned};
6
7use super::{
8    AccountId, MAX_ATTACHMENT_WORDS, MAX_ATTACHMENTS_PER_NOTE, NoteType, RawAccountId,
9    RawAttachmentLocation, Recipient, Tag, assert_attachment_count, assert_attachment_word_count,
10};
11
12const MAX_NOTE_STORAGE_ITEMS: usize = 1024;
13
14#[allow(improper_ctypes)]
15unsafe extern "C" {
16    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
17    #[link_name = "miden::protocol::note::compute_and_store_recipient"]
18    fn extern_note_build_recipient(
19        storage_ptr: *mut Felt,
20        num_storage_items: usize,
21        serial_num_f0: Felt,
22        serial_num_f1: Felt,
23        serial_num_f2: Felt,
24        serial_num_f3: Felt,
25        script_root_f0: Felt,
26        script_root_f1: Felt,
27        script_root_f2: Felt,
28        script_root_f3: Felt,
29        ptr: *mut Recipient,
30    );
31    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
32    #[link_name = "miden::protocol::note::compute_storage_commitment"]
33    fn extern_note_compute_storage_commitment(
34        storage_ptr: *const Felt,
35        num_storage_items: usize,
36        ptr: *mut Word,
37    );
38    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
39    #[link_name = "miden::protocol::note::write_attachment_commitments_to_memory"]
40    fn extern_note_write_attachment_commitments_to_memory(
41        attachments_commitment_f0: Felt,
42        attachments_commitment_f1: Felt,
43        attachments_commitment_f2: Felt,
44        attachments_commitment_f3: Felt,
45        dest_ptr: *mut Felt,
46    ) -> usize;
47    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
48    #[link_name = "miden::protocol::note::write_attachment_to_memory"]
49    fn extern_note_write_attachment_to_memory(
50        attachment_commitment_f0: Felt,
51        attachment_commitment_f1: Felt,
52        attachment_commitment_f2: Felt,
53        attachment_commitment_f3: Felt,
54        dest_ptr: *mut Felt,
55    ) -> usize;
56    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
57    #[link_name = "miden::protocol::note::write_indexed_attachment_to_memory"]
58    fn extern_note_write_indexed_attachment_to_memory(
59        num_attachments: Felt,
60        attachment_commitments_ptr: *const Felt,
61        attachment_idx: Felt,
62        dest_ptr: *mut Felt,
63    ) -> usize;
64    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
65    #[link_name = "miden::protocol::note::compute_recipient"]
66    fn extern_note_compute_recipient(
67        serial_num_f0: Felt,
68        serial_num_f1: Felt,
69        serial_num_f2: Felt,
70        serial_num_f3: Felt,
71        script_root_f0: Felt,
72        script_root_f1: Felt,
73        script_root_f2: Felt,
74        script_root_f3: Felt,
75        storage_commitment_f0: Felt,
76        storage_commitment_f1: Felt,
77        storage_commitment_f2: Felt,
78        storage_commitment_f3: Felt,
79        ptr: *mut Recipient,
80    );
81    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
82    #[link_name = "miden::protocol::note::metadata_into_sender"]
83    fn extern_note_metadata_into_sender(
84        metadata_f0: Felt,
85        metadata_f1: Felt,
86        metadata_f2: Felt,
87        metadata_f3: Felt,
88        ptr: *mut RawAccountId,
89    );
90    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
91    #[link_name = "miden::protocol::note::metadata_into_attachment_schemes"]
92    fn extern_note_metadata_into_attachment_schemes(
93        metadata_f0: Felt,
94        metadata_f1: Felt,
95        metadata_f2: Felt,
96        metadata_f3: Felt,
97        ptr: *mut Word,
98    );
99    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
100    #[link_name = "miden::protocol::note::metadata_into_note_type"]
101    fn extern_note_metadata_into_note_type(
102        metadata_f0: Felt,
103        metadata_f1: Felt,
104        metadata_f2: Felt,
105        metadata_f3: Felt,
106    ) -> Felt;
107    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
108    #[link_name = "miden::protocol::note::metadata_into_tag"]
109    fn extern_note_metadata_into_tag(
110        metadata_f0: Felt,
111        metadata_f1: Felt,
112        metadata_f2: Felt,
113        metadata_f3: Felt,
114    ) -> Felt;
115    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
116    #[link_name = "miden::protocol::note::find_attachment_idx"]
117    fn extern_note_find_attachment_idx(
118        attachment_scheme: Felt,
119        metadata_f0: Felt,
120        metadata_f1: Felt,
121        metadata_f2: Felt,
122        metadata_f3: Felt,
123        ptr: *mut RawAttachmentLocation,
124    );
125    // The name must stay in lockstep with the stub's `export_name` (`stubs/note.rs`) and
126    // `SCRIPT_ROOT_STUB_NAME` in the compiler frontend (`frontend/wasm/src/intrinsics/note.rs`).
127    #[cfg_attr(target_family = "wasm", linkage = "extern_weak")]
128    #[link_name = "intrinsics::note::script_root"]
129    fn extern_note_script_root(ptr: *mut Word);
130}
131
132/// Returns the MAST root digest of the note script defined by the current crate.
133///
134/// Macro plumbing behind the `get_entrypoint_root()` associated method that `#[note]` generates
135/// on the note input type — call that method instead of this function. It lives here because
136/// the underlying weak extern requires `feature(linkage)`, which user crates do not enable.
137///
138/// This is a compiler intrinsic: the call compiles to a MASM `procref` of the crate's
139/// `#[note_script]` entrypoint export, so the digest is the note script root observed by the
140/// transaction kernel when the note is executed. The digest is computed at assembly time.
141///
142/// Compilation fails if the current project does not define a `#[note_script]` entrypoint.
143///
144/// Must not be called from code reachable from the `#[note_script]` entrypoint itself: the note
145/// script's MAST root would then depend on its own digest, and assembly fails with a call-graph
146/// cycle error. Inside a running note script, use [`active_note::get_script_root`] instead.
147///
148/// [`active_note::get_script_root`]: crate::bindings::active_note::get_script_root
149#[doc(hidden)]
150pub fn __entrypoint_root() -> Word {
151    unsafe {
152        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<Word>::uninit());
153        extern_note_script_root(ret_area.as_mut_ptr());
154        ret_area.into_inner().assume_init()
155    }
156}
157
158/// Computes and stores a note recipient from serial number, script root, and storage elements.
159///
160/// This maps to `miden::protocol::note::compute_and_store_recipient`, which also inserts the
161/// provided storage into the advice map under the storage commitment used by the returned
162/// recipient digest.
163///
164/// Panics if `storage` contains more than 1024 elements.
165pub fn compute_and_store_recipient(
166    serial_num: Word,
167    script_root: Word,
168    storage: Vec<Felt>,
169) -> Recipient {
170    assert!(
171        storage.len() <= MAX_NOTE_STORAGE_ITEMS,
172        "note storage cannot contain more than {MAX_NOTE_STORAGE_ITEMS} items"
173    );
174
175    let rust_ptr = if storage.is_empty() {
176        0
177    } else {
178        storage.as_ptr().addr() as u32
179    };
180    let miden_ptr = rust_ptr / 4;
181
182    // Vec storage comes from the SDK allocator, which only produces word-aligned pointers.
183    assert_eq!(miden_ptr % 4, 0, "storage pointer must be word-aligned");
184
185    unsafe {
186        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<Recipient>::uninit());
187        extern_note_build_recipient(
188            miden_ptr as *mut Felt,
189            storage.len(),
190            serial_num[0],
191            serial_num[1],
192            serial_num[2],
193            serial_num[3],
194            script_root[0],
195            script_root[1],
196            script_root[2],
197            script_root[3],
198            ret_area.as_mut_ptr(),
199        );
200        ret_area.into_inner().assume_init()
201    }
202}
203
204/// Builds a note recipient from the provided serial number, script root, and storage elements.
205///
206/// This is retained as an SDK-friendly alias for [`compute_and_store_recipient`].
207pub fn build_recipient(serial_num: Word, script_root: Word, storage: Vec<Felt>) -> Recipient {
208    compute_and_store_recipient(serial_num, script_root, storage)
209}
210
211/// Computes the commitment to the provided note storage elements.
212///
213/// Panics if `storage` contains more than 1024 elements.
214pub fn compute_storage_commitment(storage: &[Felt]) -> Word {
215    assert!(
216        storage.len() <= MAX_NOTE_STORAGE_ITEMS,
217        "note storage cannot contain more than {MAX_NOTE_STORAGE_ITEMS} items"
218    );
219
220    let rust_ptr = if storage.is_empty() {
221        0
222    } else {
223        storage.as_ptr().addr() as u32
224    };
225    let miden_ptr = rust_ptr / 4;
226
227    assert_eq!(miden_ptr % 4, 0, "storage pointer must be word-aligned");
228
229    unsafe {
230        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<Word>::uninit());
231        extern_note_compute_storage_commitment(
232            miden_ptr as *const Felt,
233            storage.len(),
234            ret_area.as_mut_ptr(),
235        );
236        ret_area.into_inner().assume_init()
237    }
238}
239
240/// Writes attachment commitments from the advice map to memory.
241///
242/// The advice map must contain the preimage committed to by `attachments_commitment`.
243pub fn write_attachment_commitments_to_memory(attachments_commitment: Word) -> Vec<Word> {
244    let mut commitments: Vec<Word> = Vec::with_capacity(MAX_ATTACHMENTS_PER_NOTE);
245    let num_attachments = unsafe {
246        let ptr = (commitments.as_mut_ptr().addr() / 4) as u32;
247        extern_note_write_attachment_commitments_to_memory(
248            attachments_commitment[0],
249            attachments_commitment[1],
250            attachments_commitment[2],
251            attachments_commitment[3],
252            ptr as *mut Felt,
253        )
254    };
255    assert_attachment_count(num_attachments);
256    unsafe {
257        commitments.set_len(num_attachments);
258    }
259    commitments
260}
261
262/// Writes one attachment from the advice map to memory.
263///
264/// The advice map must contain the attachment elements committed to by `attachment_commitment`.
265pub fn write_attachment_to_memory(attachment_commitment: Word) -> Vec<Word> {
266    let mut attachment: Vec<Word> = Vec::with_capacity(MAX_ATTACHMENT_WORDS);
267    let num_words = unsafe {
268        let ptr = (attachment.as_mut_ptr().addr() / 4) as u32;
269        extern_note_write_attachment_to_memory(
270            attachment_commitment[0],
271            attachment_commitment[1],
272            attachment_commitment[2],
273            attachment_commitment[3],
274            ptr as *mut Felt,
275        )
276    };
277    assert_attachment_word_count(num_words);
278    unsafe {
279        attachment.set_len(num_words);
280    }
281    attachment
282}
283
284/// Writes the indexed attachment from an attachment commitment list to memory.
285///
286/// The advice map must contain the selected attachment elements.
287pub fn write_indexed_attachment_to_memory(
288    attachment_commitments: &[Word],
289    attachment_idx: u32,
290) -> Vec<Word> {
291    assert_attachment_count(attachment_commitments.len());
292
293    let mut attachment: Vec<Word> = Vec::with_capacity(MAX_ATTACHMENT_WORDS);
294    let num_words = unsafe {
295        let commitments_ptr = if attachment_commitments.is_empty() {
296            0
297        } else {
298            (attachment_commitments.as_ptr().addr() / 4) as u32
299        };
300        let dest_ptr = (attachment.as_mut_ptr().addr() / 4) as u32;
301        extern_note_write_indexed_attachment_to_memory(
302            Felt::from_u32(attachment_commitments.len() as u32),
303            commitments_ptr as *const Felt,
304            Felt::from_u32(attachment_idx),
305            dest_ptr as *mut Felt,
306        )
307    };
308    assert_attachment_word_count(num_words);
309    unsafe {
310        attachment.set_len(num_words);
311    }
312    attachment
313}
314
315/// Computes a note recipient from serial number, script root, and storage commitment.
316pub fn compute_recipient(
317    serial_num: Word,
318    script_root: Word,
319    storage_commitment: Word,
320) -> Recipient {
321    unsafe {
322        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<Recipient>::uninit());
323        extern_note_compute_recipient(
324            serial_num[0],
325            serial_num[1],
326            serial_num[2],
327            serial_num[3],
328            script_root[0],
329            script_root[1],
330            script_root[2],
331            script_root[3],
332            storage_commitment[0],
333            storage_commitment[1],
334            storage_commitment[2],
335            storage_commitment[3],
336            ret_area.as_mut_ptr(),
337        );
338        ret_area.into_inner().assume_init()
339    }
340}
341
342/// Extracts the sender account ID from a note metadata header word.
343pub fn metadata_into_sender(metadata: Word) -> AccountId {
344    unsafe {
345        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<RawAccountId>::uninit());
346        extern_note_metadata_into_sender(
347            metadata[0],
348            metadata[1],
349            metadata[2],
350            metadata[3],
351            ret_area.as_mut_ptr(),
352        );
353        ret_area.into_inner().assume_init().into_account_id()
354    }
355}
356
357/// Extracts the four attachment schemes encoded in a note metadata header word.
358pub fn metadata_into_attachment_schemes(metadata: Word) -> Word {
359    unsafe {
360        let mut ret_area = WordAligned::new(::core::mem::MaybeUninit::<Word>::uninit());
361        extern_note_metadata_into_attachment_schemes(
362            metadata[0],
363            metadata[1],
364            metadata[2],
365            metadata[3],
366            ret_area.as_mut_ptr(),
367        );
368        ret_area.into_inner().assume_init()
369    }
370}
371
372/// Extracts the note type encoded in a note metadata header word.
373pub fn metadata_into_note_type(metadata: Word) -> NoteType {
374    unsafe {
375        NoteType::from(extern_note_metadata_into_note_type(
376            metadata[0],
377            metadata[1],
378            metadata[2],
379            metadata[3],
380        ))
381    }
382}
383
384/// Extracts the note tag encoded in a note metadata header word.
385pub fn metadata_into_tag(metadata: Word) -> Tag {
386    unsafe {
387        Tag::from(extern_note_metadata_into_tag(
388            metadata[0],
389            metadata[1],
390            metadata[2],
391            metadata[3],
392        ))
393    }
394}
395
396/// Searches a metadata header word for `attachment_scheme`.
397pub fn find_attachment_idx(attachment_scheme: Felt, metadata: Word) -> Option<u32> {
398    unsafe {
399        let mut ret_area =
400            WordAligned::new(::core::mem::MaybeUninit::<RawAttachmentLocation>::uninit());
401        extern_note_find_attachment_idx(
402            attachment_scheme,
403            metadata[0],
404            metadata[1],
405            metadata[2],
406            metadata[3],
407            ret_area.as_mut_ptr(),
408        );
409        ret_area.into_inner().assume_init().into_attachment_index()
410    }
411}