Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c48d582
feat: add get_attachments_commitment kernel API
PhilippGackstatter Apr 28, 2026
3a97ed8
chore: add test for get_attachments_commitemnt API
PhilippGackstatter Apr 28, 2026
e504b7c
feat: add get_attachment API
PhilippGackstatter Apr 28, 2026
90fbee9
chore: implement metadata_into_attachment_schemes
PhilippGackstatter Apr 29, 2026
d662faa
feat: implement find_attachment_idx
PhilippGackstatter Apr 29, 2026
6fd3c64
feat: implement output_note::find_attachment
PhilippGackstatter Apr 29, 2026
d9b2d73
chore: add docs for num allocated locals
PhilippGackstatter Apr 29, 2026
bee88a5
feat: implement attachment APIs for input/active note
PhilippGackstatter Apr 29, 2026
7cfde4a
fix: construct input notes in tx context in provided order
PhilippGackstatter Apr 29, 2026
3b0cad1
chore: adapt network account target MASM to multiple attachments
PhilippGackstatter Apr 29, 2026
47374b0
chore: remove outdated metadata_into_attachment_header
PhilippGackstatter Apr 29, 2026
58946f9
chore: remove outdated attachment param from input_note_get_metadata
PhilippGackstatter Apr 29, 2026
40c7cda
chore: remove outdated attachment param from output_note_get_metadata
PhilippGackstatter Apr 29, 2026
75c9791
chore: update protocol library docs
PhilippGackstatter Apr 29, 2026
4ad8e60
fixup! feat: implement attachment APIs for input/active note
PhilippGackstatter Apr 29, 2026
7605e21
fixup! feat: implement attachment APIs for input/active note
PhilippGackstatter Apr 29, 2026
15df409
fixup! chore: implement metadata_into_attachment_schemes
PhilippGackstatter Apr 29, 2026
5b158be
chore: update safety comments in attachment piping procs
PhilippGackstatter Apr 29, 2026
0d65ba6
chore: add changelog
PhilippGackstatter Apr 29, 2026
bf0a15c
Merge remote-tracking branch 'origin/pgackst-multiple-note-attachment…
PhilippGackstatter May 4, 2026
853aeac
Merge branch 'pgackst-multiple-note-attachments' into pgackst-multipl…
PhilippGackstatter May 5, 2026
a093a3b
chore: add `_ptr` suffix to get_attachment* APIs
PhilippGackstatter May 5, 2026
1254f82
fix: make format
PhilippGackstatter May 5, 2026
60bd303
chore: rename to write_attachment_commitments_to_memory
PhilippGackstatter May 6, 2026
a573966
Merge branch 'pgackst-multiple-note-attachments' into pgackst-multipl…
PhilippGackstatter May 7, 2026
e331b80
Merge branch 'pgackst-multiple-note-attachments' into pgackst-multipl…
PhilippGackstatter May 7, 2026
262ee14
Merge branch 'pgackst-multiple-note-attachments' into pgackst-multipl…
PhilippGackstatter May 7, 2026
d7051e1
fix: remove unused, outdated line
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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
- [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)).
- [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] 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)).

### Changes

Expand Down
127 changes: 79 additions & 48 deletions crates/miden-protocol/asm/kernels/transaction/api.masm
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use $kernel::input_note
use $kernel::memory
use $kernel::output_note
use $kernel::tx
use $kernel::constants::WORD_SIZE

use $kernel::memory::UPCOMING_FOREIGN_PROCEDURE_PTR
use $kernel::memory::UPCOMING_FOREIGN_PROC_INPUT_VALUE_15_PTR
Expand Down Expand Up @@ -43,6 +42,8 @@ const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SCRIPT_ROOT_WHILE_NO_NOTE_BEING_PROCESSED=

const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_SERIAL_NUMBER_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note serial number of active note because no note is currently being processed"

const ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_ATTACHMENTS_WHILE_NO_NOTE_BEING_PROCESSED="failed to access note attachments of active note because no note is currently being processed"

const ERR_FOREIGN_ACCOUNT_PROCEDURE_ROOT_IS_ZERO="root of the provided foreign procedure equals zero indicating that tx_prepare_fpi was not called"

const ERR_FOREIGN_ACCOUNT_ID_IS_ZERO="ID of the provided foreign account equals zero indicating that tx_prepare_fpi was not called"
Expand Down Expand Up @@ -912,15 +913,14 @@ end
#! Returns the metadata of the specified input note.
#!
#! Inputs: [is_active_note, note_index, pad(14)]
#! Outputs: [NOTE_ATTACHMENT_0, METADATA_HEADER, pad(8)]
#! Outputs: [METADATA_HEADER, pad(12)]
#!
#! Where:
#! - is_active_note is the boolean flag indicating whether we should return the metadata from
#! the active note or from the note with the specified index.
#! - note_index is the index of the input note whose metadata should be returned. Notice that if
#! is_active_note is 1, note_index is ignored.
#! - METADATA_HEADER is the metadata header of the specified input note.
#! - NOTE_ATTACHMENT_0 is the first attachment of the specified input note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of input notes.
Expand All @@ -944,38 +944,12 @@ pub proc input_note_get_metadata
# => [input_note_ptr, pad(16)]

# get the metadata
dup exec.memory::get_input_note_metadata_header
# => [METADATA_HEADER, input_note_ptr, pad(16)]

# get the attachment
movup.4 exec.memory::get_input_note_attachments_commitment
# => [NOTE_ATTACHMENTS_COMMITMENT, METADATA_HEADER, pad(16)]
exec.memory::get_input_note_metadata_header
# => [METADATA_HEADER, pad(16)]

# truncate the stack
swapdw dropw dropw
# => [NOTE_ATTACHMENTS_COMMITMENT, METADATA_HEADER, pad(8)]

# TODO(multi_attachments): Maintain temporary compatibility with the previous API by returning
# the first attachment. This will be refactored in a follow-up PR.
exec.word::testz not
# => [!is_attachments_commitment_empty, NOTE_ATTACHMENTS_COMMITMENT, METADATA_HEADER, pad(8)]

# if the attachments commitment is the empty word, the first attachment is also the empty word,
# so we leave the empty word on the stack
#
# otherwise:
if.true
# fetch the first attachment from the advice stack and overwrite the attachments commitment
adv.push_mapval
adv_loadw
# => [ATTACHMENT_COMMITMENT_0, METADATA_HEADER, pad(8)]

adv.push_mapvaln
adv_push.1 eq.WORD_SIZE assert.err="retrieved attachments must be temporarily word-sized"
adv_loadw
# => [ATTACHMENT_0, METADATA_HEADER, pad(8)]
end
# => [ATTACHMENT_0, METADATA_HEADER, pad(8)]
swapw dropw
# => [METADATA_HEADER, pad(12)]
end

#! Returns the serial number of the specified input note.
Expand Down Expand Up @@ -1103,6 +1077,46 @@ pub proc input_note_get_script_root
# => [SCRIPT_ROOT, pad(12)]
end

#! Returns the attachments commitment of the specified input note.
#!
#! Inputs: [is_active_note, note_index, pad(14)]
#! Outputs: [ATTACHMENTS_COMMITMENT, pad(12)]
#!
#! Where:
#! - is_active_note is the boolean flag indicating whether we should return the attachments
#! commitment from the active note or from the note with the specified index.
#! - note_index is the index of the input note whose attachments commitment should be returned.
#! Notice that if is_active_note is 1, note_index is ignored.
#! - ATTACHMENTS_COMMITMENT is the commitment to all attachments of the specified input note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of input notes.
#! - is_active_note is 1 and no input note is not being processed (attempted to access note
#! attachments from incorrect context).
#!
#! Invocation: dynexec
pub proc input_note_get_attachments_commitment
# get the input note pointer depending on whether the requested note is current or it was
# requested by index.
exec.get_requested_note_ptr
# => [input_note_ptr, pad(15)]

# assert the pointer is not zero - this would suggest the procedure has been called from an
# incorrect context
Comment thread
mmagician marked this conversation as resolved.
dup neq.0 assert.err=ERR_NOTE_ATTEMPT_TO_ACCESS_NOTE_ATTACHMENTS_WHILE_NO_NOTE_BEING_PROCESSED
# => [input_note_ptr, pad(15)]

# get the attachments commitment
exec.memory::get_input_note_attachments_commitment
# => [ATTACHMENTS_COMMITMENT, pad(15)]

# truncate the stack
repeat.3
movup.4 drop
end
# => [ATTACHMENTS_COMMITMENT, pad(12)]
end

# OUTPUT NOTE
# -------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -1215,6 +1229,33 @@ pub proc output_note_get_assets_info
# => [ASSETS_COMMITMENT, num_assets, pad(11)]
end

#! Returns the commitment over all attachments.
#!
#! Inputs: [note_index, pad(15)]
#! Outputs: [ATTACHMENTS_COMMITMENT, pad(12)]
#!
#! Where:
#! - note_index is the index of the output note whose attachments commitment should be returned.
#! - ATTACHMENTS_COMMITMENT is the commitment to all attachments of the note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of output notes.
#!
#! Invocation: dynexec
pub proc output_note_get_attachments_commitment
# assert that the provided note index is less than the total number of output notes
exec.output_note::assert_note_index_in_bounds
# => [note_index, pad(15)]

# get the attachments commitment
exec.output_note::get_attachments_commitment
# => [ATTACHMENTS_COMMITMENT, pad(16)]

# truncate the stack
swapw dropw
# => [ATTACHMENTS_COMMITMENT, pad(12)]
end

#! Returns the recipient of the output note with the specified index.
#!
#! Inputs: [note_index, pad(15)]
Expand Down Expand Up @@ -1249,12 +1290,11 @@ end
#! Returns the metadata of the output note with the specified index.
#!
#! Inputs: [note_index, pad(15)]
#! Outputs: [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)]
#! Outputs: [METADATA_HEADER, pad(12)]
#!
#! Where:
#! - note_index is the index of the output note whose metadata should be returned.
#! - METADATA_HEADER is the metadata header of the specified output note.
#! - NOTE_ATTACHMENT is the attachment of the specified output note.
#!
#! Panics if:
#! - the note index is greater or equal to the total number of output notes.
Expand All @@ -1269,22 +1309,13 @@ pub proc output_note_get_metadata
exec.memory::get_output_note_ptr
# => [note_ptr, pad(15)]

# make stack truncation at the end of the procedure easier
push.0 swap
# => [note_ptr, pad(16)]

# get the metadata
dup exec.memory::get_output_note_metadata_header
# => [METADATA_HEADER, note_ptr, pad(16)]

# TODO(multi_attachments): Temporarily maintain compatibility with the old API and return the
# first attachment.
movup.4 push.0 swap exec.memory::get_output_note_attachment_commitment
# => [ATTACHMENT_0, METADATA_HEADER, pad(16)]
exec.memory::get_output_note_metadata_header
# => [METADATA_HEADER, pad(15)]

# truncate the stack
swapdw dropw dropw
# => [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)]
swapw drop drop drop movdn.4
# => [METADATA_HEADER, pad(12)]
Comment thread
mmagician marked this conversation as resolved.
end

# TRANSACTION
Expand Down
35 changes: 35 additions & 0 deletions crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,41 @@ pub proc get_assets_info
# => [ASSETS_COMMITMENT, num_assets]
end

#! Returns the commitment over all attachments in the note with the provided index.
#!
#! Inputs: [note_index]
#! Outputs: [ATTACHMENTS_COMMITMENT]
#!
#! Where:
#! - note_index is the index of the output note whose attachments commitment should be returned.
#! - ATTACHMENTS_COMMITMENT is the commitment to all attachments of the note.
pub proc get_attachments_commitment
# get the note data pointer based on the index of the requested note
exec.memory::get_output_note_ptr
# => [note_ptr]

dup exec.memory::get_output_note_num_attachments
# => [num_attachments, note_ptr]

dup.1 exec.memory::get_output_note_attachment_commitment_ptr
# => [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
# => [start_ptr, end_ptr, note_ptr]

movup.2 exec.note::compute_attachments_commitment
# => [ATTACHMENTS_COMMITMENT, start_ptr, end_ptr]

# store the attachment commitments to the advice map using ATTACHMENTS_COMMITMENT as a key
adv.insert_mem
# => [ATTACHMENTS_COMMITMENT, start_ptr, end_ptr]

# remove pointers from the stack
movup.4 drop movup.4 drop
# => [ATTACHMENTS_COMMITMENT]
end

#! Adds the asset to the note specified by the index.
#!
#! Inputs: [ASSET_KEY, ASSET_VALUE, note_idx]
Expand Down
Loading
Loading