Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
453dbe6
feat: add `AssetCallbacks` and integrate into vault key
PhilippGackstatter Mar 6, 2026
f12f15f
feat: add metadata extraction code in MASM
PhilippGackstatter Mar 6, 2026
8dfb8b6
feat: implement `on_asset_added_to_account`
PhilippGackstatter Mar 9, 2026
c56074d
chore: rename `AssetCallbacks` -> `AssetCallbacksFlag`
PhilippGackstatter Mar 9, 2026
2cb5e91
feat: Add `AssetCallbacks` storage helper struct
PhilippGackstatter Mar 9, 2026
e5107cd
chore: add `BlockList` callback test component
PhilippGackstatter Mar 9, 2026
1031162
chore: define `Asset::is_same` based on vault key
PhilippGackstatter Mar 9, 2026
2701ded
chore: set callbacks flag in create_fungible_asset
PhilippGackstatter Mar 9, 2026
eff4cc7
chore: add callbacks flag in `FungibleAsset`
PhilippGackstatter Mar 9, 2026
c4e1432
fix: size hint of fungible asset delta
PhilippGackstatter Mar 9, 2026
c498054
feat: add `account::find_storage_slot`
PhilippGackstatter Mar 10, 2026
a901831
feat: add `has_callbacks` kernel proc
PhilippGackstatter Mar 10, 2026
02d598c
feat: pass `enable_callbacks` to `create_fungible_key`
PhilippGackstatter Mar 10, 2026
773b492
chore: rename to `on_before_asset_added_to_account`
PhilippGackstatter Mar 10, 2026
d9f2329
chore: update NUM_POST_COMPUTE_FEE_CYCLES according to benchmark
PhilippGackstatter Mar 10, 2026
3ffe4ba
chore: add test for callback inputs
PhilippGackstatter Mar 10, 2026
de06e70
feat: use `AssetVaultKey` in `FungibleAssetDelta`
PhilippGackstatter Mar 10, 2026
6316cb8
chore: implement serialization for `AssetVaultKey`
PhilippGackstatter Mar 10, 2026
7588e08
fix: test_create_fungible_asset_succeeds
PhilippGackstatter Mar 10, 2026
82ed18b
chore: add missing docs
PhilippGackstatter Mar 10, 2026
301425e
chore: split fpi memory clearing out of end_foreign_context
PhilippGackstatter Mar 10, 2026
5014e9b
chore: add changelog
PhilippGackstatter Mar 10, 2026
9073f5c
chore: update protocol library docs
PhilippGackstatter Mar 10, 2026
b0e8e16
chore: deduplicate slot ID definition
PhilippGackstatter Mar 10, 2026
dd75128
chore: use empty word for detection in `AssetCallbacks`
PhilippGackstatter Mar 10, 2026
08d5582
chore: return vault key in fungible asset error
PhilippGackstatter Mar 10, 2026
4b61a4f
chore: rename account_has_callbacks -> faucet_has_callbacks
PhilippGackstatter Mar 10, 2026
2b5ca98
chore: AssetCallbacksFlag -> AssetCallbackFlag
PhilippGackstatter Mar 10, 2026
bcc14a1
chore: simplify block list; minor fixes
PhilippGackstatter Mar 10, 2026
4c37b06
chore: do not pass native account ID to callback
PhilippGackstatter Mar 11, 2026
9a0d240
Merge remote-tracking branch 'origin/next' into pgackst-asset-callbacks
PhilippGackstatter Mar 11, 2026
0a1a757
feat: handle missing callback slot in kernel event handler
PhilippGackstatter Mar 11, 2026
a72d12b
chore: simplify has_non_empty_slot
PhilippGackstatter Mar 11, 2026
fb8dc3e
Merge remote-tracking branch 'origin/next' into pgackst-asset-callbacks
PhilippGackstatter Mar 12, 2026
3418a74
chore: use `has_callbacks` in `faucet::create_fungible_asset`
PhilippGackstatter Mar 12, 2026
40f401a
chore: use `padw padw swapdw` for padding
PhilippGackstatter Mar 12, 2026
b3ff0a0
chore: `get_storage_slot` -> `get_storage_slot_ptr`
PhilippGackstatter Mar 12, 2026
adfba4f
chore: make flag constants associated
PhilippGackstatter Mar 12, 2026
78db024
chore: move with_callbacks to constructor section
PhilippGackstatter Mar 12, 2026
73c2dee
chore: mention callbacks in fungible asset docs
PhilippGackstatter Mar 12, 2026
ea5c074
chore: add missing docs
PhilippGackstatter Mar 12, 2026
aa9173b
chore: remove unused doc link
PhilippGackstatter Mar 12, 2026
7010651
Merge remote-tracking branch 'origin/next' into pgackst-asset-callbacks
PhilippGackstatter Mar 16, 2026
be3c98b
chore: add potential future fungible asset delta todo
PhilippGackstatter Mar 16, 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: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
- Added `CodeBuilder::with_warnings_as_errors()` to promote assembler warning diagnostics to errors ([#2558](https://github.com/0xMiden/protocol/pull/2558)).
- Added `MockChain::add_pending_batch()` to allow submitting user batches directly ([#2565](https://github.com/0xMiden/protocol/pull/2565)).
- Added `create_fungible_key` for construction of fungible asset keys ([#2575](https://github.com/0xMiden/protocol/pull/2575)).
- Implemented the `on_before_asset_added_to_account` asset callback ([#2571](https://github.com/0xMiden/protocol/pull/2571)).
- Added `InputNoteCommitment::from_parts()` for construction of input note commitments from a nullifier and optional note header ([#2588](https://github.com/0xMiden/protocol/pull/2588)).
- Added `bool` schema type to the type registry and updated ACL auth component to use it for boolean config fields ([#2591](https://github.com/0xMiden/protocol/pull/2591)).
- Added `component_metadata()` to all account components to expose their metadata ([#2596](https://github.com/0xMiden/protocol/pull/2596)).
Expand Down Expand Up @@ -89,6 +90,8 @@
- Explicitly use `get_native_account_active_storage_slots_ptr` in `account::set_item` and `account::set_map_item`.
- Added Ownable2Step as an Account Component ([#2572](https://github.com/0xMiden/protocol/pull/2572))
- [BREAKING] Introduced `PrivateNoteHeader` for output notes and removed `RawOutputNote::Header` variant ([#2569](https://github.com/0xMiden/protocol/pull/2569)).
- [BREAKING] Changed `asset::create_fungible_asset` and `faucet::create_fungible_asset` signature to take `enable_callbacks` flag ([#2571](https://github.com/0xMiden/protocol/pull/2571)).

- [BREAKING] Fixed `TokenSymbol::try_from(Felt)` to reject values below `MIN_ENCODED_VALUE`; implemented `Display` for `TokenSymbol` replacing the fallible `to_string()` method; removed `Default` derive ([#2464](https://github.com/0xMiden/protocol/issues/2464)).

## 0.13.3 (2026-01-27)
Expand Down
36 changes: 18 additions & 18 deletions bin/bench-transaction/bench-tx.json
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
{
"consume single P2ID note": {
"prologue": 3173,
"notes_processing": 1714,
"prologue": 3487,
"notes_processing": 1831,
"note_execution": {
"0xa030091e37d38b506d764d5666f3a13af9e5702a0159974a3bc27053d7a55e01": 1674
"0x1421e92d0f84f11b3e6f84e4e1d193e648eb820666ffb8c50ea818c25a32990c": 1791
},
"tx_script_processing": 42,
"epilogue": {
"total": 63977,
"auth_procedure": 62667,
"after_tx_cycles_obtained": 574
"total": 71195,
"auth_procedure": 69694,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auth procedure cycle count went up quite a bit - but I'm guessing this may be because of some prior changes rather than because of this PR, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked a few key PRs:

before auth component consolidation: 9cfb4f2a
  "auth_procedure": 62641
before asset refactor: a09027b6
  "auth_procedure": 63170
before VM migration: e8570061
  "auth_procedure": 63169
after VM migration: 0897aae6
  "auth_procedure": 69686

So auth component consolidation added ~500 cycles but the VM migration was the main reason for the large increase in cycles.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since most of the auth procedure is Falcon signature verification - I wonder if most of the delta is because Falcon signature verification got a bit less efficient.

@Al-Kindi-0 - could you check how many cycles Falcon signature takes now? (maybe some stack re-orientation work made it less efficient?)

"after_tx_cycles_obtained": 608
}
},
"consume two P2ID notes": {
"prologue": 4131,
"notes_processing": 3431,
"prologue": 4509,
"notes_processing": 3668,
"note_execution": {
"0x209ecf97790d4328e60a3b15160760934383ecff02550cb5df72e3f6d459fa70": 1708,
"0x4f9da5658d9f717fdcfa674906e92a7424d86da93f3a21fe0362a220f0e457b7": 1674
"0x702c078c74683d33b507e16d9fc67f0be0cc943cd94c1f652e3a60e0f4164d9f": 1791,
"0x92cc0c8c208e3b8bad970c23b2c4b4c24cc8d42626b3f56363ce1a6bbf4c7ac2": 1828
},
"tx_script_processing": 42,
"epilogue": {
"total": 63949,
"auth_procedure": 62653,
"after_tx_cycles_obtained": 574
"total": 71143,
"auth_procedure": 69668,
"after_tx_cycles_obtained": 608
}
},
"create single P2ID note": {
"prologue": 1681,
"prologue": 1766,
"notes_processing": 32,
"note_execution": {},
"tx_script_processing": 1497,
"tx_script_processing": 1682,
"epilogue": {
"total": 64803,
"auth_procedure": 62899,
"after_tx_cycles_obtained": 574
"total": 72099,
"auth_procedure": 69906,
"after_tx_cycles_obtained": 608
}
}
}
22 changes: 22 additions & 0 deletions crates/miden-protocol/asm/kernels/transaction/api.masm
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,24 @@ pub proc faucet_burn_asset
# => [ASSET_VALUE, pad(12)]
end

#! Returns whether the active account defines callbacks.
#!
#! Inputs: [pad(16)]
#! Outputs: [has_callbacks, pad(15)]
#!
#! Where:
#! - has_callbacks is 1 if the account defines callbacks, 0 otherwise.
#!
#! Invocation: dynexec
pub proc faucet_has_callbacks
exec.account::has_callbacks
# => [has_callbacks, pad(16)]

# truncate the stack
swap drop
# => [has_callbacks, pad(15)]
end

# INPUT NOTE
# -------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -1484,6 +1502,10 @@ pub proc tx_exec_foreign_proc
# end the foreign context
exec.tx::end_foreign_context
# => [foreign_procedure_outputs(16)]

# clear the foreign procedure ID and root in memory
exec.tx::clear_fpi_memory
# => [foreign_procedure_outputs(16)]
end

#! Updates the transaction expiration block delta.
Expand Down
147 changes: 124 additions & 23 deletions crates/miden-protocol/asm/kernels/transaction/lib/account.masm
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use $kernel::account_delta
use $kernel::account_id
use $kernel::asset_vault
use $kernel::callbacks
use $kernel::callbacks::ON_BEFORE_ASSET_ADDED_TO_ACCOUNT_PROC_ROOT_SLOT
use $kernel::constants::ACCOUNT_PROCEDURE_DATA_LENGTH
use $kernel::constants::EMPTY_SMT_ROOT
use $kernel::constants::STORAGE_SLOT_TYPE_MAP
Expand Down Expand Up @@ -425,14 +427,51 @@ pub proc get_item
exec.memory::get_account_active_storage_slots_section_ptr
# => [acct_storage_slots_section_offset, slot_id_suffix, slot_id_prefix]

exec.find_storage_slot
exec.get_storage_slot_ptr
# => [slot_ptr]

# get the item from storage
exec.get_item_raw
# => [VALUE]
end

#! Finds an item in the active account's storage by slot ID, returning whether the slot was found
#! along with its value.
#!
#! Unlike `get_item`, this procedure does not panic if the slot does not exist. Instead, it
#! returns `is_found = 0` and the empty word.
#!
#! Inputs: [slot_id_suffix, slot_id_prefix]
#! Outputs: [is_found, VALUE]
#!
#! Where:
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#! the first two felts of the hashed slot name.
#! - is_found is 1 if the slot was found, 0 otherwise.
#! - VALUE is the value of the item, or the empty word if the slot was not found.
pub proc find_item
# get account storage slots section offset
exec.memory::get_account_active_storage_slots_section_ptr
# => [acct_storage_slots_section_offset, slot_id_suffix, slot_id_prefix]

exec.find_storage_slot
# => [is_found, slot_ptr]

if.true
# slot was found, read its value
exec.get_item_raw
# => [VALUE]

push.1
# => [is_found = 1, VALUE]
else
# slot was not found, drop slot_ptr and return empty word
drop padw push.0
# => [is_found = 0, EMPTY_WORD]
end
# => [is_found, VALUE]
end

#! Gets an item and its slot type from the account storage.
#!
#! Inputs: [slot_id_suffix, slot_id_prefix]
Expand All @@ -451,7 +490,7 @@ pub proc get_typed_item
exec.memory::get_account_active_storage_slots_section_ptr
# => [acct_storage_slots_section_offset, slot_id_suffix, slot_id_prefix]

exec.find_storage_slot
exec.get_storage_slot_ptr
# => [slot_ptr]

dup add.ACCOUNT_SLOT_TYPE_OFFSET mem_load
Expand Down Expand Up @@ -479,7 +518,7 @@ pub proc get_initial_item
exec.memory::get_account_initial_storage_slots_ptr
# => [account_initial_storage_slots_ptr, slot_id_suffix, slot_id_prefix]

exec.find_storage_slot
exec.get_storage_slot_ptr
# => [slot_ptr]

# get the item from initial storage
Expand Down Expand Up @@ -508,7 +547,7 @@ pub proc set_item
exec.memory::get_native_account_active_storage_slots_ptr
# => [storage_slots_ptr, slot_id_suffix, slot_id_prefix, VALUE]

exec.find_storage_slot
exec.get_storage_slot_ptr
# => [slot_ptr, VALUE]

# load the slot type
Expand Down Expand Up @@ -603,7 +642,7 @@ pub proc set_map_item
# => [storage_slots_ptr, slot_id_suffix, slot_id_prefix, KEY, NEW_VALUE]

# resolve the slot name to its pointer
exec.find_storage_slot
exec.get_storage_slot_ptr
# => [slot_ptr, KEY, NEW_VALUE]

# load the slot type
Expand Down Expand Up @@ -666,31 +705,38 @@ end
#! added.
#! - the vault already contains the same non-fungible asset.
pub proc add_asset_to_vault
swapw dupw.1
# => [ASSET_KEY, ASSET_VALUE, ASSET_KEY]

exec.callbacks::on_before_asset_added_to_account
swapw
# => [ASSET_KEY, PROCESSED_ASSET_VALUE]

# duplicate the asset for the later event and delta update
dupw.1 dupw.1
# => [ASSET_KEY, ASSET_VALUE, ASSET_KEY, ASSET_VALUE]
# => [ASSET_KEY, PROCESSED_ASSET_VALUE, ASSET_KEY, PROCESSED_ASSET_VALUE]

# push the account vault root ptr
exec.memory::get_account_vault_root_ptr movdn.8
# => [ASSET_KEY, ASSET_VALUE, account_vault_root_ptr, ASSET_KEY, ASSET_VALUE]
# => [ASSET_KEY, PROCESSED_ASSET_VALUE, account_vault_root_ptr, ASSET_KEY, PROCESSED_ASSET_VALUE]

# emit event to signal that an asset is going to be added to the account vault
emit.ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT
# => [ASSET_KEY, ASSET_VALUE, account_vault_root_ptr, ASSET_KEY, ASSET_VALUE]
# => [ASSET_KEY, PROCESSED_ASSET_VALUE, account_vault_root_ptr, ASSET_KEY, PROCESSED_ASSET_VALUE]

# add the asset to the account vault
exec.asset_vault::add_asset
# => [ASSET_VALUE', ASSET_KEY, ASSET_VALUE]
# => [PROCESSED_ASSET_VALUE', ASSET_KEY, PROCESSED_ASSET_VALUE]

movdnw.2
# => [ASSET_KEY, ASSET_VALUE, ASSET_VALUE']
# => [ASSET_KEY, PROCESSED_ASSET_VALUE, PROCESSED_ASSET_VALUE']

# emit event to signal that an asset is being added to the account vault
emit.ACCOUNT_VAULT_AFTER_ADD_ASSET_EVENT
# => [ASSET_KEY, ASSET_VALUE, ASSET_VALUE']
# => [ASSET_KEY, PROCESSED_ASSET_VALUE, PROCESSED_ASSET_VALUE']

exec.account_delta::add_asset
# => [ASSET_VALUE']
# => [PROCESSED_ASSET_VALUE']
end

#! Removes the specified asset from the account vault.
Expand Down Expand Up @@ -1581,7 +1627,7 @@ end
#! - a slot with the provided slot ID does not exist in account storage.
#! - the requested storage slot type is not map.
proc get_map_item_raw
exec.find_storage_slot
exec.get_storage_slot_ptr
# => [slot_ptr, KEY]

emit.ACCOUNT_STORAGE_BEFORE_GET_MAP_ITEM_EVENT
Expand Down Expand Up @@ -1613,20 +1659,18 @@ proc get_map_item_raw
# => [VALUE]
end

#! Finds the slot identified by the key [slot_id_prefix, slot_id_suffix, 0, 0] (stack order) and
#! returns the pointer to that slot.
#! Finds the slot identified by the key [_, _, slot_id_suffix, slot_id_prefix] and returns a flag
#! indicating whether the slot was found and the pointer to that slot.
#!
#! Inputs: [storage_slots_ptr, slot_id_suffix, slot_id_prefix]
#! Outputs: [slot_ptr]
#! Outputs: [is_found, slot_ptr]
#!
#! Where:
#! - storage_slots_ptr is the pointer to the storage slots section.
#! - is_found is 1 if the slot was found, 0 otherwise.
#! - slot_ptr is the pointer to the resolved storage slot.
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#! the first two felts of the hashed slot name.
#!
#! Panics if:
#! - a slot with the provided slot ID does not exist in account storage.
proc find_storage_slot
# construct the start and end pointers of the storage slot section in which we will search
dup exec.memory::get_num_storage_slots mul.ACCOUNT_STORAGE_SLOT_DATA_LENGTH add
Expand All @@ -1635,15 +1679,33 @@ proc find_storage_slot
swap movup.3 movup.3
# => [slot_id_suffix, slot_id_prefix, storage_slots_start_ptr, storage_slots_end_ptr]

# find the slot whose slot key matches [_, _, slot_id_suffix, slot_id_prefix]
# if the slot key does not exist, this procedure will validate its absence
exec.sorted_array::find_half_key_value
# => [is_slot_found, slot_ptr, storage_slots_start_ptr, storage_slots_end_ptr]

assert.err=ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME
# => [slot_ptr, storage_slots_start_ptr, storage_slots_end_ptr]
movup.2 drop movup.2 drop
# => [is_slot_found, slot_ptr]
end

#! Finds the slot identified by the key [_, _, slot_id_suffix, slot_id_prefix] and returns the
#! pointer to that slot.
#!
#! Inputs: [storage_slots_ptr, slot_id_suffix, slot_id_prefix]
#! Outputs: [slot_ptr]
#!
#! Where:
#! - storage_slots_ptr is the pointer to the storage slots section.
#! - slot_ptr is the pointer to the resolved storage slot.
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#! the first two felts of the hashed slot name.
#!
#! Panics if:
#! - a slot with the provided slot ID does not exist in account storage.
proc get_storage_slot_ptr
exec.find_storage_slot
# => [is_found, slot_ptr]

swap.2 drop drop
assert.err=ERR_ACCOUNT_UNKNOWN_STORAGE_SLOT_NAME
# => [slot_ptr]
end

Expand Down Expand Up @@ -1988,6 +2050,45 @@ pub proc has_procedure
# => [is_procedure_available']
end

# CALLBACKS
# -------------------------------------------------------------------------------------------------

#! Returns whether the active account defines callbacks.
#!
#! Inputs: []
#! Outputs: [has_callbacks]
#!
#! Where:
#! - has_callbacks is 1 if the account defines callbacks, 0 otherwise.
pub proc has_callbacks
# check if the on_before_asset_added_to_account callback slot exists and is non-empty
push.ON_BEFORE_ASSET_ADDED_TO_ACCOUNT_PROC_ROOT_SLOT[0..2]
exec.has_non_empty_slot
# => [has_callbacks]
end

#! Checks whether a storage slot with the given slot ID exists in the active account's storage
#! and has a non-empty value.
#!
#! Inputs: [slot_id_suffix, slot_id_prefix]
#! Outputs: [has_non_empty_value]
#!
#! Where:
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#! the first two felts of the hashed slot name.
#! - has_non_empty_value is 1 if the slot exists and its value is non-empty, 0 otherwise.
proc has_non_empty_slot
exec.find_item
# => [is_found, VALUE]

# check if is_found && value is non-empty
movdn.4 exec.word::eqz not
# => [is_non_empty_value, is_found]

and
# => [has_non_empty_value]
end

#! Returns the key built from the provided account ID for use in the advice map or the account
#! tree.
#!
Expand Down
Loading
Loading