@@ -3,6 +3,7 @@ use $kernel::memory
33use $kernel::note
44use $kernel::asset
55use $kernel::constants::MAX_OUTPUT_NOTES_PER_TX
6+ use miden::core::mem
67use miden::core::word
78
89# CONSTANTS
@@ -23,6 +24,9 @@ const ATTACHMENT_CONTENT_TYPE_ARRAY=2
2324# "untyped".
2425const ATTACHMENT_DEFAULT_TYPE_INFO=0
2526
27+ #! The default attachment type, representing an untyped attachment.
28+ const ATTACHMENT_TYPE_UNTYPED=0
29+
2630# ERRORS
2731# =================================================================================================
2832
@@ -32,6 +36,14 @@ const ERR_NOTE_INVALID_TYPE="invalid note type"
3236
3337const ERR_OUTPUT_NOTE_INDEX_OUT_OF_BOUNDS="requested output note index should be less than the total number of created output notes"
3438
39+ const ERR_OUTPUT_NOTE_INVALID_ATTACHMENT_TYPES="attachment types must fit into u32s"
40+
41+ const ERR_OUTPUT_NOTE_UNKNOWN_ATTACHMENT_CONTENT_TYPE="attachment content type variant must be between 0 and 2"
42+
43+ const ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_HAVE_UNTYPED_ATTACHMENT_TYPE="attachment type of content type none must be 0"
44+
45+ const ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_BE_EMPTY_WORD="attachment of content type none must be set to an empty word"
46+
3547const ERR_NOTE_INVALID_INDEX="failed to find note at the given index; index must be within [0, num_of_notes]"
3648
3749const ERR_NOTE_FUNGIBLE_MAX_AMOUNT_EXCEEDED="adding a fungible asset to a note cannot exceed the max_amount of 9223372036854775807"
@@ -53,6 +65,9 @@ const NOTE_BEFORE_ADD_ASSET_EVENT=event("miden::note::before_add_asset")
5365# Event emitted after an ASSET is added to a note
5466const NOTE_AFTER_ADD_ASSET_EVENT=event("miden::note::after_add_asset")
5567
68+ # Event emitted before an ATTACHMENT is added to a note
69+ const NOTE_BEFORE_SET_ATTACHMENT_EVENT=event("miden::note::before_set_attachment")
70+
5671# OUTPUT NOTE PROCEDURES
5772# =================================================================================================
5873
185200#! - ASSET can be a fungible or non-fungible asset.
186201#!
187202#! Panics if:
203+ #! - the note index points to a non-existent output note.
188204#! - the ASSET is malformed (e.g., invalid faucet ID).
189205#! - the max amount of fungible assets is exceeded.
190206#! - the non-fungible asset already exists in the note.
@@ -239,6 +255,51 @@ pub proc add_asset
239255 # => []
240256end
241257
258+ #! Sets the attachment of the note specified by the index.
259+ #!
260+ #! Inputs: [note_idx, attachment_content_type, attachment_type, ATTACHMENT]
261+ #! Outputs: []
262+ #!
263+ #! Where:
264+ #! - note_idx is the index of the note on which the attachment is set.
265+ #! - attachment_content_type is the content type of the attachment.
266+ #! - attachment_type is the user-defined type of the attachment.
267+ #! - ATTACHMENT is the attachment to be set.
268+ #!
269+ #! Panics if:
270+ #! - the note index points to a non-existent output note.
271+ #! - any of the attachment types does not fit into a u32.
272+ #! - the attachment content type is an unknown variant.
273+ pub proc set_attachment
274+ dup exec.memory::get_num_output_notes lte assert.err=ERR_NOTE_INVALID_INDEX
275+ # => [note_idx, attachment_content_type, attachment_type, ATTACHMENT]
276+
277+ exec.memory::get_output_note_ptr dup
278+ # => [note_ptr, note_ptr, attachment_content_type, attachment_type, ATTACHMENT]
279+
280+ dupw.1
281+ # => [ATTACHMENT, note_ptr, note_ptr, attachment_content_type, attachment_type, ATTACHMENT]
282+
283+ dup.7 dup.7
284+ # => [attachment_content_type, attachment_type, ATTACHMENT, note_ptr, note_ptr,
285+ # attachment_content_type, attachment_type, ATTACHMENT]
286+
287+ exec.validate_attachment
288+ # => [note_ptr, note_ptr, attachment_content_type, attachment_type, ATTACHMENT]
289+
290+ movdn.3 movdn.3
291+ # => [attachment_content_type, attachment_type, note_ptr, note_ptr, ATTACHMENT]
292+
293+ emit.NOTE_BEFORE_SET_ATTACHMENT_EVENT
294+ # => [attachment_content_type, attachment_type, note_ptr, note_ptr, ATTACHMENT]
295+
296+ exec.set_attachment_type_info
297+ # => [note_ptr, ATTACHMENT]
298+
299+ exec.memory::set_output_note_attachment
300+ # => []
301+ end
302+
242303#! Assert that the provided note index is less than the total number of output notes.
243304#!
244305#! Inputs: [note_index]
@@ -306,6 +367,89 @@ pub proc build_metadata_header
306367 # => [NOTE_METADATA_HEADER]
307368end
308369
370+ #! Validate the ATTACHMENT against the content type.
371+ #!
372+ #! Inputs: [attachment_content_type, attachment_type, ATTACHMENT]
373+ #! Outputs: []
374+ #!
375+ #! Where:
376+ #! - attachment_type is the user-defined type of the attachment.
377+ #! - attachment_content_type is the content type of the attachment.
378+ #! - ATTACHMENT is the attachment to validate.
379+ #!
380+ #! Panics if:
381+ #! - any of the attachment types does not fit into a u32.
382+ #! - the attachment content type is an unknown variant.
383+ #! - the content type is None and the ATTACHMENT is not an empty word.
384+ proc validate_attachment
385+ u32assert2.err=ERR_OUTPUT_NOTE_INVALID_ATTACHMENT_TYPES
386+ # => [attachment_content_type, attachment_type, ATTACHMENT]
387+
388+ # assert that the attachment content type is valid
389+ dup u32lte.ATTACHMENT_CONTENT_TYPE_ARRAY
390+ assert.err=ERR_OUTPUT_NOTE_UNKNOWN_ATTACHMENT_CONTENT_TYPE
391+ # => [attachment_content_type, attachment_type, ATTACHMENT]
392+
393+ eq.ATTACHMENT_CONTENT_TYPE_NONE
394+ # => [is_attachment_none, attachment_type, ATTACHMENT]
395+
396+ if.true
397+ eq.ATTACHMENT_TYPE_UNTYPED
398+ assert.err=ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_HAVE_UNTYPED_ATTACHMENT_TYPE
399+ # => [ATTACHMENT]
400+
401+ padw assert_eqw.err=ERR_OUTPUT_NOTE_ATTACHMENT_NONE_MUST_BE_EMPTY_WORD
402+ # => []
403+ else
404+ drop dropw
405+ # => []
406+ end
407+ # => []
408+ end
409+
410+ #! Sets an output note's attachment type info in the metadata header.
411+ #!
412+ #! WARNING: The attachment types must be valid.
413+ #!
414+ #! Inputs: [attachment_content_type, attachment_type, note_ptr]
415+ #! Outputs: []
416+ #!
417+ #! Where:
418+ #! - attachment_content_type is the content type of the attachment.
419+ #! - attachment_type is the user-defined type of the attachment.
420+ #! - note_ptr is the memory address at which the output note data begins.
421+ proc set_attachment_type_info
422+ exec.merge_attachment_type_info
423+ # => [attachment_type_info, note_ptr]
424+
425+ swap
426+ # => [note_ptr, attachment_type_info]
427+
428+ exec.memory::set_output_note_attachment_type_info
429+ # => []
430+ end
431+
432+ #! Merges the attachment types into a single felt with the following layout:
433+ #!
434+ #! [30 zero bits | attachment_content_type (2 bits) | attachment_type (32 bits)]
435+ #!
436+ #! WARNING: The attachment types must be valid.
437+ #!
438+ #! Inputs: [attachment_content_type, attachment_type]
439+ #! Outputs: [attachment_type_info]
440+ #!
441+ #! Where:
442+ #! - attachment_content_type is the content type of the attachment.
443+ #! - attachment_type is the user-defined type of the attachment.
444+ #! - attachment_type_info is the felt constructed from the inputs.
445+ proc merge_attachment_type_info
446+ # shift the content type 32 bits to the left, which is the same as multiplying by 2^32
447+ # and set the lower bits to the attachment_type, which is done by adding the values together
448+ mul.0x100000000
449+ add
450+ # => [attachment_type_info]
451+ end
452+
309453#! Increments the number of output notes by one. Returns the index of the next note to be created.
310454#!
311455#! Inputs: []
0 commit comments