Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4aa8e87
chore: reorder memory layout of output notes
PhilippGackstatter May 4, 2026
44719b3
chore: enforce total number of attachment words per note
PhilippGackstatter May 4, 2026
d8a5c09
feat: unify attachment word and array variants
PhilippGackstatter May 4, 2026
ec70314
chore: `new_word` -> `with_word` & `new_array` -> `with_words`
PhilippGackstatter May 4, 2026
9094c52
chore: update attachment docs in note.md
PhilippGackstatter May 4, 2026
ea91dbe
chore: refactor add_array_attachment to add_words_attachment
PhilippGackstatter May 5, 2026
8d48d1c
feat: create shared constants.masm for WORD_NUM_ELEMENTS
PhilippGackstatter May 5, 2026
1666221
chore: add test exceeding max attachment count
PhilippGackstatter May 5, 2026
09355b6
chore: make note header and metadata `Copy`
PhilippGackstatter May 5, 2026
f9cb55d
chore: move attachment tests to dedicated module
PhilippGackstatter May 5, 2026
fe46dde
feat: implement metadata_into_tag
PhilippGackstatter May 5, 2026
c629d84
chore: add dedicated error for total num words exceeded
PhilippGackstatter May 5, 2026
2dd982e
fix: OutputNote::Private docs
PhilippGackstatter May 5, 2026
12293c5
chore: add changelog
PhilippGackstatter May 5, 2026
6dd9c49
chore: validate returned data in test_get_attachment_ptr
PhilippGackstatter May 6, 2026
b54558b
Merge branch 'pgackst-multiple-note-attachments-getters' into pgackst…
PhilippGackstatter May 7, 2026
460db96
feat: support multiple attachments in send_note
PhilippGackstatter May 7, 2026
a0dfbd4
Merge branch 'pgackst-multiple-note-attachments-getters' into pgackst…
PhilippGackstatter May 7, 2026
ca50660
Merge branch 'pgackst-multiple-note-attachments-getters' into pgackst…
PhilippGackstatter May 7, 2026
326e708
chore: validate all attachments in test_get_attachment_ptr
PhilippGackstatter May 7, 2026
f0b063f
fix: attachment docs
PhilippGackstatter May 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
- [BREAKING] Renamed `native_asset_id` to `fee_faucet_id` ([#2718](https://github.com/0xMiden/protocol/pull/2718)).
- [BREAKING] Removed redundant outputs from kernel procedures: `note::write_assets_to_memory`, `active_note::get_assets`, `input_note::get_assets`, `output_note::get_assets`, `active_note::get_storage`, and `faucet::mint` no longer return values identical to their inputs ([#2523](https://github.com/0xMiden/protocol/issues/2523)).
- Added PSWAP (partial swap) note for decentralized partial-fill asset exchange with remainder note re-creation ([#2636](https://github.com/0xMiden/protocol/pull/2636)).
- [BREAKING] Add support for multiple attachments per note ([#2795](https://github.com/0xMiden/protocol/pull/2795), [#2849](https://github.com/0xMiden/protocol/pull/2849)):
- [BREAKING] Add support for multiple attachments per note ([#2795](https://github.com/0xMiden/protocol/pull/2795), [#2871](https://github.com/0xMiden/protocol/pull/2871)).
- [BREAKING] Renamed `set_attachment` to `add_attachment`, `set_word_attachment` to `add_word_attachment`, and `set_array_attachment` to `add_array_attachment` in `miden::protocol::output_note` ([#2795](https://github.com/0xMiden/protocol/pull/2795), [#2849](https://github.com/0xMiden/protocol/pull/2849)).
- [BREAKING] Replaced `metadata_into_attachment_info` with `metadata_into_attachment_schemes` in `miden::protocol::note` ([#2795](https://github.com/0xMiden/protocol/pull/2795), [#2849](https://github.com/0xMiden/protocol/pull/2849)).
- [BREAKING] All `get_metadata` procedures (`active_note`, `input_note`, `output_note`) no longer return attachments ([#2795](https://github.com/0xMiden/protocol/pull/2795), [#2849](https://github.com/0xMiden/protocol/pull/2849)).
- Added `metadata_into_tag` helper for extracting the tag from metadata. This should be used instead of extracting the tag manually from the header ([#2871](https://github.com/0xMiden/protocol/pull/2871)).

### Changes

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# CONSTANTS
# =================================================================================================

# The number of elements in a Word
pub const WORD_SIZE = 4
pub use $kernel::util::constants::WORD_NUM_ELEMENTS

# The maximum number of storage items associated with a single note.
pub const MAX_NOTE_STORAGE_ITEMS = 1024
Expand Down
49 changes: 37 additions & 12 deletions crates/miden-protocol/asm/kernels/transaction/lib/memory.masm
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use $kernel::constants::ACCOUNT_PROCEDURE_DATA_LENGTH
use $kernel::constants::MAX_ASSETS_PER_NOTE
use $kernel::constants::NOTE_MEM_SIZE
use $kernel::constants::WORD_SIZE
use $kernel::constants::WORD_NUM_ELEMENTS
# use $kernel::types::AccountId
use miden::core::mem

Expand Down Expand Up @@ -270,16 +270,17 @@ const OUTPUT_NOTE_SECTION_OFFSET=16777216
# The offsets at which data of an output note is stored relative to the start of its data segment.
const OUTPUT_NOTE_ID_OFFSET=0
const OUTPUT_NOTE_METADATA_HEADER_OFFSET=4
const OUTPUT_NOTE_NUM_ATTACHMENTS_OFFSET=8
const OUTPUT_NOTE_ATTACHMENT_0_OFFSET=12
const OUTPUT_NOTE_ATTACHMENT_1_OFFSET=16
const OUTPUT_NOTE_ATTACHMENT_2_OFFSET=20
const OUTPUT_NOTE_ATTACHMENT_3_OFFSET=24
const OUTPUT_NOTE_RECIPIENT_OFFSET=28
const OUTPUT_NOTE_RECIPIENT_OFFSET=8
const OUTPUT_NOTE_DIRTY_FLAG_OFFSET=12
const OUTPUT_NOTE_NUM_ASSETS_OFFSET=13
const OUTPUT_NOTE_NUM_ATTACHMENTS_OFFSET=14
const OUTPUT_NOTE_TOTAL_ATTACHMENT_WORDS_OFFSET=15
const OUTPUT_NOTE_ATTACHMENT_0_OFFSET=16
const OUTPUT_NOTE_ATTACHMENT_1_OFFSET=20
const OUTPUT_NOTE_ATTACHMENT_2_OFFSET=24
const OUTPUT_NOTE_ATTACHMENT_3_OFFSET=28
const OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET=32
const OUTPUT_NOTE_NUM_ASSETS_OFFSET=36
const OUTPUT_NOTE_DIRTY_FLAG_OFFSET=37
const OUTPUT_NOTE_ASSETS_OFFSET=40
const OUTPUT_NOTE_ASSETS_OFFSET=36

# LINK MAP MEMORY
# -------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -1884,7 +1885,7 @@ pub proc get_output_note_attachment_commitment
add.OUTPUT_NOTE_ATTACHMENT_0_OFFSET
# => [note_ptr + attachment_0_offset, attachment_idx]

swap mul.WORD_SIZE add
swap mul.WORD_NUM_ELEMENTS add
# => [attachment_ptr]

padw movup.4 mem_loadw_le
Expand Down Expand Up @@ -1916,7 +1917,7 @@ pub proc set_output_note_attachment_commitment
add.OUTPUT_NOTE_ATTACHMENT_0_OFFSET
# => [note_ptr + base_offset, attachment_idx, ATTACHMENT_COMMITMENT]

swap mul.WORD_SIZE add
swap mul.WORD_NUM_ELEMENTS add
# => [attachment_ptr, ATTACHMENT_COMMITMENT]

mem_storew_le dropw
Expand Down Expand Up @@ -1947,6 +1948,30 @@ pub proc set_output_note_num_attachments
add.OUTPUT_NOTE_NUM_ATTACHMENTS_OFFSET mem_store
end

#! Returns the total number of attachment words for the output note.
#!
#! Inputs: [note_ptr]
#! Outputs: [total_num_attachment_words]
#!
#! Where:
#! - note_ptr is the memory address at which the output note data begins.
#! - total_num_attachment_words is the total number of words across all attachments.
pub proc get_output_note_total_attachment_words
add.OUTPUT_NOTE_TOTAL_ATTACHMENT_WORDS_OFFSET mem_load
end

#! Sets the total number of attachment words for the output note.
#!
#! Inputs: [note_ptr, total_num_attachment_words]
#! Outputs: []
#!
#! Where:
#! - note_ptr is the memory address at which the output note data begins.
#! - total_num_attachment_words is the total number of words across all attachments.
pub proc set_output_note_total_attachment_words
add.OUTPUT_NOTE_TOTAL_ATTACHMENT_WORDS_OFFSET mem_store
end

#! Returns the number of assets in the output note.
#!
#! Inputs: [note_ptr]
Expand Down
7 changes: 4 additions & 3 deletions crates/miden-protocol/asm/kernels/transaction/lib/note.masm
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ use miden::core::crypto::hashes::poseidon2

use $kernel::asset::ASSET_SIZE
use $kernel::constants::NOTE_MEM_SIZE
use $kernel::constants::WORD_SIZE
use $kernel::constants::WORD_NUM_ELEMENTS
use $kernel::memory

pub use $kernel::util::note::NOTE_TYPE_PUBLIC
pub use $kernel::util::note::NOTE_TYPE_PRIVATE
pub use $kernel::util::note::MAX_ATTACHMENT_SCHEME
pub use $kernel::util::note::MAX_ATTACHMENT_WORDS
pub use $kernel::util::note::MAX_ATTACHMENT_TOTAL_WORDS
pub use $kernel::util::note::ATTACHMENT_SCHEME_NONE

# ERRORS
Expand Down Expand Up @@ -102,11 +103,11 @@ proc compute_attachments_commitment
dup exec.memory::get_output_note_num_attachments
# => [num_attachments, note_ptr]

# end_ptr = attachment_data_ptr + num_attachments * WORD_SIZE
# end_ptr = attachment_data_ptr + num_attachments * WORD_NUM_ELEMENTS
swap exec.memory::get_output_note_attachment_commitment_ptr
# => [start_ptr, num_attachments]

swap mul.WORD_SIZE dup.1 add swap
swap mul.WORD_NUM_ELEMENTS dup.1 add swap
# => [start_ptr, end_ptr]

exec.poseidon2::hash_words
Expand Down
46 changes: 34 additions & 12 deletions crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ use $kernel::note
use $kernel::note::NOTE_TYPE_PUBLIC
use $kernel::note::MAX_ATTACHMENT_SCHEME
use $kernel::note::MAX_ATTACHMENT_WORDS
use $kernel::note::MAX_ATTACHMENT_TOTAL_WORDS
use $kernel::constants::MAX_OUTPUT_NOTES_PER_TX
use $kernel::constants::WORD_SIZE
use $kernel::constants::WORD_NUM_ELEMENTS
use $kernel::asset::ASSET_SIZE
use $kernel::asset::ASSET_VALUE_MEMORY_OFFSET
use miden::core::word
Expand Down Expand Up @@ -49,6 +50,8 @@ const ERR_OUTPUT_NOTE_ATTACHMENT_SIZE_CANNOT_BE_ZERO="attachment num_words canno

const ERR_OUTPUT_NOTE_TOO_MANY_ATTACHMENTS="number of attachments per note cannot exceed 4"

const ERR_OUTPUT_NOTE_TOTAL_ATTACHMENT_WORDS_EXCEEDED="total number of attachment words per note cannot exceed 512"

const ERR_NOTE_FUNGIBLE_MAX_AMOUNT_EXCEEDED="adding a fungible asset to a note cannot exceed the max_amount of 9223372036854775807"

const ERR_OUTPUT_NOTE_ATTACHMENT_COMMITMENT_MISMATCH="the computed hash of fetched attachment elements does not match the provided commitment"
Expand Down Expand Up @@ -200,7 +203,7 @@ pub proc get_attachments_commitment
# => [start_ptr, num_attachments, note_ptr]

# compute start_ptr and end_ptr for the attachment commitments in memory
dup movup.2 mul.WORD_SIZE add swap
dup movup.2 mul.WORD_NUM_ELEMENTS add swap
# => [start_ptr, end_ptr, note_ptr]

movup.2 exec.note::compute_attachments_commitment
Expand Down Expand Up @@ -309,13 +312,26 @@ pub proc add_attachment

# validate preimage for commitment is available and number of committed words is within limits
dupw exec.validate_attachment
# => [ATTACHMENT_COMMITMENT, attachment_scheme, note_idx]
# => [num_words, ATTACHMENT_COMMITMENT, attachment_scheme, note_idx]

movup.4
# => [attachment_scheme, ATTACHMENT_COMMITMENT, note_idx]
movup.5
# => [attachment_scheme, num_words, ATTACHMENT_COMMITMENT, note_idx]

# get note_ptr from note_idx
movup.5 exec.memory::get_output_note_ptr
movup.6 exec.memory::get_output_note_ptr
# => [note_ptr, attachment_scheme, num_words, ATTACHMENT_COMMITMENT]

# update and validate total attachment words
dup exec.memory::get_output_note_total_attachment_words
# => [total_num_words, note_ptr, attachment_scheme, num_words, ATTACHMENT_COMMITMENT]

movup.3 add
# => [new_total_num_words, note_ptr, attachment_scheme, ATTACHMENT_COMMITMENT]

dup u32lte.MAX_ATTACHMENT_TOTAL_WORDS assert.err=ERR_OUTPUT_NOTE_TOTAL_ATTACHMENT_WORDS_EXCEEDED
# => [new_total_num_words, note_ptr, attachment_scheme, ATTACHMENT_COMMITMENT]

dup.1 exec.memory::set_output_note_total_attachment_words
# => [note_ptr, attachment_scheme, ATTACHMENT_COMMITMENT]

# validate current number of attachments < 4
Expand Down Expand Up @@ -381,7 +397,7 @@ end
#! Advice map: {
#! ATTACHMENT_COMMITMENT: [[ATTACHMENT_ELEMENTS]],
#! }
#! Outputs: []
#! Outputs: [num_words]
#!
#! Panics if:
#! - the number of elements is not a multiple of 4, or num_words is zero or exceeds 256.
Expand All @@ -394,11 +410,11 @@ proc validate_attachment

# derive num_words from num_elements
adv_push.1 u32assert.err=ERR_OUTPUT_NOTE_ATTACHMENT_SIZE_MAX_EXCEEDED
u32divmod.WORD_SIZE
u32divmod.WORD_NUM_ELEMENTS
# OS => [remainder, num_words, ATTACHMENT_COMMITMENT]
# AS => [[ATTACHMENT_ELEMENTS]]

# assert the number of elements is a multiple of WORD_SIZE
# assert the number of elements is a multiple of WORD_NUM_ELEMENTS
eq.0 assert.err=ERR_OUTPUT_NOTE_ATTACHMENT_SIZE_MUST_BE_MULTIPLE_OF_WORD_SIZE
# OS => [num_words, ATTACHMENT_COMMITMENT]
# AS => [[ATTACHMENT_ELEMENTS]]
Expand All @@ -413,13 +429,19 @@ proc validate_attachment

# we use scratch memory because pipe_preimage_to_memory needs to write to memory, but we only
# need to assert that the preimage is available, the exact content itself is unimportant

# save num_words to return it later
dup movdn.5
# OS => [num_words, ATTACHMENT_COMMITMENT, num_words]
# AS => [[ATTACHMENT_ELEMENTS]]

push.KERNEL_SCRATCH_PTR swap
# OS => [num_words, scratch_ptr, ATTACHMENT_COMMITMENT]
# OS => [num_words, scratch_ptr, ATTACHMENT_COMMITMENT, num_words]
# AS => [[ATTACHMENT_ELEMENTS]]

# validate the sequential hash over the attachment elements is ATTACHMENT_COMMITMENT
exec.mem::pipe_preimage_to_memory drop
# OS => []
# OS => [num_words]
end

#! Builds the provided inputs into the NOTE_METADATA_HEADER word.
Expand Down Expand Up @@ -481,7 +503,7 @@ end
#!
#! Where:
#! - num_attachments is the number of attachments the note had before this attachment was added.
#! - attachment_scheme is the user-defined type of the attachment.
#! - attachment_scheme is the user-defined scheme of the attachment.
#! - note_ptr is the memory address at which the output note data begins.
proc set_attachment_schemes
# the schemes are stored as follows in the third felt:
Expand Down
2 changes: 1 addition & 1 deletion crates/miden-protocol/asm/protocol/active_note.masm
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ end
#! Writes the attachment commitments of the active note with the specified index to memory and
#! returns the pointer to the data.
#!
#! This procedure allocates 16 elements of memory (max num attachments * WORD_SIZE) to store all
#! This procedure allocates 16 elements of memory (max num attachments * WORD_NUM_ELEMENTS) to store all
#! potential four attachment commitments.
#!
#! Inputs: []
Expand Down
2 changes: 1 addition & 1 deletion crates/miden-protocol/asm/protocol/input_note.masm
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ end
#! Writes the attachment commitments of the input note with the specified index to memory and
#! returns the pointer to the data.
#!
#! This procedure allocates 16 elements of memory (max num attachments * WORD_SIZE) to store all
#! This procedure allocates 16 elements of memory (max num attachments * WORD_NUM_ELEMENTS) to store all
#! potential four attachment commitments.
#!
#! Inputs: [note_index]
Expand Down
39 changes: 28 additions & 11 deletions crates/miden-protocol/asm/protocol/note.masm
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use miden::protocol::account_id
use miden::protocol::util::constants::WORD_NUM_ELEMENTS
use miden::core::crypto::hashes::poseidon2
use miden::core::mem

Expand All @@ -8,6 +9,7 @@ pub use miden::protocol::util::note::NOTE_TYPE_PUBLIC
pub use miden::protocol::util::note::NOTE_TYPE_PRIVATE
pub use miden::protocol::util::note::MAX_ATTACHMENT_SCHEME
pub use miden::protocol::util::note::MAX_ATTACHMENT_WORDS
pub use miden::protocol::util::note::MAX_ATTACHMENT_TOTAL_WORDS
pub use miden::protocol::util::note::ATTACHMENT_SCHEME_NONE

# ERRORS
Expand All @@ -17,12 +19,6 @@ const ERR_PROLOGUE_NOTE_NUM_STORAGE_ITEMS_EXCEEDED_LIMIT="number of note storage

const ERR_OUTPUT_NOTE_ATTACHMENT_IDX_OUT_OF_BOUNDS = "attachment index out of bounds"

# CONSTANTS
# =================================================================================================

#! The number of elements in a word.
const WORD_SIZE = 4

# NOTE UTILITY PROCEDURES
# =================================================================================================

Expand Down Expand Up @@ -108,7 +104,7 @@ pub proc write_attachment_commitments_to_memory
# pipe_preimage_to_memory so we assume validity and only do a basic u32 assertion to protect
# against invalid advice inputs.
adv_push.1 u32assert.err="invalid attachment num_elements advice input"
u32div.WORD_SIZE
u32div.WORD_NUM_ELEMENTS
# OS => [num_words, ATTACHMENTS_COMMITMENT, dest_ptr]
# AS => [[ATTACHMENT_COMMITMENT]]

Expand Down Expand Up @@ -147,7 +143,7 @@ pub proc write_attachment_to_memory
# pipe_preimage_to_memory so we assume validity and only do a basic u32 assertion to protect
# against invalid advice inputs.
adv_push.1 u32assert.err="invalid attachment num_elements advice input"
u32div.WORD_SIZE
u32div.WORD_NUM_ELEMENTS
# OS => [num_words, ATTACHMENT_COMMITMENT, dest_ptr]
# AS => [[ATTACHMENT_ELEMENTS]]

Expand All @@ -163,7 +159,7 @@ end
#! Writes the attachment with the provided index from to memory and returns the pointer to the
#! attachment elements.
#!
#! This procedure allocates 1024 elements of memory (MAX_ATTACHMENT_WORDS * WORD_SIZE) to store
#! This procedure allocates 1024 elements of memory (MAX_ATTACHMENT_WORDS * WORD_NUM_ELEMENTS) to store
#! all potential attachment elements.
#!
#! Inputs: [num_attachments, attachment_commitments_ptr, attachment_idx]
Expand All @@ -190,8 +186,8 @@ pub proc get_attachment_ptr
# => [attachment_commitments_ptr, attachment_idx]

# compute the memory address of the attachment commitment:
# commitment_ptr = attachment_commitments_ptr + attachment_idx * WORD_SIZE
swap mul.WORD_SIZE add
# commitment_ptr = attachment_commitments_ptr + attachment_idx * WORD_NUM_ELEMENTS
swap mul.WORD_NUM_ELEMENTS add
# => [commitment_ptr]

# load the ATTACHMENT_COMMITMENT from memory
Expand Down Expand Up @@ -387,6 +383,27 @@ pub proc metadata_into_note_type
# => [note_type]
end

#! Extracts the tag from the provided metadata header.
#!
#! The tag is stored in the lower 32 bits of the tag element. The upper 32 bits are reserved.
#!
#! Inputs: [METADATA_HEADER]
#! Outputs: [tag]
#!
#! Where:
#! - METADATA_HEADER is the metadata header word of a note.
#! - tag is the lower 32 bits of the tag element.
#!
#! Invocation: exec
pub proc metadata_into_tag
drop drop swap drop
# => [tag_element]

# extract the lower 32 bits as the tag
u32split swap drop
# => [tag]
end

#! Searches the metadata header for the specified attachment scheme and returns the index of the
#! first matching slot.
#!
Expand Down
Loading
Loading