Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- Added `miden::protocol::output_note::seal` to prevent further asset and attachment changes to an output note and `is_sealed` to query its sealing state ([#3923](https://github.com/0xMiden/protocol/pull/3923)).

### Changes

- [BREAKING] Linked standard and AggLayer account components dynamically against their libraries ([#3925](https://github.com/0xMiden/protocol/pull/3925)).
Expand Down
35 changes: 26 additions & 9 deletions crates/miden-protocol/asm/kernels/transaction-core/src/memory.masm
Original file line number Diff line number Diff line change
Expand Up @@ -344,15 +344,16 @@ const OUTPUT_NOTE_DETAILS_COMMITMENT_OFFSET = 0
const OUTPUT_NOTE_METADATA_OFFSET = 4
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_ASSETS_OFFSET = 36
const OUTPUT_NOTE_SEALED_FLAG_OFFSET = 13
const OUTPUT_NOTE_NUM_ASSETS_OFFSET = 16
const OUTPUT_NOTE_NUM_ATTACHMENTS_OFFSET = 17
const OUTPUT_NOTE_TOTAL_ATTACHMENT_WORDS_OFFSET = 18
const OUTPUT_NOTE_ATTACHMENT_0_OFFSET = 20
const OUTPUT_NOTE_ATTACHMENT_1_OFFSET = 24
const OUTPUT_NOTE_ATTACHMENT_2_OFFSET = 28
const OUTPUT_NOTE_ATTACHMENT_3_OFFSET = 32
const OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET = 36
const OUTPUT_NOTE_ASSETS_OFFSET = 40

# LINK MAP MEMORY
# -------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -2145,6 +2146,22 @@ pub proc set_output_note_dirty_flag(output_note_data_ptr: ptr<OutputNoteData>, d
add.OUTPUT_NOTE_DIRTY_FLAG_OFFSET mem_store
end

#! Returns whether the output note has been sealed against further mutations.
#!
#! Inputs: [output_note_data_ptr]
#! Outputs: [sealed]
pub proc is_output_note_sealed(output_note_data_ptr: ptr<OutputNoteData>) -> Bool
add.OUTPUT_NOTE_SEALED_FLAG_OFFSET mem_load
end

#! Seals an output note for the remainder of the transaction.
#!
#! Inputs: [output_note_data_ptr]
#! Outputs: []
pub proc seal_output_note(output_note_data_ptr: ptr<OutputNoteData>)
push.1 swap add.OUTPUT_NOTE_SEALED_FLAG_OFFSET mem_store
end

#! Returns a pointer to the output note asset data.
#!
#! Inputs: [output_note_data_ptr]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const ATTACHMENT_RESERVED_SCHEME = 0
const ERR_TX_NUMBER_OF_OUTPUT_NOTES_EXCEEDS_LIMIT =
"number of output notes in the transaction exceeds the maximum limit of 1024"

const ERR_OUTPUT_NOTE_IS_SEALED = "sealed output notes cannot be modified"

const ERR_NOTE_INVALID_TYPE = "invalid note type"

const ERR_OUTPUT_NOTE_INDEX_OUT_OF_BOUNDS =
Expand Down Expand Up @@ -247,6 +249,7 @@ end
#! - the max amount of fungible assets is exceeded.
#! - the non-fungible asset already exists in the note.
#! - the total number of ASSETs exceeds the maximum of 16.
#! - the output note is sealed, including by the asset callback.
pub proc add_asset(asset: Asset, note_idx: u16)
# reject assets with a Custom composition; only None and Fungible are supported today
exec.asset::assert_supported_composition
Expand All @@ -270,7 +273,10 @@ pub proc add_asset(asset: Asset, note_idx: u16)
exec.callbacks::on_before_asset_added_to_note
# => [ASSET_ID, ASSET_VALUE, note_idx]

movup.8 exec.memory::get_output_note_ptr dup
movup.8 exec.memory::get_output_note_ptr
# Check after the callback, which can itself seal the note.
dup exec.memory::is_output_note_sealed assertz.err=ERR_OUTPUT_NOTE_IS_SEALED
dup
# => [note_ptr, note_ptr, ASSET_ID, ASSET_VALUE]

movdn.9 movdn.9
Expand Down Expand Up @@ -313,6 +319,7 @@ end
#! - the number of elements is not a multiple of 4, or num_words is zero or exceeds 256.
#! - the computed hash of fetched attachment elements does not match ATTACHMENT_COMMITMENT.
#! - the note already has 4 attachments.
#! - the output note is sealed.
pub proc add_attachment(
attachment_scheme: u16,
attachment_commitment: NoteAttachmentCommitment,
Expand Down Expand Up @@ -341,6 +348,9 @@ pub proc add_attachment(
movup.6 exec.memory::get_output_note_ptr
# => [note_ptr, attachment_scheme, num_words, ATTACHMENT_COMMITMENT]

dup exec.memory::is_output_note_sealed assertz.err=ERR_OUTPUT_NOTE_IS_SEALED
# => [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]
Expand Down
45 changes: 45 additions & 0 deletions crates/miden-protocol/asm/kernels/transaction/lib/api.masm
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,7 @@ end
#! Panics if:
#! - the procedure is called when the active account is not the native one.
#! - the note index points to a non-existent output note.
#! - the output note is sealed.
#!
#! Invocation: dynexec
pub proc output_note_add_asset(asset: Asset, note_idx: u16, padding: [felt; 7]) -> [felt; 16]
Expand All @@ -1574,6 +1575,49 @@ pub proc output_note_add_asset(asset: Asset, note_idx: u16, padding: [felt; 7])
# => [pad(16)]
end

#! Prevents further changes to the specified output note's assets and attachments.
#! Sealing is idempotent.
#!
#! Inputs: [note_idx, pad(15)]
#! Outputs: [pad(16)]
#!
#! Panics if:
#! - the active account is not the native account.
#! - the note index points to a non-existent output note.
#!
#! Invocation: dynexec
pub proc output_note_seal(note_idx: u16, padding: [felt; 15]) -> [felt; 16]
exec.memory::assert_native_account
exec.output_note::assert_note_index_in_bounds
# => [note_idx, pad(15)]

exec.memory::get_output_note_ptr
# => [note_ptr, pad(15)]

exec.memory::seal_output_note
# => [pad(16)]
end

#! Returns whether the specified output note is sealed against asset and attachment changes.
#!
#! Inputs: [note_idx, pad(15)]
#! Outputs: [is_sealed, pad(15)]
#!
#! Panics if:
#! - the note index points to a non-existent output note.
#!
#! Invocation: dynexec
pub proc output_note_is_sealed(note_idx: u16, padding: [felt; 15]) -> (Bool, [felt; 15])
exec.output_note::assert_note_index_in_bounds
# => [note_idx, pad(15)]

exec.memory::get_output_note_ptr
# => [note_ptr, pad(15)]

exec.memory::is_output_note_sealed
# => [is_sealed, pad(15)]
end
Comment thread
partylikeits1983 marked this conversation as resolved.

#! Adds an attachment to the note specified by the index.
#!
#! Inputs: [attachment_scheme, ATTACHMENT, note_idx, pad(10)]
Expand All @@ -1587,6 +1631,7 @@ end
#! Panics if:
#! - the procedure is called when the active account is not the native one.
#! - the note index points to a non-existent output note.
#! - the output note is sealed.
#! - the attachment scheme is 0 or exceeds 65534.
#! - the attachment num_words exceeds 256 or is zero.
#! - the note already has 4 attachments.
Expand Down
30 changes: 16 additions & 14 deletions crates/miden-protocol/asm/protocol/src/kernel_proc_offsets.masm
Original file line number Diff line number Diff line change
Expand Up @@ -75,33 +75,35 @@ pub const OUTPUT_NOTE_GET_ASSETS_INFO_OFFSET = 43
pub const OUTPUT_NOTE_GET_ATTACHMENTS_COMMITMENT_OFFSET = 44
pub const OUTPUT_NOTE_ADD_ASSET_OFFSET = 45
pub const OUTPUT_NOTE_ADD_ATTACHMENT_OFFSET = 46
pub const OUTPUT_NOTE_SEAL_OFFSET = 47
pub const OUTPUT_NOTE_IS_SEALED_OFFSET = 48

### Tx ##########################################

# input notes
pub const TX_GET_NUM_INPUT_NOTES_OFFSET = 47
pub const TX_GET_INPUT_NOTES_COMMITMENT_OFFSET = 48
pub const TX_GET_NUM_INPUT_NOTES_OFFSET = 49
pub const TX_GET_INPUT_NOTES_COMMITMENT_OFFSET = 50

# output notes
pub const TX_GET_NUM_OUTPUT_NOTES_OFFSET = 49
pub const TX_GET_OUTPUT_NOTES_COMMITMENT_OFFSET = 50
pub const TX_GET_NUM_OUTPUT_NOTES_OFFSET = 51
pub const TX_GET_OUTPUT_NOTES_COMMITMENT_OFFSET = 52

# block info
pub const TX_GET_BLOCK_COMMITMENT_OFFSET = 51
pub const TX_GET_REFERENCE_BLOCK_NUMBER_OFFSET = 52
pub const TX_GET_BLOCK_TIMESTAMP_OFFSET = 53
pub const TX_GET_BLOCK_COMMITMENT_OFFSET = 53
pub const TX_GET_REFERENCE_BLOCK_NUMBER_OFFSET = 54
pub const TX_GET_BLOCK_TIMESTAMP_OFFSET = 55

# foreign context
pub const TX_PREPARE_FPI_OFFSET = 54
pub const TX_EXEC_FOREIGN_PROC_OFFSET = 55
pub const TX_PREPARE_FPI_OFFSET = 56
pub const TX_EXEC_FOREIGN_PROC_OFFSET = 57

# expiration data
pub const TX_GET_EXPIRATION_DELTA_OFFSET = 56 # accessor
pub const TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET = 57 # mutator
pub const TX_GET_EXPIRATION_DELTA_OFFSET = 58 # accessor
pub const TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET = 59 # mutator

# tx script
pub const TX_GET_TX_SCRIPT_ROOT_OFFSET = 58
pub const TX_GET_TX_SCRIPT_ROOT_OFFSET = 60

# fee
pub const TX_COMPUTE_FEE_OFFSET = 59
pub const TX_GET_FEE_ASSET_ID_OFFSET = 60 # accessor
pub const TX_COMPUTE_FEE_OFFSET = 61
pub const TX_GET_FEE_ASSET_ID_OFFSET = 62 # accessor
59 changes: 58 additions & 1 deletion crates/miden-protocol/asm/protocol/src/output_note.masm
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use miden::protocol::note
use miden::protocol::note_internal
use miden::core::crypto::hashes::poseidon2
use {WORD_NUM_ELEMENTS} from miden::protocol::constants
use {OUTPUT_NOTE_ADD_ASSET_OFFSET, OUTPUT_NOTE_ADD_ATTACHMENT_OFFSET, OUTPUT_NOTE_CREATE_OFFSET, OUTPUT_NOTE_GET_ASSETS_INFO_OFFSET, OUTPUT_NOTE_GET_ATTACHMENTS_COMMITMENT_OFFSET, OUTPUT_NOTE_GET_METADATA_OFFSET, OUTPUT_NOTE_GET_RECIPIENT_OFFSET}
use {OUTPUT_NOTE_ADD_ASSET_OFFSET, OUTPUT_NOTE_ADD_ATTACHMENT_OFFSET, OUTPUT_NOTE_CREATE_OFFSET, OUTPUT_NOTE_GET_ASSETS_INFO_OFFSET, OUTPUT_NOTE_GET_ATTACHMENTS_COMMITMENT_OFFSET, OUTPUT_NOTE_GET_METADATA_OFFSET, OUTPUT_NOTE_GET_RECIPIENT_OFFSET, OUTPUT_NOTE_IS_SEALED_OFFSET, OUTPUT_NOTE_SEAL_OFFSET}
from miden::protocol::kernel_proc_offsets

# PROCEDURES
Expand Down Expand Up @@ -57,6 +57,55 @@ pub proc create(tag: NoteTag, note_type: NoteType, recipient: NoteRecipient) ->
# => [note_idx]
end

#! Prevents further changes to an output note's assets and attachments.
#!
#! Call this after fully populating an output whose final NoteId must remain fixed.
#! Sealing is idempotent.
#!
#! Inputs: [note_index]
#! Outputs: []
#!
#! Panics if:
#! - the active account is not the native account.
#! - the note index is greater or equal to the total number of output notes.
#!
#! Invocation: exec
pub proc seal(note_index: u16)
# Pad above the caller's remaining stack before bringing the note index to the top.
padw padw padw push.0.0
movup.14 push.OUTPUT_NOTE_SEAL_OFFSET
# => [offset, note_index, pad(14)]

syscall.exec_kernel_proc
# => [pad(16)]

dropw dropw dropw dropw
# => []
end

#! Returns whether the specified output note is sealed against asset and attachment changes.
#!
#! Inputs: [note_index]
#! Outputs: [is_sealed]
#!
#! Panics if:
#! - the note index is greater or equal to the total number of output notes.
#!
#! Invocation: exec
pub proc is_sealed(note_index: u16) -> Bool
# Pad above the caller's remaining stack before bringing the note index to the top.
padw padw padw push.0.0
movup.14 push.OUTPUT_NOTE_IS_SEALED_OFFSET
# => [offset, note_index, pad(14)]

syscall.exec_kernel_proc
# => [is_sealed, pad(15)]

# Remove padding while preserving the result.
swapw.3 dropw dropw dropw movdn.3 drop drop drop
# => [is_sealed]
end

#! Returns the information about assets in the output note with the specified index.
#!
#! This information can then be used to retrieve the actual assets from the advice map.
Expand Down Expand Up @@ -177,6 +226,11 @@ end
#! - ASSET_ID is the asset ID of the asset to add.
#! - ASSET_VALUE is the value of the asset to add.
#!
#! Panics if:
#! - the output note is sealed.
#! - the note index points to a non-existent output note.
#! - the active account is not the native account.
#!
#! Invocation: exec
pub proc add_asset(asset: Asset, note_idx: u16)
push.OUTPUT_NOTE_ADD_ASSET_OFFSET
Expand Down Expand Up @@ -218,6 +272,7 @@ end
#! Panics if:
#! - the procedure is called when the active account is not the native one.
#! - the note index points to a non-existent output note.
#! - the output note is sealed.
#! - the attachment scheme is 0 or exceeds 65534.
#! - the note already has 4 attachments.
#! - the number of words in the attachment exceeds 256.
Expand Down Expand Up @@ -260,6 +315,7 @@ end
#! Panics if:
#! - the procedure is called when the active account is not the native one.
#! - the note index points to a non-existent output note.
#! - the output note is sealed.
#! - the attachment scheme is 0 or exceeds 65534.
#! - the note already has 4 attachments.
#! - the total number of attachment words in the note exceeds 512.
Expand Down Expand Up @@ -319,6 +375,7 @@ end
#! Panics if:
#! - the procedure is called when the active account is not the native one.
#! - the note index points to a non-existent output note.
#! - the output note is sealed.
#! - the attachment scheme is 0 or exceeds 65534.
#! - the note already has 4 attachments.
#! - the number of words in the attachment exceeds 256.
Expand Down
52 changes: 28 additions & 24 deletions crates/miden-protocol/src/transaction/kernel/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,23 +474,26 @@ pub const INPUT_NOTE_ASSETS_OFFSET: MemoryOffset = 48;
// The total number of output notes for a transaction is stored in the bookkeeping section of the
// memory. Data section of each note is laid out like so:
//
// ┌──────────────┬──────────┬───────────┬───────────────────────────────────────────┬
// │ NOTE DETAILS │ METADATA │ RECIPIENT │ [dirty_flag, num_assets, │
// │ COMMITMENT │ │ │ num_attachments, total_attachment_words] │
// ├──────────────┼──────────┼───────────┼───────────────────────────────────────────┼
// 0 4 8 12
// | Offset | Section |
// | ------- | -------------------------------------------------------- |
// | 0 | NOTE DETAILS COMMITMENT |
// | 4 | METADATA |
// | 8 | RECIPIENT |
// | 12 | [dirty_flag, sealed_flag, 0, 0] |
// | 16 | [num_assets, num_attachments, total_attachment_words, 0] |
// | 20 | ATTACHMENT 0 |
// | 24 | ATTACHMENT 1 |
// | 28 | ATTACHMENT 2 |
// | 32 | ATTACHMENT 3 |
// | 36 | ASSETS COMMITMENT |
// | 40 + 8i | ASSET KEY i |
// | 44 + 8i | ASSET VALUE i |
// | 40 + 8n | PADDING |
//
// ┬────────────┬────────────┬────────────┬────────────┬────────────┬
// │ ATTACHMENT │ ATTACHMENT │ ATTACHMENT │ ATTACHMENT │ ASSETS │
// │ 0 │ 1 │ 2 │ 3 │ COMMITMENT │
// ┼────────────┼────────────┼────────────┼────────────┼────────────┼
// 16 20 24 28 32
// Offsets are measured in field elements, where n = num_assets and 0 <= i < n.
//
// ┬───────┬─────────┬─────┬────────┬─────────┬─────────┐
// │ ASSET │ ASSET │ ... │ ASSET │ ASSET │ PADDING │
// │ KEY 0 │ VALUE 0 │ │ KEY n │ VALUE n │ │
// ┼───────┼─────────┼─────┼────────┼─────────┼─────────┘
// 36 40 36 + 8n 40 + 8n
// The sealed flag starts at zero. Once set to one, asset and attachment mutations are rejected; it is
// excluded from note commitments and serialization.
//
// The DIRTY_FLAG is the binary flag which specifies whether the assets commitment stored in this
// note is outdated. It holds 1 if some changes were made to the note assets since the last
Expand All @@ -507,15 +510,16 @@ pub const OUTPUT_NOTE_DETAILS_COMMITMENT_OFFSET: MemoryOffset = 0;
pub const OUTPUT_NOTE_METADATA_OFFSET: MemoryOffset = 4;
pub const OUTPUT_NOTE_RECIPIENT_OFFSET: MemoryOffset = 8;
pub const OUTPUT_NOTE_DIRTY_FLAG_OFFSET: MemoryOffset = 12;
pub const OUTPUT_NOTE_NUM_ASSETS_OFFSET: MemoryOffset = 13;
pub const OUTPUT_NOTE_NUM_ATTACHMENTS_OFFSET: MemoryOffset = 14;
pub const OUTPUT_NOTE_TOTAL_ATTACHMENT_WORDS_OFFSET: MemoryOffset = 15;
pub const OUTPUT_NOTE_ATTACHMENT_0_OFFSET: MemoryOffset = 16;
pub const OUTPUT_NOTE_ATTACHMENT_1_OFFSET: MemoryOffset = 20;
pub const OUTPUT_NOTE_ATTACHMENT_2_OFFSET: MemoryOffset = 24;
pub const OUTPUT_NOTE_ATTACHMENT_3_OFFSET: MemoryOffset = 28;
pub const OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET: MemoryOffset = 32;
pub const OUTPUT_NOTE_ASSETS_OFFSET: MemoryOffset = 36;
pub const OUTPUT_NOTE_SEALED_FLAG_OFFSET: MemoryOffset = 13;
pub const OUTPUT_NOTE_NUM_ASSETS_OFFSET: MemoryOffset = 16;
pub const OUTPUT_NOTE_NUM_ATTACHMENTS_OFFSET: MemoryOffset = 17;
pub const OUTPUT_NOTE_TOTAL_ATTACHMENT_WORDS_OFFSET: MemoryOffset = 18;
pub const OUTPUT_NOTE_ATTACHMENT_0_OFFSET: MemoryOffset = 20;
pub const OUTPUT_NOTE_ATTACHMENT_1_OFFSET: MemoryOffset = 24;
pub const OUTPUT_NOTE_ATTACHMENT_2_OFFSET: MemoryOffset = 28;
pub const OUTPUT_NOTE_ATTACHMENT_3_OFFSET: MemoryOffset = 32;
pub const OUTPUT_NOTE_ASSETS_COMMITMENT_OFFSET: MemoryOffset = 36;
pub const OUTPUT_NOTE_ASSETS_OFFSET: MemoryOffset = 40;

// ASSETS
// ------------------------------------------------------------------------------------------------
Expand Down
Loading
Loading