From 12124d495191a01216584ee75565f8680ce94b8c Mon Sep 17 00:00:00 2001 From: riemann Date: Fri, 27 Feb 2026 20:23:12 +0300 Subject: [PATCH 01/29] feat: wip CLAIM note flow reorientation --- .../asm/agglayer/bridge/bridge_config.masm | 112 ++++- .../asm/agglayer/bridge/bridge_in.masm | 389 ++++++++++++++++++ .../asm/agglayer/faucet/mod.masm | 323 +-------------- .../miden-agglayer/asm/components/bridge.masm | 1 + .../miden-agglayer/asm/components/faucet.masm | 9 +- .../asm/note_scripts/CLAIM.masm | 22 +- .../asm/note_scripts/CONFIG_AGG_BRIDGE.masm | 45 +- crates/miden-agglayer/src/claim_note.rs | 11 +- crates/miden-agglayer/src/config_note.rs | 25 +- crates/miden-agglayer/src/errors/agglayer.rs | 7 +- crates/miden-agglayer/src/lib.rs | 34 +- .../miden-testing/tests/agglayer/bridge_in.rs | 80 +++- .../tests/agglayer/bridge_out.rs | 1 + .../tests/agglayer/config_bridge.rs | 7 + 14 files changed, 678 insertions(+), 388 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm index 2e2d80da89..26b28f69cd 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm @@ -9,6 +9,7 @@ use miden::protocol::native_account const ERR_GER_NOT_FOUND = "GER not found in storage" const ERR_FAUCET_NOT_REGISTERED="faucet is not registered in the bridge's faucet registry" +const ERR_TOKEN_NOT_REGISTERED="token address is not registered in the bridge's token registry" const ERR_SENDER_NOT_BRIDGE_ADMIN="note sender is not the bridge admin" const ERR_SENDER_NOT_GER_MANAGER="note sender is not the global exit root manager" @@ -20,6 +21,10 @@ const BRIDGE_ADMIN_SLOT=word("miden::agglayer::bridge::admin") const GER_MANAGER_SLOT=word("miden::agglayer::bridge::ger_manager") const GER_STORAGE_SLOT=word("miden::agglayer::bridge::ger") const FAUCET_REGISTRY_SLOT=word("miden::agglayer::bridge::faucet_registry") +const TOKEN_REGISTRY_SLOT=word("miden::agglayer::bridge::token_registry") + +# Memory pointer for hashing token address +const TOKEN_ADDR_HASH_PTR=900 # Flags const GER_KNOWN_FLAG=1 @@ -96,40 +101,67 @@ pub proc assert_valid_ger # => [] end -#! Registers a faucet in the bridge's faucet registry. -#! -#! Writes `KEY -> [1, 0, 0, 0]` into the `faucet_registry` map, where -#! `KEY = [faucet_id_prefix, faucet_id_suffix, 0, 0]`. +#! Registers a faucet in the bridge's faucet registry and token registry. #! -#! The sentinel value `[1, 0, 0, 0]` distinguishes registered faucets from -#! non-existent entries (SMTs return EMPTY_WORD for missing keys). +#! 1. Writes `KEY -> [1, 0, 0, 0]` into the `faucet_registry` map, where +#! `KEY = [faucet_id_prefix, faucet_id_suffix, 0, 0]`. +#! 2. Writes `hash(tokenAddress[5]) -> [faucet_id_prefix, faucet_id_suffix, 0, 0]` +#! into the `token_registry` map. #! #! Panics if the note sender is not the bridge admin. #! -#! Inputs: [faucet_id_prefix, faucet_id_suffix, pad(14)] +#! Inputs: [origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] #! Outputs: [pad(16)] #! #! Invocation: call pub proc register_faucet # assert the note sender is the bridge admin. exec.assert_sender_is_bridge_admin - # => [faucet_id_prefix, faucet_id_suffix, pad(14)] + # => [origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] + + push.111 debug.stack drop + # Save faucet ID for later use in token_registry + dup.6 dup.6 + # => [faucet_id_prefix, faucet_id_suffix, origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] + + # --- 1. Register faucet in faucet_registry --- # set_map_item expects [slot_id(2), KEY(4), VALUE(4)] and returns [OLD_VALUE(4)]. push.IS_FAUCET_REGISTERED_FLAG - # => [IS_FAUCET_REGISTERED_FLAG, faucet_id_prefix, faucet_id_suffix, pad(14)] + # => [1, faucet_id_prefix, faucet_id_suffix, origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] movdn.7 - # => [[faucet_id_prefix, faucet_id_suffix, 0, 0], [0, 0, 0, IS_FAUCET_REGISTERED_FLAG], pad(9)] + # => [[faucet_id_prefix, faucet_id_suffix, 0, 0], [0, 0, 0, 1], origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] - # Place slot ID on top push.FAUCET_REGISTRY_SLOT[0..2] - # Stack: [slot0, slot1, [prefix, suffix, 0, 0], [0, 0, 0, 1], pad(9)] + exec.native_account::set_map_item + # => [OLD_VALUE(4), origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] + + dropw + # => [origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] + + # --- 2. Register token address → faucet ID in token_registry --- + + # Hash the token address + exec.hash_token_address + # => [TOKEN_ADDR_HASH(4), faucet_id_prefix, faucet_id_suffix, pad(9)] + # Build VALUE = [faucet_id_prefix, faucet_id_suffix, 0, 0] + movup.5 movup.5 + # => [faucet_id_prefix, faucet_id_suffix, TOKEN_ADDR_HASH(4), pad(9)] + + push.0.0 movup.3 movup.3 + # => [faucet_id_prefix, faucet_id_suffix, 0, 0, TOKEN_ADDR_HASH(4), pad(9)] + + swapw + # => [TOKEN_ADDR_HASH(4), faucet_id_prefix, faucet_id_suffix, 0, 0, pad(9)] + + push.TOKEN_REGISTRY_SLOT[0..2] exec.native_account::set_map_item # => [OLD_VALUE(4), pad(9)] dropw + # => [pad(9)] end #! Asserts that a faucet is registered in the bridge's faucet registry. @@ -160,6 +192,62 @@ pub proc assert_faucet_registered # => [] end +#! Looks up the faucet account ID for a given origin token address. +#! +#! Hashes the origin token address (5 felts) using RPO256 and looks up the result +#! in the token_registry map. Returns the faucet account ID. +#! +#! Inputs: [origin_token_addr(5)] +#! Outputs: [faucet_id_prefix, faucet_id_suffix] +#! +#! Panics if: +#! - the token address is not registered in the token registry. +#! +#! Invocation: exec +pub proc lookup_faucet_by_token_address + # Hash the token address + exec.hash_token_address + # => [TOKEN_ADDR_HASH(4)] + + push.TOKEN_REGISTRY_SLOT[0..2] + exec.active_account::get_map_item + # => [VALUE(4)] = [faucet_id_prefix, faucet_id_suffix, 0, 0] + + # Assert the token is registered: faucet_id_prefix is always non-zero for valid account IDs. + # Check that at least one of prefix/suffix is non-zero (unregistered entries return all zeros). + dup dup.2 or assert.err=ERR_TOKEN_NOT_REGISTERED + # => [faucet_id_prefix, faucet_id_suffix, 0, 0] + + movup.2 drop movup.2 drop + # => [faucet_id_prefix, faucet_id_suffix] +end + +# HELPER PROCEDURES +# ================================================================================================= + +#! Hashes a 5-felt origin token address using RPO256. +#! +#! Writes the 5 felts to memory and computes the RPO hash. +#! +#! Inputs: [origin_token_addr(5)] +#! Outputs: [TOKEN_ADDR_HASH(4)] +#! +#! Invocation: exec +proc hash_token_address + # Write origin_token_addr[5] to memory for hashing + mem_store.TOKEN_ADDR_HASH_PTR + push.TOKEN_ADDR_HASH_PTR add.1 mem_store + push.TOKEN_ADDR_HASH_PTR add.2 mem_store + push.TOKEN_ADDR_HASH_PTR add.3 mem_store + push.TOKEN_ADDR_HASH_PTR add.4 mem_store + # => [] + + # Hash the token address: rpo256::hash_elements(num_elements=5, start_ptr) + push.5 push.TOKEN_ADDR_HASH_PTR + exec.rpo256::hash_elements + # => [TOKEN_ADDR_HASH(4)] +end + #! Asserts that the note sender matches the bridge admin stored in account storage. #! #! Reads the bridge admin account ID from BRIDGE_ADMIN_SLOT and compares it against diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index 53a17ded37..9c9469294c 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -1,9 +1,16 @@ use miden::agglayer::bridge::bridge_config use miden::agglayer::bridge::leaf_utils use miden::agglayer::common::utils +use miden::agglayer::common::asset_conversion +use miden::agglayer::common::eth_address use miden::core::crypto::hashes::keccak256 +use miden::core::crypto::hashes::rpo256 use miden::core::mem use miden::core::word +use miden::protocol::note +use miden::protocol::output_note +use miden::standards::note_tag +use miden::standards::attachments::network_account_target # TYPE ALIASES # ================================================================================================= @@ -19,6 +26,7 @@ const ERR_BRIDGE_NOT_MAINNET = "bridge not mainnet" const ERR_LEADING_BITS_NON_ZERO = "leading bits of global index must be zero" const ERR_ROLLUP_INDEX_NON_ZERO = "rollup index must be zero for a mainnet deposit" const ERR_SMT_ROOT_VERIFICATION_FAILED = "merkle proof verification failed: provided SMT root does not match the computed root" +const ERR_INVALID_CLAIM_PROOF = "invalid claim proof" # CONSTANTS # ================================================================================================= @@ -46,6 +54,65 @@ const PROOF_DATA_WORD_LEN = 134 # the number of words (4 felts each) in the advice map leaf data const LEAF_DATA_NUM_WORDS = 8 +# Memory pointers for piped advice map data (used by claim procedure) +const CLAIM_PROOF_DATA_START_PTR = 0 +const CLAIM_LEAF_DATA_START_PTR = 536 +const CLAIM_OUTPUT_NOTE_FAUCET_AMOUNT = 568 + +# Memory addresses for stored keys (used by claim procedure) +const CLAIM_PROOF_DATA_KEY_MEM_ADDR = 700 +const CLAIM_LEAF_DATA_KEY_MEM_ADDR = 704 + +# Data sizes for claim +const CLAIM_PROOF_DATA_WORD_LEN = 134 +const CLAIM_LEAF_DATA_WORD_LEN = 8 + +# Memory addresses for leaf data fields (derived from leaf data layout at CLAIM_LEAF_DATA_START_PTR=536) +const ORIGIN_TOKEN_ADDRESS_0 = 538 +const ORIGIN_TOKEN_ADDRESS_1 = 539 +const ORIGIN_TOKEN_ADDRESS_2 = 540 +const ORIGIN_TOKEN_ADDRESS_3 = 541 +const ORIGIN_TOKEN_ADDRESS_4 = 542 +const DESTINATION_ADDRESS_0 = 544 +const DESTINATION_ADDRESS_1 = 545 +const DESTINATION_ADDRESS_2 = 546 +const DESTINATION_ADDRESS_3 = 547 +const DESTINATION_ADDRESS_4 = 548 +const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_0 = 549 +const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_1 = 550 +const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_2 = 551 +const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_3 = 552 +const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_4 = 553 +const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_5 = 554 +const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_6 = 555 +const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_7 = 556 + +# Memory for MINT note output construction +const MINT_NOTE_STORAGE_MEM_ADDR = 800 +const P2ID_RECIPIENT_STORAGE_MEM_ADDR = 0 + +# claim memory locals +const CLAIM_PREFIX_MEM_LOC = 8 +const CLAIM_SUFFIX_MEM_LOC = 9 +const CLAIM_AMOUNT_MEM_LOC_0 = 0 +const CLAIM_AMOUNT_MEM_LOC_1 = 4 + +# build_mint_output_note memory locals +const BUILD_MINT_AMOUNT_MEM_LOC_0 = 0 +const BUILD_MINT_AMOUNT_MEM_LOC_1 = 4 +const BUILD_MINT_PREFIX_MEM_LOC = 8 +const BUILD_MINT_FAUCET_PREFIX_LOC = 10 +const BUILD_MINT_FAUCET_SUFFIX_LOC = 11 + +# MINT note storage layout (private mode, 12 items): +# [tag, amount, attachment_kind, attachment_scheme, ATTACHMENT(4), RECIPIENT_DIGEST(4)] +const MINT_NOTE_NUM_STORAGE_ITEMS = 12 +const MINT_NOTE_TYPE_PUBLIC = 1 + +# P2ID output note constants +const P2ID_NOTE_NUM_STORAGE_ITEMS = 2 +const OUTPUT_NOTE_TYPE_PUBLIC = 1 + # PUBLIC INTERFACE # ================================================================================================= @@ -189,6 +256,96 @@ pub proc verify_merkle_proof( # => [verification_flag] end +#! Validates a claim against the AggLayer bridge and creates a MINT note for the aggfaucet. +#! +#! This procedure is called by the CLAIM note script when consumed by the bridge account. +#! It validates the Merkle proof directly (no FPI needed since bridge is the native account), +#! then looks up the faucet account ID from the token registry using the origin token address +#! from the leaf data, and creates a MINT note targeting the aggfaucet. +#! +#! The MINT note uses the standard MintNote pattern (private mode) with 12 storage items: +#! [tag, amount, attachment_kind(0), attachment_scheme(0), ATTACHMENT(0,0,0,0), RECIPIENT_DIGEST(4)] +#! +#! Inputs: [PROOF_DATA_KEY, LEAF_DATA_KEY, faucet_mint_amount, pad(7)] +#! Outputs: [pad(16)] +#! +#! Advice map: { +#! PROOF_DATA_KEY => [ +#! smtProofLocalExitRoot[256], // SMT proof for local exit root (256 felts) +#! smtProofRollupExitRoot[256], // SMT proof for rollup exit root (256 felts) +#! globalIndex[8], // Global index (8 felts, uint256 as 8 u32 felts) +#! mainnetExitRoot[8], // Mainnet exit root hash (8 felts) +#! rollupExitRoot[8], // Rollup exit root hash (8 felts) +#! ], +#! LEAF_DATA_KEY => [ +#! leafType[1], // Leaf type (1 felt, uint32) +#! originNetwork[1], // Origin network identifier (1 felt, uint32) +#! originTokenAddress[5], // Origin token address (5 felts) +#! destinationNetwork[1], // Destination network identifier (1 felt, uint32) +#! destinationAddress[5], // Destination address (5 felts) +#! amount[8], // Amount of tokens (8 felts, uint256 as 8 u32 felts) +#! metadata[8], // ABI encoded metadata (8 felts, fixed size) +#! padding[3], // padding (3 felts) +#! ], +#! } +#! +#! Panics if: +#! - the Merkle proof validation fails. +#! - the origin token address is not registered in the bridge's token registry. +#! +#! Invocation: call +@locals(12) # 0-3: amount_lo, 4-7: amount_hi, 8: dest_prefix, 9: dest_suffix, 10: faucet_prefix, 11: faucet_suffix +pub proc claim + # Write output note faucet amount to memory + movup.8 mem_store.CLAIM_OUTPUT_NOTE_FAUCET_AMOUNT + # => [PROOF_DATA_KEY, LEAF_DATA_KEY, pad(7)] + + # Check AdviceMap values hash to keys & write CLAIM inputs & DATA_KEYs to global memory + exec.claim_batch_pipe_double_words + # => [pad(7)] + + # validate_claim will overwrite memory in-place, so we need to load the account and amount + # before calling validate_claim and store it in memory locals + exec.get_destination_account_id_data + loc_store.CLAIM_PREFIX_MEM_LOC loc_store.CLAIM_SUFFIX_MEM_LOC + # => [pad(7)] + + exec.get_raw_claim_amount + loc_storew_be.CLAIM_AMOUNT_MEM_LOC_0 dropw loc_storew_be.CLAIM_AMOUNT_MEM_LOC_1 dropw + # => [pad(7)] + + # Look up the faucet account ID from the origin token address in the leaf data + exec.get_origin_token_address + # => [origin_token_addr(5), pad(7)] + + exec.bridge_config::lookup_faucet_by_token_address + # => [faucet_id_prefix, faucet_id_suffix, pad(7)] + + loc_store.BUILD_MINT_FAUCET_PREFIX_LOC loc_store.BUILD_MINT_FAUCET_SUFFIX_LOC + # => [pad(7)] + + # VALIDATE CLAIM - call verify_leaf_bridge directly (bridge is the native account) + mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR + # => [PROOF_DATA_KEY, pad(7)] + + swapw mem_loadw_be.CLAIM_LEAF_DATA_KEY_MEM_ADDR + # => [LEAF_DATA_KEY, PROOF_DATA_KEY, pad(7)] + + # Call verify_leaf_bridge directly (no FPI needed - bridge is the native account) + exec.verify_leaf_bridge + # => [pad(16)] + + # Build MINT output note targeting the aggfaucet + loc_loadw_be.CLAIM_AMOUNT_MEM_LOC_1 swapw loc_loadw_be.CLAIM_AMOUNT_MEM_LOC_0 + # => [AMOUNT[0], AMOUNT[1], pad(8)] + + loc_load.CLAIM_SUFFIX_MEM_LOC loc_load.CLAIM_PREFIX_MEM_LOC + # => [prefix, suffix, AMOUNT[0], AMOUNT[1], pad(8)] + + exec.build_mint_output_note + # => [pad(16)] +end + # HELPER PROCEDURES # ================================================================================================= @@ -286,6 +443,238 @@ proc verify_leaf # => [] end +# Inputs: [PROOF_DATA_KEY, LEAF_DATA_KEY] +# Outputs: [] +proc claim_batch_pipe_double_words + # 1) Verify PROOF_DATA_KEY + mem_storew_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR + adv.push_mapval + # => [PROOF_DATA_KEY] + + push.CLAIM_PROOF_DATA_START_PTR push.CLAIM_PROOF_DATA_WORD_LEN + exec.mem::pipe_double_words_preimage_to_memory drop + + # 2) Verify LEAF_DATA_KEY + mem_storew_be.CLAIM_LEAF_DATA_KEY_MEM_ADDR + adv.push_mapval + # => [LEAF_DATA_KEY] + + push.CLAIM_LEAF_DATA_START_PTR push.CLAIM_LEAF_DATA_WORD_LEN + exec.mem::pipe_double_words_preimage_to_memory drop +end + +#! Extracts the destination account ID as address[5] from memory. +#! +#! This procedure reads the destination address from the leaf data and converts it from +#! Ethereum address format to AccountId format (prefix, suffix). +#! +#! Inputs: [] +#! Outputs: [prefix, suffix] +#! +#! Invocation: exec +proc get_destination_account_id_data + mem_load.DESTINATION_ADDRESS_4 + mem_load.DESTINATION_ADDRESS_3 + mem_load.DESTINATION_ADDRESS_2 + mem_load.DESTINATION_ADDRESS_1 + mem_load.DESTINATION_ADDRESS_0 + # => [address[5]] + + exec.eth_address::to_account_id + # => [prefix, suffix] +end + +# Inputs: [] +# Outputs: [U256[0], U256[1]] +proc get_raw_claim_amount + mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_7 + mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_6 + mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_5 + mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_4 + mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_3 + mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_2 + mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_1 + mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_0 +end + +#! Reads the origin token address (5 felts) from the leaf data in memory. +#! +#! Inputs: [] +#! Outputs: [origin_token_addr(5)] +#! +#! Invocation: exec +proc get_origin_token_address + mem_load.ORIGIN_TOKEN_ADDRESS_0 + mem_load.ORIGIN_TOKEN_ADDRESS_1 + mem_load.ORIGIN_TOKEN_ADDRESS_2 + mem_load.ORIGIN_TOKEN_ADDRESS_3 + mem_load.ORIGIN_TOKEN_ADDRESS_4 + # => [origin_token_addr(5)] +end + +#! Builds a MINT output note targeting the aggfaucet. +#! +#! The MINT note uses private mode (12 storage items) and contains: +#! - tag: Note tag targeting the faucet account +#! - amount: The scaled-down Miden amount to mint +#! - attachment_kind: 0 (no attachment) +#! - attachment_scheme: 0 (no attachment) +#! - ATTACHMENT: [0, 0, 0, 0] +#! - RECIPIENT_DIGEST: The P2ID recipient digest for the destination account +#! +#! Inputs: [prefix, suffix, AMOUNT[0], AMOUNT[1]] +#! Outputs: [] +#! +#! Where: +#! - prefix, suffix: destination account ID +#! - AMOUNT[0], AMOUNT[1]: raw U256 claim amount (8 felts) +@locals(12) # 0-3: amount_lo, 4-7: amount_hi, 8: prefix, 9: faucet_prefix, 10: faucet_suffix, 11: unused +proc build_mint_output_note + # save prefix to local memory for later use in note tag computation + dup loc_store.BUILD_MINT_PREFIX_MEM_LOC + + # write destination account id into memory for use in note::build_recipient + push.P2ID_RECIPIENT_STORAGE_MEM_ADDR add.1 mem_store mem_store.P2ID_RECIPIENT_STORAGE_MEM_ADDR + + # store amount in memory locals + loc_storew_be.BUILD_MINT_AMOUNT_MEM_LOC_0 dropw loc_storew_be.BUILD_MINT_AMOUNT_MEM_LOC_1 dropw + # => [pad(16)] + + # Build P2ID recipient digest for the MINT note storage + # First, get the P2ID script root + procref.::miden::standards::notes::p2id::main + # => [SCRIPT_ROOT] + + # Use PROOF_DATA_KEY as the P2ID serial number + swapw mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR + # => [SERIAL_NUM, SCRIPT_ROOT] + + push.P2ID_NOTE_NUM_STORAGE_ITEMS + # => [note_num_storage_items, SERIAL_NUM, SCRIPT_ROOT] + + push.P2ID_RECIPIENT_STORAGE_MEM_ADDR + # => [storage_ptr, note_num_storage_items, SERIAL_NUM, SCRIPT_ROOT] + + exec.note::build_recipient + # => [RECIPIENT_DIGEST] + + # Now build the MINT note storage in memory + # Layout: [tag, amount, attachment_kind(0), attachment_scheme(0), ATTACHMENT(0,0,0,0), RECIPIENT_DIGEST(4)] + + # Compute the note tag for the destination account + loc_load.BUILD_MINT_PREFIX_MEM_LOC + # => [dest_prefix, RECIPIENT_DIGEST] + + exec.note_tag::create_account_target + # => [dest_tag, RECIPIENT_DIGEST] + + # Verify the U256 amount converts correctly to the native amount + padw loc_loadw_be.BUILD_MINT_AMOUNT_MEM_LOC_1 padw loc_loadw_be.BUILD_MINT_AMOUNT_MEM_LOC_0 + # => [AMOUNT[0], AMOUNT[1], dest_tag, RECIPIENT_DIGEST] + + mem_load.CLAIM_OUTPUT_NOTE_FAUCET_AMOUNT movdn.8 + # => [AMOUNT[0], AMOUNT[1], native_amount, dest_tag, RECIPIENT_DIGEST] + + # We need the scale factor. Since the bridge doesn't store it, we use the + # pre-computed miden_claim_amount from the CLAIM note storage directly. + # The native_amount was already verified by the CLAIM note creator. + # Drop the U256 amount and use native_amount directly. + repeat.8 swap drop end + # => [native_amount, dest_tag, RECIPIENT_DIGEST] + + # Store MINT note storage items in memory: + # [0]: dest_tag (tag for the P2ID output note) + # [1]: native_amount + # [2]: attachment_kind = 0 + # [3]: attachment_scheme = 0 + # [4..7]: ATTACHMENT = [0, 0, 0, 0] + # [8..11]: RECIPIENT_DIGEST + + # Write tag to MINT note storage + push.MINT_NOTE_STORAGE_MEM_ADDR mem_store + # => [native_amount, RECIPIENT_DIGEST] + + # Write amount to MINT note storage + push.MINT_NOTE_STORAGE_MEM_ADDR add.1 mem_store + # => [RECIPIENT_DIGEST] + + # Write attachment_kind = 0 + push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.2 mem_store + # => [RECIPIENT_DIGEST] + + # Write attachment_scheme = 0 + push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.3 mem_store + # => [RECIPIENT_DIGEST] + + # Write ATTACHMENT = [0, 0, 0, 0] + push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.4 mem_store + push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.5 mem_store + push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.6 mem_store + push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.7 mem_store + # => [RECIPIENT_DIGEST] + + # Write RECIPIENT_DIGEST to MINT note storage [8..11] + # Stack: [r0, r1, r2, r3] + push.MINT_NOTE_STORAGE_MEM_ADDR add.11 mem_store + push.MINT_NOTE_STORAGE_MEM_ADDR add.10 mem_store + push.MINT_NOTE_STORAGE_MEM_ADDR add.9 mem_store + push.MINT_NOTE_STORAGE_MEM_ADDR add.8 mem_store + # => [] + + # Now create the MINT output note + # Get the MINT note script root + procref.::miden::standards::notes::mint::main + # => [MINT_SCRIPT_ROOT] + + # Generate a serial number for the MINT note (use PROOF_DATA_KEY) + swapw mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR + # => [SERIAL_NUM, MINT_SCRIPT_ROOT] + + # Build the MINT note recipient + push.MINT_NOTE_NUM_STORAGE_ITEMS + # => [num_storage_items, SERIAL_NUM, MINT_SCRIPT_ROOT] + + push.MINT_NOTE_STORAGE_MEM_ADDR + # => [storage_ptr, num_storage_items, SERIAL_NUM, MINT_SCRIPT_ROOT] + + exec.note::build_recipient + # => [MINT_RECIPIENT] + + # Create the MINT output note targeting the faucet + push.MINT_NOTE_TYPE_PUBLIC + # => [note_type, MINT_RECIPIENT] + + # Compute note tag targeting the faucet account + loc_load.BUILD_MINT_FAUCET_PREFIX_LOC + # => [faucet_prefix, note_type, MINT_RECIPIENT] + + exec.note_tag::create_account_target + # => [faucet_tag, note_type, MINT_RECIPIENT] + + # Create the output note (no assets - MINT notes carry no assets) + exec.output_note::create + # => [note_idx] + + # Set the attachment on the MINT note to target the faucet account + # NetworkAccountTarget attachment: targets the faucet so only it can consume the note + # network_account_target::new expects [prefix, suffix, exec_hint] + # and returns [attachment_scheme, attachment_kind, ATTACHMENT] + push.1 # exec_hint = ALWAYS + loc_load.BUILD_MINT_FAUCET_SUFFIX_LOC + loc_load.BUILD_MINT_FAUCET_PREFIX_LOC + # => [faucet_prefix, faucet_suffix, exec_hint, note_idx] + + exec.network_account_target::new + # => [attachment_scheme, attachment_kind, ATTACHMENT(4), note_idx] + + # Rearrange for set_attachment: [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] + movup.6 + # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT(4)] + + exec.output_note::set_attachment + # => [] +end + #! Computes the root of the SMT based on the provided Merkle path, leaf value and leaf index. #! #! Inputs: [LEAF_VALUE_LO, LEAF_VALUE_HI, merkle_path_ptr, leaf_idx] diff --git a/crates/miden-agglayer/asm/agglayer/faucet/mod.masm b/crates/miden-agglayer/asm/agglayer/faucet/mod.masm index d3b2913627..5fec0122db 100644 --- a/crates/miden-agglayer/asm/agglayer/faucet/mod.masm +++ b/crates/miden-agglayer/asm/agglayer/faucet/mod.masm @@ -1,4 +1,3 @@ -use miden::agglayer::bridge::bridge_in use miden::core::sys use miden::agglayer::common::utils use miden::agglayer::common::asset_conversion @@ -6,78 +5,19 @@ use miden::agglayer::common::eth_address use miden::protocol::active_account use miden::protocol::active_note use miden::standards::faucets -use miden::standards::note_tag -use miden::protocol::note -use miden::protocol::tx -use miden::core::mem +use miden::standards::faucets::network_fungible +use miden::standards::access::ownable use miden::core::word -# ERRORS -# ================================================================================================= - -const ERR_INVALID_CLAIM_PROOF = "invalid claim proof" - # CONSTANTS # ================================================================================================= -# Storage slots -# The slot in this component's storage layout where the bridge account ID is stored. -const BRIDGE_ID_SLOT = word("miden::agglayer::faucet") # Storage slots for conversion metadata. # Slot 1: [addr_felt0, addr_felt1, addr_felt2, addr_felt3] — first 4 felts of origin token address const CONVERSION_INFO_1_SLOT = word("miden::agglayer::faucet::conversion_info_1") # Slot 2: [addr_felt4, origin_network, scale, 0] — remaining address felt + origin network + scale const CONVERSION_INFO_2_SLOT = word("miden::agglayer::faucet::conversion_info_2") -# Memory pointers for piped advice map data -const PROOF_DATA_START_PTR = 0 -const LEAF_DATA_START_PTR = 536 -const OUTPUT_NOTE_DATA_START_PTR = 568 - -# Memory addresses for stored keys -const PROOF_DATA_KEY_MEM_ADDR = 700 -const LEAF_DATA_KEY_MEM_ADDR = 704 -const OUTPUT_NOTE_DATA_MEM_ADDR = 708 -const CLAIM_NOTE_DATA_MEM_ADDR = 712 - -# Memory addresses for output note fields (derived from leaf data layout) -const DESTINATION_ADDRESS_0 = 544 -const DESTINATION_ADDRESS_1 = 545 -const DESTINATION_ADDRESS_2 = 546 -const DESTINATION_ADDRESS_3 = 547 -const DESTINATION_ADDRESS_4 = 548 -const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_0 = 549 -const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_1 = 550 -const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_2 = 551 -const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_3 = 552 -const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_4 = 553 -const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_5 = 554 -const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_6 = 555 -const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_7 = 556 -const OUTPUT_NOTE_STORAGE_MEM_ADDR = 0 -const OUTPUT_NOTE_FAUCET_AMOUNT = 568 - -# Memory locals in claim -const CLAIM_PREFIX_MEM_LOC = 8 -const CLAIM_SUFFIX_MEM_LOC = 9 -const CLAIM_AMOUNT_MEM_LOC_0 = 0 -const CLAIM_AMOUNT_MEM_LOC_1 = 4 - -# Memory locals in build_p2id_output_note -const BUILD_P2ID_AMOUNT_MEM_LOC_0 = 0 -const BUILD_P2ID_AMOUNT_MEM_LOC_1 = 4 -const BUILD_P2ID_PREFIX_MEM_LOC = 8 - -# Data sizes -const PROOF_DATA_WORD_LEN = 134 -const LEAF_DATA_WORD_LEN = 8 -const OUTPUT_NOTE_DATA_WORD_LEN = 2 - -# P2ID output note constants -const P2ID_NOTE_NUM_STORAGE_ITEMS = 2 -const OUTPUT_NOTE_TYPE_PUBLIC = 1 -const OUTPUT_NOTE_AUX = 0 - # PUBLIC INTERFACE # ================================================================================================= @@ -190,253 +130,6 @@ pub proc asset_to_origin_asset exec.sys::truncate_stack end -# CLAIM PROCEDURES -# ================================================================================================= - -#! Inputs: [LEAF_DATA_KEY, PROOF_DATA_KEY] -#! Outputs: [] -#! -#! Panics if: -#! - the bridge account ID is not properly configured in storage. -#! - the foreign procedure invocation fails. -#! - the claim proof validation fails. -#! -#! Invocation: exec -proc validate_claim - # get bridge_in::verify_leaf_bridge procedure MAST root - procref.bridge_in::verify_leaf_bridge - # => [BRIDGE_PROC_MAST_ROOT, LEAF_DATA_KEY, PROOF_DATA_KEY] - - push.BRIDGE_ID_SLOT[0..2] - # => [bridge_id_idx, BRIDGE_PROC_MAST_ROOT, LEAF_DATA_KEY, PROOF_DATA_KEY] - - # get bridge account ID - exec.active_account::get_item - # => [bridge_account_id_prefix, bridge_account_id_suffix, 0, 0, BRIDGE_PROC_MAST_ROOT, LEAF_DATA_KEY, PROOF_DATA_KEY] - - movup.2 drop movup.2 drop - # => [bridge_account_id_prefix, bridge_account_id_suffix, BRIDGE_PROC_MAST_ROOT, LEAF_DATA_KEY, PROOF_DATA_KEY] - - # call bridge_in::verify_leaf_bridge - exec.tx::execute_foreign_procedure - # => [] -end - -# Inputs: [] -# Outputs: [U256[0], U256[1]] -proc get_raw_claim_amount - mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_7 - mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_6 - mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_5 - mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_4 - mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_3 - mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_2 - mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_1 - mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_0 -end - -# Inputs: [U256[0], U256[1]] -# Outputs: [amount] -proc scale_down_amount - repeat.7 drop end -end - -# Inputs: [PROOF_DATA_KEY, LEAF_DATA_KEY] -# Outputs: [] -proc batch_pipe_double_words - # 1) Verify PROOF_DATA_KEY - mem_storew_be.PROOF_DATA_KEY_MEM_ADDR - adv.push_mapval - # => [PROOF_DATA_KEY] - - push.PROOF_DATA_START_PTR push.PROOF_DATA_WORD_LEN - exec.mem::pipe_double_words_preimage_to_memory drop - - # 2) Verify LEAF_DATA_KEY - mem_storew_be.LEAF_DATA_KEY_MEM_ADDR - adv.push_mapval - # => [LEAF_DATA_KEY] - - push.LEAF_DATA_START_PTR push.LEAF_DATA_WORD_LEN - exec.mem::pipe_double_words_preimage_to_memory drop -end - -#! Extracts the destination account ID as address[5] from memory. -#! -#! This procedure reads the destination address from the leaf data and converts it from -#! Ethereum address format to AccountId format (prefix, suffix). -#! -#! Inputs: [] -#! Outputs: [prefix, suffix] -#! -#! Invocation: exec -proc get_destination_account_id_data - mem_load.DESTINATION_ADDRESS_4 - mem_load.DESTINATION_ADDRESS_3 - mem_load.DESTINATION_ADDRESS_2 - mem_load.DESTINATION_ADDRESS_1 - mem_load.DESTINATION_ADDRESS_0 - # => [address[5]] - - exec.eth_address::to_account_id - # => [prefix, suffix] -end - -#! Builds a P2ID output note for the claim recipient. -#! -#! This procedure expects the claim data to be already written to memory via batch_pipe_double_words. -#! It reads the destination account ID, amount, and other note parameters from memory to construct -#! the output note. -#! -#! Inputs: [prefix, suffix, AMOUNT[0], AMOUNT[1]] -#! Outputs: [] -#! -#! WARNING: This procedure currently assumes the claim amount fits within 128 bits (i.e. AMOUNT[1] -#! is all zeros). This assumption holds for all practical token amounts but is not explicitly -#! enforced here. See the TODO below. -#! -#! TODO: Add an explicit assertion that AMOUNT[1] is zero. -#! -#! Note: This procedure will be refactored in a follow-up to use leaf data to build the output note. -@locals(9) -proc build_p2id_output_note - # save prefix to local memory for later use in note tag computation - dup loc_store.BUILD_P2ID_PREFIX_MEM_LOC - - # write destination account id into memory for use in note::build_recipient - push.OUTPUT_NOTE_STORAGE_MEM_ADDR add.1 mem_store mem_store.OUTPUT_NOTE_STORAGE_MEM_ADDR - - # store amount in memory locals for use in faucets::distribute - loc_storew_be.BUILD_P2ID_AMOUNT_MEM_LOC_0 dropw loc_storew_be.BUILD_P2ID_AMOUNT_MEM_LOC_1 dropw - # => [pad(16)] - - # Build P2ID output note - procref.::miden::standards::notes::p2id::main - # => [SCRIPT_ROOT] - - # Use PROOF_DATA_KEY as the P2ID serial number - swapw mem_loadw_be.PROOF_DATA_KEY_MEM_ADDR - # => [SERIAL_NUM, SCRIPT_ROOT] - - push.P2ID_NOTE_NUM_STORAGE_ITEMS - # => [note_num_storage_items, SERIAL_NUM, SCRIPT_ROOT] - - push.OUTPUT_NOTE_STORAGE_MEM_ADDR - # => [storage_ptr, note_num_storage_items, SERIAL_NUM, SCRIPT_ROOT] - - exec.note::build_recipient - # => [RECIPIENT] - - push.OUTPUT_NOTE_TYPE_PUBLIC - # => [note_type, RECIPIENT] - - # Compute note tag from destination account prefix (read from local memory) - loc_load.BUILD_P2ID_PREFIX_MEM_LOC - # => [account_id_prefix, note_type, RECIPIENT] - - exec.note_tag::create_account_target - # => [tag, note_type, RECIPIENT] - - padw loc_loadw_be.BUILD_P2ID_AMOUNT_MEM_LOC_1 padw loc_loadw_be.BUILD_P2ID_AMOUNT_MEM_LOC_0 - # => [AMOUNT[0], AMOUNT[1], tag, note_type, RECIPIENT] - - mem_load.OUTPUT_NOTE_FAUCET_AMOUNT movdn.8 - # => [AMOUNT[0], AMOUNT[1], native_amount, tag, note_type, RECIPIENT] - - exec.get_scale movdn.8 - # => [AMOUNT[0], AMOUNT[1], scale, native_amount, tag, note_type, RECIPIENT] - - exec.asset_conversion::verify_u256_to_native_amount_conversion - # => [amount, tag, note_type, RECIPIENT] - - exec.faucets::distribute - # => [pad(16)] -end - -#! Validates a claim against the AggLayer bridge and mints the corresponding asset to the recipient. -#! -#! This procedure validates the rollup exit root Merkle Proof via FPI against the agglayer bridge, -#! and if validation passes, mints the asset and creates an output note for the recipient. -#! -#! WARNING: The EVM claim asset amount is currently assumed to fit within 128 bits. See the WARNING in -#! build_p2id_output_note for details. -#! -#! TODO: Expand this description to cover the double-spend protection mechanism in detail. -#! Double-spend can be prevented in two ways: -#! 1) While it's possible to create two identical P2ID notes, only one can actually be consumed. -#! If the claim note is consumed twice, only one P2ID output note will be successfully consumed. -#! 2) We can have a mapping in the bridge or in the faucet that stores consumed claim proofs -#! as a hash -> bool value (similar to how it's done in the agglayer solidity contract). -#! -#! Inputs: [PROOF_DATA_KEY, LEAF_DATA_KEY, faucet_mint_amount, pad(7)] -#! Outputs: [pad(16)] -#! -#! Advice map: { -#! PROOF_DATA_KEY => [ -#! smtProofLocalExitRoot[256], // SMT proof for local exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) -#! smtProofRollupExitRoot[256], // SMT proof for rollup exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) -#! globalIndex[8], // Global index (8 felts, uint256 as 8 u32 felts) -#! mainnetExitRoot[8], // Mainnet exit root hash (8 felts, bytes32 as 8 u32 felts) -#! rollupExitRoot[8], // Rollup exit root hash (8 felts, bytes32 as 8 u32 felts) -#! ], -#! LEAF_DATA_KEY => [ -#! leafType[1], // Leaf type (1 felt, uint8) -#! originNetwork[1], // Origin network identifier (1 felt, uint32) -#! originTokenAddress[5], // Origin token address (5 felts, address as 5 u32 felts) -#! destinationNetwork[1], // Destination network identifier (1 felt, uint32) -#! destinationAddress[5], // Destination address (5 felts, address as 5 u32 felts) -#! amount[8], // Amount of tokens (8 felts, uint256 as 8 u32 felts) -#! metadata[8], // ABI encoded metadata (8 felts, fixed size) -#! ], -#! } -#! -#! Panics if: -#! - the rollup exit root Merkle Proof validation via FPI fails. -#! - any of the validations in faucets::distribute fail. -#! -#! Invocation: call -@locals(10) # 2 for prefix and suffix, 8 for amount -pub proc claim - # Write output note faucet amount to memory - movup.8 mem_store.OUTPUT_NOTE_FAUCET_AMOUNT - # => [PROOF_DATA_KEY, LEAF_DATA_KEY, pad(7)] - - # Check AdviceMap values hash to keys & write CLAIM inputs & DATA_KEYs to global memory - exec.batch_pipe_double_words - # => [pad(7)] - - # validate_claim will overwrite memory in-place, so we need to load the account and amount - # before calling validate_claim and store it in memory locals - exec.get_destination_account_id_data - loc_store.CLAIM_PREFIX_MEM_LOC loc_store.CLAIM_SUFFIX_MEM_LOC - # => [pad(7)] - - exec.get_raw_claim_amount - loc_storew_be.CLAIM_AMOUNT_MEM_LOC_0 dropw loc_storew_be.CLAIM_AMOUNT_MEM_LOC_1 dropw - # => [pad(7)] - - # VALIDATE CLAIM - mem_loadw_be.PROOF_DATA_KEY_MEM_ADDR - # => [PROOF_DATA_KEY, pad(7)] - - swapw mem_loadw_be.LEAF_DATA_KEY_MEM_ADDR - # => [LEAF_DATA_KEY, PROOF_DATA_KEY, pad(7)] - - # Errors on invalid proof - exec.validate_claim - # => [pad(16)] - - # Create P2ID output note - loc_loadw_be.CLAIM_AMOUNT_MEM_LOC_1 swapw loc_loadw_be.CLAIM_AMOUNT_MEM_LOC_0 - # => [AMOUNT[0], AMOUNT[1], pad(8)] - - loc_load.CLAIM_SUFFIX_MEM_LOC loc_load.CLAIM_PREFIX_MEM_LOC - # => [prefix, suffix, AMOUNT[0], AMOUNT[1], pad(8)] - - exec.build_p2id_output_note - # => [pad(16)] -end - #! Burns the fungible asset from the active note. #! #! This procedure retrieves the asset from the active note and burns it. The note must contain @@ -454,3 +147,15 @@ end #! #! Invocation: call pub use ::miden::standards::faucets::basic_fungible::burn + +#! Re-export the network fungible faucet's distribute procedure. +#! +#! This procedure first checks if the note sender is the owner of the faucet, and then +#! mints the asset and creates an output note with that asset for the recipient. +#! Used by the standard MINT note to mint assets on the aggfaucet. +#! +#! Inputs: [amount, tag, note_type, RECIPIENT, pad(9)] +#! Outputs: [note_idx, pad(15)] +#! +#! Invocation: call +pub use ::miden::standards::faucets::network_fungible::distribute diff --git a/crates/miden-agglayer/asm/components/bridge.masm b/crates/miden-agglayer/asm/components/bridge.masm index 98c8287576..a17b426b91 100644 --- a/crates/miden-agglayer/asm/components/bridge.masm +++ b/crates/miden-agglayer/asm/components/bridge.masm @@ -6,4 +6,5 @@ pub use ::miden::agglayer::bridge::bridge_config::register_faucet pub use ::miden::agglayer::bridge::bridge_config::update_ger pub use ::miden::agglayer::bridge::bridge_in::verify_leaf_bridge +pub use ::miden::agglayer::bridge::bridge_in::claim pub use ::miden::agglayer::bridge::bridge_out::bridge_out diff --git a/crates/miden-agglayer/asm/components/faucet.masm b/crates/miden-agglayer/asm/components/faucet.masm index 641b6089e7..d2ac674a48 100644 --- a/crates/miden-agglayer/asm/components/faucet.masm +++ b/crates/miden-agglayer/asm/components/faucet.masm @@ -1,10 +1,11 @@ # The MASM code of the AggLayer Faucet Account Component. # # This is a thin wrapper that re-exports faucet-related procedures from the -# agglayer library. Only procedures relevant to faucet accounts are exposed -# here, so that bridge-specific procedures (like `bridge_out`) are not -# available on faucet accounts. +# agglayer library. The faucet exposes: +# - `distribute` from the network fungible faucet (for MINT note consumption, with owner verification) +# - `asset_to_origin_asset` for bridge-out FPI +# - `burn` for bridge-out -pub use ::miden::agglayer::faucet::claim +pub use ::miden::agglayer::faucet::distribute pub use ::miden::agglayer::faucet::asset_to_origin_asset pub use ::miden::agglayer::faucet::burn diff --git a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm index 8e420ff285..334ba1e1bf 100644 --- a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm +++ b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm @@ -1,4 +1,4 @@ -use miden::agglayer::faucet -> agg_faucet +use miden::agglayer::bridge::bridge_in -> bridge use miden::protocol::active_note use miden::protocol::note use miden::core::crypto::hashes::keccak256 @@ -84,13 +84,15 @@ proc write_claim_data_into_advice_map_by_key # => [PROOF_DATA_KEY, LEAF_DATA_KEY] end -#! Agglayer Faucet CLAIM script: claims assets by calling the agglayer faucet's claim function. +#! Agglayer Bridge CLAIM script: claims assets by calling the bridge's claim function. #! -#! This note can only be consumed by the specific agglayer faucet account whose ID is provided -#! in the note attachment (NetworkAccountTarget). Upon consumption, it will create a P2ID note. +#! This note is consumed by the agglayer bridge account whose ID is provided +#! in the note attachment (NetworkAccountTarget). Upon consumption, the bridge validates +#! the Merkle proof, looks up the faucet from the token registry, and creates a MINT note +#! targeting the aggfaucet. #! #! Requires that the account exposes: -#! - agglayer::agglayer_faucet::claim procedure. +#! - agglayer::bridge::bridge_in::claim procedure. #! #! Inputs: [ARGS, pad(12)] #! Outputs: [pad(16)] @@ -138,7 +140,7 @@ begin dropw # => [pad(16)] - # Ensure note attachment targets the consuming faucet account. + # Ensure note attachment targets the consuming bridge account. exec.network_account_target::active_account_matches_target_account assert.err=ERR_CLAIM_TARGET_ACCT_MISMATCH # => [pad(16)] @@ -156,11 +158,11 @@ begin movdn.8 # => [PROOF_DATA_KEY, LEAF_DATA_KEY, faucet_mint_amount, pad(16)] - # call the Aggfaucet Claim procedure - call.agg_faucet::claim - # => [pad(16), pad(9)] + # call the Bridge Claim procedure + call.bridge::claim + # => [pad(16), pad(7)] # a call invocation consumes and returns 16 elements, but we had trailing padding - dropw dropw drop + dropw drop drop drop # => [pad(16)] end diff --git a/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm b/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm index 8201c9ae5d..1ee966648c 100644 --- a/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm +++ b/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm @@ -8,20 +8,19 @@ use miden::standards::attachments::network_account_target # ================================================================================================= const STORAGE_START_PTR = 0 -const CONFIG_AGG_BRIDGE_NUM_STORAGE_ITEMS = 2 +const CONFIG_AGG_BRIDGE_NUM_STORAGE_ITEMS = 7 # ERRORS # ================================================================================================= -const ERR_CONFIG_AGG_BRIDGE_UNEXPECTED_STORAGE_ITEMS = "CONFIG_AGG_BRIDGE expects exactly 2 note storage items" +const ERR_CONFIG_AGG_BRIDGE_UNEXPECTED_STORAGE_ITEMS = "CONFIG_AGG_BRIDGE expects exactly 7 note storage items" const ERR_CONFIG_AGG_BRIDGE_TARGET_ACCOUNT_MISMATCH = "CONFIG_AGG_BRIDGE note attachment target account does not match consuming account" -#! Registers a faucet in the bridge's faucet registry. +#! Registers a faucet in the bridge's faucet registry and token registry. #! #! This note can only be consumed by the Agglayer Bridge account that is targeted by the note #! attachment, and only if the note was sent by the bridge admin. -#! Upon consumption, it registers the faucet ID from note storage in the bridge's -#! faucet registry. +#! Upon consumption, it registers the faucet ID and origin token address mapping in the bridge. #! #! Requires that the account exposes: #! - agglayer::bridge_config::register_faucet procedure. @@ -29,19 +28,19 @@ const ERR_CONFIG_AGG_BRIDGE_TARGET_ACCOUNT_MISMATCH = "CONFIG_AGG_BRIDGE note at #! Inputs: [ARGS, pad(12)] #! Outputs: [pad(16)] #! -#! NoteStorage layout (2 felts total): -#! - faucet_id_prefix [0]: 1 felt -#! - faucet_id_suffix [1]: 1 felt -#! -#! Where: -#! - faucet_id_prefix: Prefix felt of the faucet account ID to register. -#! - faucet_id_suffix: Suffix felt of the faucet account ID to register. +#! NoteStorage layout (7 felts total): +#! - origin_token_addr_0 [0]: 1 felt +#! - origin_token_addr_1 [1]: 1 felt +#! - origin_token_addr_2 [2]: 1 felt +#! - origin_token_addr_3 [3]: 1 felt +#! - origin_token_addr_4 [4]: 1 felt +#! - faucet_id_prefix [5]: 1 felt +#! - faucet_id_suffix [6]: 1 felt #! #! Panics if: #! - The note attachment target account does not match the consuming bridge account. -#! - The note does not contain exactly 2 storage items. +#! - The note does not contain exactly 7 storage items. #! - The account does not expose the register_faucet procedure. -#! begin dropw # => [pad(16)] @@ -58,11 +57,21 @@ begin push.CONFIG_AGG_BRIDGE_NUM_STORAGE_ITEMS assert_eq.err=ERR_CONFIG_AGG_BRIDGE_UNEXPECTED_STORAGE_ITEMS drop # => [pad(16)] - # Load the faucet ID from memory, replacing the top 4 zeros - mem_loadw_le.STORAGE_START_PTR - # => [faucet_id_prefix, faucet_id_suffix, pad(14)] + # Load origin_token_addr(5) and faucet_id from memory + # register_faucet expects: [origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] + + # Load all 7 values individually in the correct order + mem_load.6 mem_load.5 mem_load.4 + # => [addr4, faucet_id_prefix, faucet_id_suffix, pad(12)] - # Register the faucet in the bridge's faucet registry + padw mem_loadw_le.STORAGE_START_PTR + # => [addr4, addr3, addr2, addr1, addr0, faucet_id_prefix, faucet_id_suffix, pad(12)] + + debug.stack + + # Register the faucet in the bridge call.bridge_config::register_faucet # => [pad(16)] + + dropw dropw end diff --git a/crates/miden-agglayer/src/claim_note.rs b/crates/miden-agglayer/src/claim_note.rs index 699deefdd6..2aae062be6 100644 --- a/crates/miden-agglayer/src/claim_note.rs +++ b/crates/miden-agglayer/src/claim_note.rs @@ -195,12 +195,13 @@ impl TryFrom for NoteStorage { // CLAIM NOTE CREATION // ================================================================================================ -/// Generates a CLAIM note - a note that instructs an agglayer faucet to validate and mint assets. +/// Generates a CLAIM note - a note that instructs the bridge to validate a claim and create +/// a MINT note for the aggfaucet. /// /// # Parameters /// - `storage`: The core storage for creating the CLAIM note -/// - `target_faucet_id`: The account ID of the agglayer faucet that should consume this note. -/// Encoded as a `NetworkAccountTarget` attachment on the note metadata. +/// - `target_bridge_id`: The account ID of the bridge that should consume this note. Encoded as a +/// `NetworkAccountTarget` attachment on the note metadata. /// - `sender_account_id`: The account ID of the CLAIM note creator /// - `rng`: Random number generator for creating the CLAIM note serial number /// @@ -208,13 +209,13 @@ impl TryFrom for NoteStorage { /// Returns an error if note creation fails. pub fn create_claim_note( storage: ClaimNoteStorage, - target_faucet_id: AccountId, + target_bridge_id: AccountId, sender_account_id: AccountId, rng: &mut R, ) -> Result { let note_storage = NoteStorage::try_from(storage.clone())?; - let attachment = NetworkAccountTarget::new(target_faucet_id, NoteExecutionHint::Always) + let attachment = NetworkAccountTarget::new(target_bridge_id, NoteExecutionHint::Always) .map_err(|e| NoteError::other(e.to_string()))? .into(); diff --git a/crates/miden-agglayer/src/config_note.rs b/crates/miden-agglayer/src/config_note.rs index 9a323424fb..8f1b75a2ae 100644 --- a/crates/miden-agglayer/src/config_note.rs +++ b/crates/miden-agglayer/src/config_note.rs @@ -7,9 +7,10 @@ extern crate alloc; use alloc::string::ToString; use alloc::vec; +use alloc::vec::Vec; use miden_assembly::utils::Deserializable; -use miden_core::{Program, Word}; +use miden_core::{Felt, Program, Word}; use miden_protocol::account::AccountId; use miden_protocol::crypto::rand::FeltRng; use miden_protocol::errors::NoteError; @@ -26,6 +27,8 @@ use miden_protocol::note::{ use miden_standards::note::{NetworkAccountTarget, NoteExecutionHint}; use miden_utils_sync::LazyLock; +use crate::EthAddressFormat; + // NOTE SCRIPT // ================================================================================================ @@ -43,8 +46,8 @@ static CONFIG_AGG_BRIDGE_SCRIPT: LazyLock = LazyLock::new(|| { /// CONFIG_AGG_BRIDGE note. /// -/// This note is used to register a faucet in the bridge's faucet registry. -/// It carries the faucet account ID and is always public. +/// This note is used to register a faucet in the bridge's faucet and token registries. +/// It carries the origin token address and faucet account ID, and is always public. pub struct ConfigAggBridgeNote; impl ConfigAggBridgeNote { @@ -52,7 +55,8 @@ impl ConfigAggBridgeNote { // -------------------------------------------------------------------------------------------- /// Expected number of storage items for a CONFIG_AGG_BRIDGE note. - pub const NUM_STORAGE_ITEMS: usize = 2; + /// Layout: [origin_token_addr(5), faucet_id_prefix, faucet_id_suffix] + pub const NUM_STORAGE_ITEMS: usize = 7; // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- @@ -72,12 +76,14 @@ impl ConfigAggBridgeNote { /// Creates a CONFIG_AGG_BRIDGE note to register a faucet in the bridge's registry. /// - /// The note storage contains 2 felts: + /// The note storage contains 7 felts: + /// - `origin_token_addr[0..4]`: The 5 u32 felts of the origin EVM token address /// - `faucet_id_prefix`: The prefix of the faucet account ID /// - `faucet_id_suffix`: The suffix of the faucet account ID /// /// # Parameters /// - `faucet_account_id`: The account ID of the faucet to register + /// - `origin_token_address`: The origin EVM token address for the token registry /// - `sender_account_id`: The account ID of the note creator /// - `target_account_id`: The bridge account ID that will consume this note /// - `rng`: Random number generator for creating the note serial number @@ -86,12 +92,17 @@ impl ConfigAggBridgeNote { /// Returns an error if note creation fails. pub fn create( faucet_account_id: AccountId, + origin_token_address: &EthAddressFormat, sender_account_id: AccountId, target_account_id: AccountId, rng: &mut R, ) -> Result { - // Create note storage with 2 felts: [faucet_id_prefix, faucet_id_suffix] - let storage_values = vec![faucet_account_id.prefix().as_felt(), faucet_account_id.suffix()]; + // Create note storage with 7 felts: [origin_token_addr(5), faucet_id_prefix, + // faucet_id_suffix] + let addr_elements = origin_token_address.to_elements(); + let mut storage_values: Vec = addr_elements; + storage_values.push(faucet_account_id.prefix().as_felt()); + storage_values.push(faucet_account_id.suffix()); let note_storage = NoteStorage::new(storage_values)?; diff --git a/crates/miden-agglayer/src/errors/agglayer.rs b/crates/miden-agglayer/src/errors/agglayer.rs index 91e98d3725..3a8feda3fe 100644 --- a/crates/miden-agglayer/src/errors/agglayer.rs +++ b/crates/miden-agglayer/src/errors/agglayer.rs @@ -24,8 +24,8 @@ pub const ERR_CLAIM_TARGET_ACCT_MISMATCH: MasmError = MasmError::from_static_str /// Error Message: "CONFIG_AGG_BRIDGE note attachment target account does not match consuming account" pub const ERR_CONFIG_AGG_BRIDGE_TARGET_ACCOUNT_MISMATCH: MasmError = MasmError::from_static_str("CONFIG_AGG_BRIDGE note attachment target account does not match consuming account"); -/// Error Message: "CONFIG_AGG_BRIDGE expects exactly 2 note storage items" -pub const ERR_CONFIG_AGG_BRIDGE_UNEXPECTED_STORAGE_ITEMS: MasmError = MasmError::from_static_str("CONFIG_AGG_BRIDGE expects exactly 2 note storage items"); +/// Error Message: "CONFIG_AGG_BRIDGE expects exactly 7 note storage items" +pub const ERR_CONFIG_AGG_BRIDGE_UNEXPECTED_STORAGE_ITEMS: MasmError = MasmError::from_static_str("CONFIG_AGG_BRIDGE expects exactly 7 note storage items"); /// Error Message: "faucet is not registered in the bridge's faucet registry" pub const ERR_FAUCET_NOT_REGISTERED: MasmError = MasmError::from_static_str("faucet is not registered in the bridge's faucet registry"); @@ -68,6 +68,9 @@ pub const ERR_SENDER_NOT_GER_MANAGER: MasmError = MasmError::from_static_str("no /// Error Message: "merkle proof verification failed: provided SMT root does not match the computed root" pub const ERR_SMT_ROOT_VERIFICATION_FAILED: MasmError = MasmError::from_static_str("merkle proof verification failed: provided SMT root does not match the computed root"); +/// Error Message: "token address is not registered in the bridge's token registry" +pub const ERR_TOKEN_NOT_REGISTERED: MasmError = MasmError::from_static_str("token address is not registered in the bridge's token registry"); + /// Error Message: "x < y*10^s (underflow detected)" pub const ERR_UNDERFLOW: MasmError = MasmError::from_static_str("x < y*10^s (underflow detected)"); diff --git a/crates/miden-agglayer/src/lib.rs b/crates/miden-agglayer/src/lib.rs index 21070e557b..8918fc0eb0 100644 --- a/crates/miden-agglayer/src/lib.rs +++ b/crates/miden-agglayer/src/lib.rs @@ -131,6 +131,10 @@ static FAUCET_REGISTRY_SLOT_NAME: LazyLock = LazyLock::new(|| { StorageSlotName::new("miden::agglayer::bridge::faucet_registry") .expect("faucet registry storage slot name should be valid") }); +static TOKEN_REGISTRY_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::agglayer::bridge::token_registry") + .expect("token registry storage slot name should be valid") +}); static BRIDGE_ADMIN_SLOT_NAME: LazyLock = LazyLock::new(|| { StorageSlotName::new("miden::agglayer::bridge::admin") .expect("bridge admin storage slot name should be valid") @@ -160,6 +164,7 @@ static GER_MANAGER_SLOT_NAME: LazyLock = LazyLock::new(|| { /// - [`Self::ler_hi_slot_name`]: Stores the upper 32 bits of the LET root. /// - [`Self::let_num_leaves_slot_name`]: Stores the number of leaves in the LET frontier. /// - [`Self::faucet_registry_slot_name`]: Stores the faucet registry map. +/// - [`Self::token_registry_slot_name`]: Stores the token address → faucet ID map. /// - [`Self::bridge_admin_slot_name`]: Stores the bridge admin account ID. /// - [`Self::ger_manager_slot_name`]: Stores the GER manager account ID. /// @@ -207,6 +212,11 @@ impl AggLayerBridge { &FAUCET_REGISTRY_SLOT_NAME } + /// Storage slot name for the token registry map. + pub fn token_registry_slot_name() -> &'static StorageSlotName { + &TOKEN_REGISTRY_SLOT_NAME + } + /// Storage slot name for the bridge admin account ID. pub fn bridge_admin_slot_name() -> &'static StorageSlotName { &BRIDGE_ADMIN_SLOT_NAME @@ -240,6 +250,7 @@ impl From for AccountComponent { StorageSlot::with_value(LET_ROOT_HI_SLOT_NAME.clone(), Word::empty()), StorageSlot::with_value(LET_NUM_LEAVES_SLOT_NAME.clone(), Word::empty()), StorageSlot::with_empty_map(FAUCET_REGISTRY_SLOT_NAME.clone()), + StorageSlot::with_empty_map(TOKEN_REGISTRY_SLOT_NAME.clone()), StorageSlot::with_value(BRIDGE_ADMIN_SLOT_NAME.clone(), bridge_admin_word), StorageSlot::with_value(GER_MANAGER_SLOT_NAME.clone(), ger_manager_word), ]; @@ -311,13 +322,17 @@ static CONVERSION_INFO_2_SLOT_NAME: LazyLock = LazyLock::new(|| StorageSlotName::new("miden::agglayer::faucet::conversion_info_2") .expect("conversion info 2 storage slot name should be valid") }); +static OWNER_CONFIG_SLOT_NAME: LazyLock = LazyLock::new(|| { + StorageSlotName::new("miden::standards::access::ownable::owner_config") + .expect("owner config storage slot name should be valid") +}); /// An [`AccountComponent`] implementing the AggLayer Faucet. /// /// It reexports the procedures from `miden::agglayer::faucet`. When linking against this /// component, the `agglayer` library must be available to the assembler. /// The procedures of this component are: -/// - `claim`, which validates a CLAIM note against one of the stored GERs in the bridge. +/// - `distribute`, which mints assets and creates output notes (with owner verification). /// - `asset_to_origin_asset`, which converts an asset to the origin asset (used in FPI from /// bridge). /// - `burn`, which burns an asset. @@ -329,6 +344,7 @@ static CONVERSION_INFO_2_SLOT_NAME: LazyLock = LazyLock::new(|| /// - [`Self::conversion_info_1_slot`]: Stores the first 4 felts of the origin token address. /// - [`Self::conversion_info_2_slot`]: Stores the remaining 5th felt of the origin token address + /// origin network + scale. +/// - [`Self::owner_config_slot`]: Stores the owner account ID (bridge) for MINT note authorization. #[derive(Debug, Clone)] pub struct AggLayerFaucet { metadata: TokenMetadata, @@ -394,6 +410,11 @@ impl AggLayerFaucet { pub fn conversion_info_2_slot() -> &'static StorageSlotName { &CONVERSION_INFO_2_SLOT_NAME } + + /// Storage slot name for the owner account ID (bridge) used by `ownable::verify_owner`. + pub fn owner_config_slot() -> &'static StorageSlotName { + &OWNER_CONFIG_SLOT_NAME + } } impl From for AccountComponent { @@ -419,8 +440,17 @@ impl From for AccountComponent { let conversion_slot2 = StorageSlot::with_value(CONVERSION_INFO_2_SLOT_NAME.clone(), conversion_slot2_word); + // Owner config slot: bridge account ID as the owner for MINT note authorization + let owner_word = Word::new([ + Felt::ZERO, + Felt::ZERO, + faucet.bridge_account_id.suffix(), + faucet.bridge_account_id.prefix().as_felt(), + ]); + let owner_slot = StorageSlot::with_value(OWNER_CONFIG_SLOT_NAME.clone(), owner_word); + let agglayer_storage_slots = - vec![metadata_slot, bridge_slot, conversion_slot1, conversion_slot2]; + vec![metadata_slot, bridge_slot, conversion_slot1, conversion_slot2, owner_slot]; agglayer_faucet_component(agglayer_storage_slots) } } diff --git a/crates/miden-testing/tests/agglayer/bridge_in.rs b/crates/miden-testing/tests/agglayer/bridge_in.rs index 1d579d3218..11e738f7d9 100644 --- a/crates/miden-testing/tests/agglayer/bridge_in.rs +++ b/crates/miden-testing/tests/agglayer/bridge_in.rs @@ -7,6 +7,7 @@ use anyhow::Context; use miden_agglayer::claim_note::Keccak256Output; use miden_agglayer::{ ClaimNoteStorage, + ConfigAggBridgeNote, ExitRoot, SmtNode, UpdateGerNote, @@ -91,7 +92,13 @@ fn merkle_proof_verification_code( ) } -/// Tests the bridge-in flow: CLAIM note -> Aggfaucet (FPI to Bridge) -> P2ID note created. +/// Tests the bridge-in flow with the new 2-transaction architecture: +/// +/// TX0: CONFIG_AGG_BRIDGE → bridge (registers faucet + token address in registries) +/// TX1: UPDATE_GER → bridge (stores GER) +/// TX2: CLAIM → bridge (validates proof, creates MINT note) +/// TX3: MINT → aggfaucet (mints asset, creates P2ID note) +/// TX4: P2ID → destination (simulated case only) /// /// Parameterized over two claim data sources: /// - [`ClaimDataSource::Real`]: uses real [`ProofData`] and [`LeafData`] from @@ -99,9 +106,6 @@ fn merkle_proof_verification_code( /// - [`ClaimDataSource::Simulated`]: uses locally generated [`ProofData`] and [`LeafData`] from /// `claim_asset_vectors_local_tx.json`, produced by simulating a `bridgeAsset()` call. /// -/// In both cases the claim note is processed against the agglayer faucet, which validates the -/// Merkle proof and creates a P2ID note for the destination address. -/// /// Note: Modifying anything in the real test vectors would invalidate the Merkle proof, /// as the proof was computed for the original leaf data including the original destination. #[rstest::rstest] @@ -113,7 +117,7 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a let mut builder = MockChain::builder(); - // CREATE BRIDGE ADMIN ACCOUNT (not used in this test, but distinct from GER manager) + // CREATE BRIDGE ADMIN ACCOUNT (sends CONFIG_AGG_BRIDGE notes) // -------------------------------------------------------------------------------------------- let bridge_admin = builder.add_existing_wallet(Auth::BasicAuth { auth_scheme: AuthScheme::Falcon512Rpo })?; @@ -196,7 +200,7 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a AccountState::Exists, )?; - // CREATE CLAIM NOTE + // CREATE CLAIM NOTE (now targets the bridge, not the faucet) // -------------------------------------------------------------------------------------------- // The P2ID serial number is derived from the PROOF_DATA_KEY (RPO hash of proof data) @@ -216,7 +220,7 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a let claim_note = create_claim_note( claim_inputs, - agglayer_faucet.id(), + bridge_account.id(), // Target the bridge, not the faucet sender_account.id(), builder.rng_mut(), )?; @@ -224,6 +228,17 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a // Add the claim note to the builder before building the mock chain builder.add_output_note(OutputNote::Full(claim_note.clone())); + // CREATE CONFIG_AGG_BRIDGE NOTE (registers faucet + token address in bridge) + // -------------------------------------------------------------------------------------------- + let config_note = ConfigAggBridgeNote::create( + agglayer_faucet.id(), + &origin_token_address, + bridge_admin.id(), + bridge_account.id(), + builder.rng_mut(), + )?; + builder.add_output_note(OutputNote::Full(config_note.clone())); + // CREATE UPDATE_GER NOTE WITH GLOBAL EXIT ROOT // -------------------------------------------------------------------------------------------- let update_ger_note = @@ -234,7 +249,17 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a // -------------------------------------------------------------------------------------------- let mut mock_chain = builder.clone().build()?; - // EXECUTE UPDATE_GER NOTE TO STORE GER IN BRIDGE ACCOUNT + // TX0: EXECUTE CONFIG_AGG_BRIDGE NOTE TO REGISTER FAUCET IN BRIDGE + // -------------------------------------------------------------------------------------------- + let config_tx_context = mock_chain + .build_tx_context(bridge_account.id(), &[config_note.id()], &[])? + .build()?; + let config_executed = config_tx_context.execute().await?; + + mock_chain.add_pending_executed_transaction(&config_executed)?; + mock_chain.prove_next_block()?; + + // TX1: EXECUTE UPDATE_GER NOTE TO STORE GER IN BRIDGE ACCOUNT // -------------------------------------------------------------------------------------------- let update_ger_tx_context = mock_chain .build_tx_context(bridge_account.id(), &[update_ger_note.id()], &[])? @@ -244,23 +269,40 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a mock_chain.add_pending_executed_transaction(&update_ger_executed)?; mock_chain.prove_next_block()?; - // EXECUTE CLAIM NOTE AGAINST AGGLAYER FAUCET (with FPI to Bridge) + // TX2: EXECUTE CLAIM NOTE AGAINST BRIDGE (validates proof, creates MINT note) // -------------------------------------------------------------------------------------------- - let foreign_account_inputs = mock_chain.get_foreign_account_inputs(bridge_account.id())?; + let claim_tx_context = + mock_chain.build_tx_context(bridge_account.id(), &[], &[claim_note])?.build()?; + + let claim_executed = claim_tx_context.execute().await?; - let tx_context = mock_chain - .build_tx_context(agglayer_faucet.id(), &[], &[claim_note])? - .foreign_accounts(vec![foreign_account_inputs]) + // VERIFY MINT NOTE WAS CREATED BY THE BRIDGE + // -------------------------------------------------------------------------------------------- + assert_eq!(claim_executed.output_notes().num_notes(), 1); + let mint_output_note = claim_executed.output_notes().get_note(0); + + // Verify the MINT note was sent by the bridge + assert_eq!(mint_output_note.metadata().sender(), bridge_account.id()); + assert_eq!(mint_output_note.metadata().note_type(), NoteType::Public); + + // Commit the CLAIM transaction and prove the block so the MINT note can be consumed + mock_chain.add_pending_executed_transaction(&claim_executed)?; + mock_chain.prove_next_block()?; + + // TX3: EXECUTE MINT NOTE AGAINST AGGFAUCET (mints asset, creates P2ID note) + // -------------------------------------------------------------------------------------------- + let mint_tx_context = mock_chain + .build_tx_context(agglayer_faucet.id(), &[mint_output_note.id()], &[])? .build()?; - let executed_transaction = tx_context.execute().await?; + let mint_executed = mint_tx_context.execute().await?; - // VERIFY P2ID NOTE WAS CREATED + // VERIFY P2ID NOTE WAS CREATED BY THE FAUCET // -------------------------------------------------------------------------------------------- // Check that exactly one P2ID note was created by the faucet - assert_eq!(executed_transaction.output_notes().num_notes(), 1); - let output_note = executed_transaction.output_notes().get_note(0); + assert_eq!(mint_executed.output_notes().num_notes(), 1); + let output_note = mint_executed.output_notes().get_note(0); // Verify note metadata properties assert_eq!(output_note.metadata().sender(), agglayer_faucet.id()); @@ -302,14 +344,14 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a assert_eq!(OutputNote::Full(expected_output_p2id_note.clone()), *output_note); - // CONSUME THE P2ID NOTE WITH THE DESTINATION ACCOUNT (simulated case only) + // TX4: CONSUME THE P2ID NOTE WITH THE DESTINATION ACCOUNT (simulated case only) // -------------------------------------------------------------------------------------------- // For the simulated case, we control the destination account and can verify the full // end-to-end flow including P2ID consumption and balance updates. if let Some(destination_account) = destination_account { // Add the faucet transaction to the chain and prove the next block so the P2ID note is // committed and can be consumed. - mock_chain.add_pending_executed_transaction(&executed_transaction)?; + mock_chain.add_pending_executed_transaction(&mint_executed)?; mock_chain.prove_next_block()?; // Execute the consume transaction for the destination account diff --git a/crates/miden-testing/tests/agglayer/bridge_out.rs b/crates/miden-testing/tests/agglayer/bridge_out.rs index 8b40e17584..a1f6b02e09 100644 --- a/crates/miden-testing/tests/agglayer/bridge_out.rs +++ b/crates/miden-testing/tests/agglayer/bridge_out.rs @@ -146,6 +146,7 @@ async fn bridge_out_consecutive() -> anyhow::Result<()> { // CONFIG_AGG_BRIDGE note to register the faucet in the bridge (sent by bridge admin) let config_note = ConfigAggBridgeNote::create( faucet.id(), + &origin_token_address, bridge_admin.id(), bridge_account.id(), builder.rng_mut(), diff --git a/crates/miden-testing/tests/agglayer/config_bridge.rs b/crates/miden-testing/tests/agglayer/config_bridge.rs index b9f7dcfbbc..3a56f0f5a1 100644 --- a/crates/miden-testing/tests/agglayer/config_bridge.rs +++ b/crates/miden-testing/tests/agglayer/config_bridge.rs @@ -3,6 +3,7 @@ extern crate alloc; use miden_agglayer::{ AggLayerBridge, ConfigAggBridgeNote, + EthAddressFormat, create_existing_bridge_account, faucet_registry_key, }; @@ -60,13 +61,19 @@ async fn test_config_agg_bridge_registers_faucet() -> anyhow::Result<()> { ); // CREATE CONFIG_AGG_BRIDGE NOTE + // Use a dummy origin token address for this test + let origin_token_address = EthAddressFormat::from_hex("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); let config_note = ConfigAggBridgeNote::create( faucet_to_register, + &origin_token_address, bridge_admin.id(), bridge_account.id(), builder.rng_mut(), )?; + println!("origin token address as vec: {:?}", origin_token_address.to_elements()); + println!("faucet id: {:?} {:?}", faucet_to_register.prefix(), faucet_to_register.suffix()); + builder.add_output_note(OutputNote::Full(config_note.clone())); let mock_chain = builder.build()?; From 79d2cac5e0514730a3f2a11338f94761ab30b497 Mon Sep 17 00:00:00 2001 From: riemann Date: Fri, 27 Feb 2026 21:25:32 +0300 Subject: [PATCH 02/29] wip: build MINT note from aggbridge --- .../asm/agglayer/bridge/bridge_config.masm | 23 ++++++++++++------- .../asm/agglayer/bridge/bridge_in.masm | 15 ++++++------ .../asm/note_scripts/CLAIM.masm | 8 ++++++- .../asm/note_scripts/CONFIG_AGG_BRIDGE.masm | 2 -- .../miden-testing/tests/agglayer/bridge_in.rs | 3 +++ crates/miden-testing/tests/scripts/faucet.rs | 6 +++++ 6 files changed, 38 insertions(+), 19 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm index 26b28f69cd..3da74af354 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm @@ -119,23 +119,25 @@ pub proc register_faucet exec.assert_sender_is_bridge_admin # => [origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] - push.111 debug.stack drop - # Save faucet ID for later use in token_registry dup.6 dup.6 # => [faucet_id_prefix, faucet_id_suffix, origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] # --- 1. Register faucet in faucet_registry --- # set_map_item expects [slot_id(2), KEY(4), VALUE(4)] and returns [OLD_VALUE(4)]. - push.IS_FAUCET_REGISTERED_FLAG - # => [1, faucet_id_prefix, faucet_id_suffix, origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] - movdn.7 + push.0.0 movdn.3 movdn.2 + # => [[faucet_id_prefix, faucet_id_suffix, 0, 0], origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] + + push.IS_FAUCET_REGISTERED_FLAG.0.0.0 + # => [[0, 0, 0, 1], [faucet_id_prefix, faucet_id_suffix, 0, 0], origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] + + swapw # => [[faucet_id_prefix, faucet_id_suffix, 0, 0], [0, 0, 0, 1], origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] push.FAUCET_REGISTRY_SLOT[0..2] exec.native_account::set_map_item - # => [OLD_VALUE(4), origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] + # => [OLD_VALUE, origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] dropw # => [origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] @@ -214,8 +216,13 @@ pub proc lookup_faucet_by_token_address # => [VALUE(4)] = [faucet_id_prefix, faucet_id_suffix, 0, 0] # Assert the token is registered: faucet_id_prefix is always non-zero for valid account IDs. - # Check that at least one of prefix/suffix is non-zero (unregistered entries return all zeros). - dup dup.2 or assert.err=ERR_TOKEN_NOT_REGISTERED + dup.1 dup.1 + # => [faucet_id_prefix, faucet_id_suffix, faucet_id_prefix, faucet_id_suffix, 0, 0] + + eq.0 swap eq.0 and + # => [is_both_zero, faucet_id_prefix, faucet_id_suffix, 0, 0] + + assertz.err=ERR_TOKEN_NOT_REGISTERED # => [faucet_id_prefix, faucet_id_suffix, 0, 0] movup.2 drop movup.2 drop diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index 9c9469294c..317faca775 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -104,12 +104,11 @@ const BUILD_MINT_PREFIX_MEM_LOC = 8 const BUILD_MINT_FAUCET_PREFIX_LOC = 10 const BUILD_MINT_FAUCET_SUFFIX_LOC = 11 -# MINT note storage layout (private mode, 12 items): -# [tag, amount, attachment_kind, attachment_scheme, ATTACHMENT(4), RECIPIENT_DIGEST(4)] -const MINT_NOTE_NUM_STORAGE_ITEMS = 12 +# MINT note storage layout (public mode, 18 items): +const MINT_NOTE_NUM_STORAGE_ITEMS = 18 const MINT_NOTE_TYPE_PUBLIC = 1 -# P2ID output note constants +# MINT note output note constants const P2ID_NOTE_NUM_STORAGE_ITEMS = 2 const OUTPUT_NOTE_TYPE_PUBLIC = 1 @@ -504,11 +503,11 @@ end #! #! Invocation: exec proc get_origin_token_address - mem_load.ORIGIN_TOKEN_ADDRESS_0 - mem_load.ORIGIN_TOKEN_ADDRESS_1 - mem_load.ORIGIN_TOKEN_ADDRESS_2 - mem_load.ORIGIN_TOKEN_ADDRESS_3 mem_load.ORIGIN_TOKEN_ADDRESS_4 + mem_load.ORIGIN_TOKEN_ADDRESS_3 + mem_load.ORIGIN_TOKEN_ADDRESS_2 + mem_load.ORIGIN_TOKEN_ADDRESS_1 + mem_load.ORIGIN_TOKEN_ADDRESS_0 # => [origin_token_addr(5)] end diff --git a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm index 334ba1e1bf..da40de4910 100644 --- a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm +++ b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm @@ -158,11 +158,17 @@ begin movdn.8 # => [PROOF_DATA_KEY, LEAF_DATA_KEY, faucet_mint_amount, pad(16)] + push.111 debug.stack drop + # call the Bridge Claim procedure call.bridge::claim # => [pad(16), pad(7)] + sdepth push.666 debug.stack drop drop + # a call invocation consumes and returns 16 elements, but we had trailing padding - dropw drop drop drop + dropw dropw dropw + + sdepth push.777 debug.stack drop drop # => [pad(16)] end diff --git a/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm b/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm index 1ee966648c..eeab846cf8 100644 --- a/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm +++ b/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm @@ -67,8 +67,6 @@ begin padw mem_loadw_le.STORAGE_START_PTR # => [addr4, addr3, addr2, addr1, addr0, faucet_id_prefix, faucet_id_suffix, pad(12)] - debug.stack - # Register the faucet in the bridge call.bridge_config::register_faucet # => [pad(16)] diff --git a/crates/miden-testing/tests/agglayer/bridge_in.rs b/crates/miden-testing/tests/agglayer/bridge_in.rs index 11e738f7d9..d06ee5d923 100644 --- a/crates/miden-testing/tests/agglayer/bridge_in.rs +++ b/crates/miden-testing/tests/agglayer/bridge_in.rs @@ -163,6 +163,9 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a ); builder.add_account(agglayer_faucet.clone())?; + println!("origin address: {:?}", leaf_data.origin_token_address.to_elements()); + println!("agg faucet id: {:?} {:?}", agglayer_faucet.id().prefix(), agglayer_faucet.id().suffix()); + // Get the destination account ID from the leaf data. // This requires the destination_address to be in the embedded Miden AccountId format // (first 4 bytes must be zero). diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 4f8afa26f9..826e5ea42d 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -597,6 +597,9 @@ async fn network_faucet_mint() -> anyhow::Result<()> { &mut rng, )?; + println!("mint note script: {:?}", mint_note.script().root()); + println!("mint note storage: {:?}", mint_note.storage().to_elements()); + // Add the MINT note to the mock chain builder.add_output_note(OutputNote::Full(mint_note.clone())); let mut mock_chain = builder.build()?; @@ -1258,6 +1261,9 @@ async fn test_mint_note_output_note_types(#[case] note_type: NoteType) -> anyhow &mut rng, )?; + println!("mint note public storage: {:?}", mint_note.storage().to_elements()); + println!("num elements: {:?}", mint_note.storage().num_items()); + builder.add_output_note(OutputNote::Full(mint_note.clone())); let mut mock_chain = builder.build()?; From d9517400f36b5b2c5aa531fc04edfd1c35a18e19 Mon Sep 17 00:00:00 2001 From: riemann Date: Fri, 27 Feb 2026 21:49:36 +0300 Subject: [PATCH 03/29] wip: created MINT note encodes incorrect public P2ID or public P2ID not in DataStore --- .../asm/agglayer/bridge/bridge_in.masm | 165 +++++++++--------- .../asm/standards/notes/mint.masm | 6 + .../miden-testing/tests/agglayer/bridge_in.rs | 8 +- .../tests/agglayer/config_bridge.rs | 3 +- 4 files changed, 100 insertions(+), 82 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index 317faca775..76a285f843 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -101,14 +101,18 @@ const CLAIM_AMOUNT_MEM_LOC_1 = 4 const BUILD_MINT_AMOUNT_MEM_LOC_0 = 0 const BUILD_MINT_AMOUNT_MEM_LOC_1 = 4 const BUILD_MINT_PREFIX_MEM_LOC = 8 +const BUILD_MINT_SUFFIX_MEM_LOC = 9 const BUILD_MINT_FAUCET_PREFIX_LOC = 10 const BUILD_MINT_FAUCET_SUFFIX_LOC = 11 # MINT note storage layout (public mode, 18 items): +# [0]: tag, [1]: amount, [2]: attachment_kind, [3]: attachment_scheme, +# [4-7]: ATTACHMENT, [8-11]: P2ID_SCRIPT_ROOT, [12-15]: SERIAL_NUM, +# [16]: account_id_suffix, [17]: account_id_prefix const MINT_NOTE_NUM_STORAGE_ITEMS = 18 const MINT_NOTE_TYPE_PUBLIC = 1 -# MINT note output note constants +# P2ID output note constants const P2ID_NOTE_NUM_STORAGE_ITEMS = 2 const OUTPUT_NOTE_TYPE_PUBLIC = 1 @@ -511,15 +515,19 @@ proc get_origin_token_address # => [origin_token_addr(5)] end -#! Builds a MINT output note targeting the aggfaucet. +#! Builds a PUBLIC MINT output note targeting the aggfaucet. #! -#! The MINT note uses private mode (12 storage items) and contains: -#! - tag: Note tag targeting the faucet account -#! - amount: The scaled-down Miden amount to mint -#! - attachment_kind: 0 (no attachment) -#! - attachment_scheme: 0 (no attachment) -#! - ATTACHMENT: [0, 0, 0, 0] -#! - RECIPIENT_DIGEST: The P2ID recipient digest for the destination account +#! The MINT note uses public mode (18 storage items) so the faucet creates a PUBLIC P2ID note. +#! Storage layout: +#! - [0]: tag (note tag for the P2ID output note, targeting the destination account) +#! - [1]: amount (the scaled-down Miden amount to mint) +#! - [2]: attachment_kind (0 = no attachment) +#! - [3]: attachment_scheme (0 = no attachment) +#! - [4-7]: ATTACHMENT ([0, 0, 0, 0]) +#! - [8-11]: P2ID_SCRIPT_ROOT (script root of the P2ID note) +#! - [12-15]: SERIAL_NUM (serial number for the P2ID note, derived from PROOF_DATA_KEY) +#! - [16]: account_id_suffix (destination account suffix) +#! - [17]: account_id_prefix (destination account prefix) #! #! Inputs: [prefix, suffix, AMOUNT[0], AMOUNT[1]] #! Outputs: [] @@ -527,132 +535,129 @@ end #! Where: #! - prefix, suffix: destination account ID #! - AMOUNT[0], AMOUNT[1]: raw U256 claim amount (8 felts) -@locals(12) # 0-3: amount_lo, 4-7: amount_hi, 8: prefix, 9: faucet_prefix, 10: faucet_suffix, 11: unused +@locals(12) # 0-3: amount_lo, 4-7: amount_hi, 8: prefix, 9: suffix, 10: faucet_prefix, 11: faucet_suffix proc build_mint_output_note - # save prefix to local memory for later use in note tag computation + # save prefix and suffix to local memory for later use dup loc_store.BUILD_MINT_PREFIX_MEM_LOC + swap dup loc_store.BUILD_MINT_SUFFIX_MEM_LOC swap + # => [prefix, suffix, AMOUNT[0], AMOUNT[1], ...] - # write destination account id into memory for use in note::build_recipient - push.P2ID_RECIPIENT_STORAGE_MEM_ADDR add.1 mem_store mem_store.P2ID_RECIPIENT_STORAGE_MEM_ADDR + # drop prefix and suffix from stack (already saved in locals) + swap drop drop + # => [AMOUNT[0], AMOUNT[1], ...] # store amount in memory locals loc_storew_be.BUILD_MINT_AMOUNT_MEM_LOC_0 dropw loc_storew_be.BUILD_MINT_AMOUNT_MEM_LOC_1 dropw # => [pad(16)] - # Build P2ID recipient digest for the MINT note storage - # First, get the P2ID script root - procref.::miden::standards::notes::p2id::main - # => [SCRIPT_ROOT] - - # Use PROOF_DATA_KEY as the P2ID serial number - swapw mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR - # => [SERIAL_NUM, SCRIPT_ROOT] - - push.P2ID_NOTE_NUM_STORAGE_ITEMS - # => [note_num_storage_items, SERIAL_NUM, SCRIPT_ROOT] - - push.P2ID_RECIPIENT_STORAGE_MEM_ADDR - # => [storage_ptr, note_num_storage_items, SERIAL_NUM, SCRIPT_ROOT] - - exec.note::build_recipient - # => [RECIPIENT_DIGEST] - - # Now build the MINT note storage in memory - # Layout: [tag, amount, attachment_kind(0), attachment_scheme(0), ATTACHMENT(0,0,0,0), RECIPIENT_DIGEST(4)] + # Get the native amount from the pre-computed miden_claim_amount + mem_load.CLAIM_OUTPUT_NOTE_FAUCET_AMOUNT + # => [native_amount, pad(16)] # Compute the note tag for the destination account loc_load.BUILD_MINT_PREFIX_MEM_LOC - # => [dest_prefix, RECIPIENT_DIGEST] + # => [dest_prefix, native_amount, pad(16)] exec.note_tag::create_account_target - # => [dest_tag, RECIPIENT_DIGEST] - - # Verify the U256 amount converts correctly to the native amount - padw loc_loadw_be.BUILD_MINT_AMOUNT_MEM_LOC_1 padw loc_loadw_be.BUILD_MINT_AMOUNT_MEM_LOC_0 - # => [AMOUNT[0], AMOUNT[1], dest_tag, RECIPIENT_DIGEST] + # => [dest_tag, native_amount, pad(16)] - mem_load.CLAIM_OUTPUT_NOTE_FAUCET_AMOUNT movdn.8 - # => [AMOUNT[0], AMOUNT[1], native_amount, dest_tag, RECIPIENT_DIGEST] - - # We need the scale factor. Since the bridge doesn't store it, we use the - # pre-computed miden_claim_amount from the CLAIM note storage directly. - # The native_amount was already verified by the CLAIM note creator. - # Drop the U256 amount and use native_amount directly. - repeat.8 swap drop end - # => [native_amount, dest_tag, RECIPIENT_DIGEST] - - # Store MINT note storage items in memory: + # Store MINT note storage items in memory (public mode, 18 items): # [0]: dest_tag (tag for the P2ID output note) # [1]: native_amount # [2]: attachment_kind = 0 # [3]: attachment_scheme = 0 # [4..7]: ATTACHMENT = [0, 0, 0, 0] - # [8..11]: RECIPIENT_DIGEST + # [8..11]: P2ID_SCRIPT_ROOT + # [12..15]: SERIAL_NUM (PROOF_DATA_KEY) + # [16]: account_id_suffix + # [17]: account_id_prefix - # Write tag to MINT note storage + # Write tag to MINT note storage [0] + # Stack: [dest_tag, native_amount, ...] push.MINT_NOTE_STORAGE_MEM_ADDR mem_store - # => [native_amount, RECIPIENT_DIGEST] + # => [native_amount, pad(16)] - # Write amount to MINT note storage + # Write amount to MINT note storage [1] push.MINT_NOTE_STORAGE_MEM_ADDR add.1 mem_store - # => [RECIPIENT_DIGEST] + # => [pad(16)] - # Write attachment_kind = 0 + # Write attachment_kind = 0 [2] push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.2 mem_store - # => [RECIPIENT_DIGEST] + # => [pad(16)] - # Write attachment_scheme = 0 + # Write attachment_scheme = 0 [3] push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.3 mem_store - # => [RECIPIENT_DIGEST] + # => [pad(16)] - # Write ATTACHMENT = [0, 0, 0, 0] + # Write ATTACHMENT = [0, 0, 0, 0] [4..7] push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.4 mem_store push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.5 mem_store push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.6 mem_store push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.7 mem_store - # => [RECIPIENT_DIGEST] + # => [pad(16)] + + # Write P2ID_SCRIPT_ROOT to MINT note storage [8..11] + procref.::miden::standards::notes::p2id::main + # => [P2ID_SCRIPT_ROOT(4), pad(16)] - # Write RECIPIENT_DIGEST to MINT note storage [8..11] - # Stack: [r0, r1, r2, r3] - push.MINT_NOTE_STORAGE_MEM_ADDR add.11 mem_store - push.MINT_NOTE_STORAGE_MEM_ADDR add.10 mem_store - push.MINT_NOTE_STORAGE_MEM_ADDR add.9 mem_store push.MINT_NOTE_STORAGE_MEM_ADDR add.8 mem_store - # => [] + push.MINT_NOTE_STORAGE_MEM_ADDR add.9 mem_store + push.MINT_NOTE_STORAGE_MEM_ADDR add.10 mem_store + push.MINT_NOTE_STORAGE_MEM_ADDR add.11 mem_store + # => [pad(16)] + + # Write SERIAL_NUM (PROOF_DATA_KEY) to MINT note storage [12..15] + mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR + # => [SERIAL_NUM(4), pad(16)] + + push.MINT_NOTE_STORAGE_MEM_ADDR add.12 mem_store + push.MINT_NOTE_STORAGE_MEM_ADDR add.13 mem_store + push.MINT_NOTE_STORAGE_MEM_ADDR add.14 mem_store + push.MINT_NOTE_STORAGE_MEM_ADDR add.15 mem_store + # => [pad(16)] + + # Write P2ID storage items: [account_id_suffix, account_id_prefix] [16..17] + loc_load.BUILD_MINT_SUFFIX_MEM_LOC + push.MINT_NOTE_STORAGE_MEM_ADDR add.16 mem_store + # => [pad(16)] + + loc_load.BUILD_MINT_PREFIX_MEM_LOC + push.MINT_NOTE_STORAGE_MEM_ADDR add.17 mem_store + # => [pad(16)] # Now create the MINT output note # Get the MINT note script root procref.::miden::standards::notes::mint::main - # => [MINT_SCRIPT_ROOT] + # => [MINT_SCRIPT_ROOT(4), pad(16)] # Generate a serial number for the MINT note (use PROOF_DATA_KEY) swapw mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR - # => [SERIAL_NUM, MINT_SCRIPT_ROOT] + # => [MINT_SERIAL_NUM(4), MINT_SCRIPT_ROOT(4), pad(16)] # Build the MINT note recipient push.MINT_NOTE_NUM_STORAGE_ITEMS - # => [num_storage_items, SERIAL_NUM, MINT_SCRIPT_ROOT] + # => [num_storage_items, MINT_SERIAL_NUM, MINT_SCRIPT_ROOT, pad(16)] push.MINT_NOTE_STORAGE_MEM_ADDR - # => [storage_ptr, num_storage_items, SERIAL_NUM, MINT_SCRIPT_ROOT] + # => [storage_ptr, num_storage_items, MINT_SERIAL_NUM, MINT_SCRIPT_ROOT, pad(16)] exec.note::build_recipient - # => [MINT_RECIPIENT] + # => [MINT_RECIPIENT(4), pad(16)] # Create the MINT output note targeting the faucet push.MINT_NOTE_TYPE_PUBLIC - # => [note_type, MINT_RECIPIENT] + # => [note_type, MINT_RECIPIENT, pad(16)] # Compute note tag targeting the faucet account loc_load.BUILD_MINT_FAUCET_PREFIX_LOC - # => [faucet_prefix, note_type, MINT_RECIPIENT] + # => [faucet_prefix, note_type, MINT_RECIPIENT, pad(16)] exec.note_tag::create_account_target - # => [faucet_tag, note_type, MINT_RECIPIENT] + # => [faucet_tag, note_type, MINT_RECIPIENT, pad(16)] # Create the output note (no assets - MINT notes carry no assets) exec.output_note::create - # => [note_idx] + # => [note_idx, pad(16)] # Set the attachment on the MINT note to target the faucet account # NetworkAccountTarget attachment: targets the faucet so only it can consume the note @@ -661,17 +666,17 @@ proc build_mint_output_note push.1 # exec_hint = ALWAYS loc_load.BUILD_MINT_FAUCET_SUFFIX_LOC loc_load.BUILD_MINT_FAUCET_PREFIX_LOC - # => [faucet_prefix, faucet_suffix, exec_hint, note_idx] + # => [faucet_prefix, faucet_suffix, exec_hint, note_idx, pad(16)] exec.network_account_target::new - # => [attachment_scheme, attachment_kind, ATTACHMENT(4), note_idx] + # => [attachment_scheme, attachment_kind, ATTACHMENT(4), note_idx, pad(16)] # Rearrange for set_attachment: [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] movup.6 - # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT(4)] + # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT(4), pad(16)] exec.output_note::set_attachment - # => [] + # => [pad(16)] end #! Computes the root of the SMT based on the provided Merkle path, leaf value and leaf index. diff --git a/crates/miden-standards/asm/standards/notes/mint.masm b/crates/miden-standards/asm/standards/notes/mint.masm index 65e8a82a90..da5062fedf 100644 --- a/crates/miden-standards/asm/standards/notes/mint.masm +++ b/crates/miden-standards/asm/standards/notes/mint.masm @@ -73,6 +73,8 @@ pub proc main u32gte.MINT_NOTE_MIN_NUM_STORAGE_ITEMS_PUBLIC # => [is_public_output_note, num_storage_items, storage_ptr, pad(16)] + push.101 debug.stack drop + if.true # public output note creation # => [num_storage_items, storage_ptr, pad(16)] @@ -96,6 +98,8 @@ pub proc main exec.note::build_recipient # => [RECIPIENT, pad(12)] + push.222 debug.stack drop + # push note_type, and load tag and amount push.OUTPUT_NOTE_TYPE_PUBLIC mem_load.0 mem_load.1 @@ -119,6 +123,8 @@ pub proc main end # => [amount, tag, note_type, RECIPIENT, pad(12)] + push.333 debug.stack drop + # distribute expects 9 pad elements, returns 15 and 12 are provided here. # so the total number of pads after calling is 12 + (15-9) = 18 call.network_faucet::distribute diff --git a/crates/miden-testing/tests/agglayer/bridge_in.rs b/crates/miden-testing/tests/agglayer/bridge_in.rs index d06ee5d923..47a6c05c76 100644 --- a/crates/miden-testing/tests/agglayer/bridge_in.rs +++ b/crates/miden-testing/tests/agglayer/bridge_in.rs @@ -164,7 +164,11 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a builder.add_account(agglayer_faucet.clone())?; println!("origin address: {:?}", leaf_data.origin_token_address.to_elements()); - println!("agg faucet id: {:?} {:?}", agglayer_faucet.id().prefix(), agglayer_faucet.id().suffix()); + println!( + "agg faucet id: {:?} {:?}", + agglayer_faucet.id().prefix(), + agglayer_faucet.id().suffix() + ); // Get the destination account ID from the leaf data. // This requires the destination_address to be in the embedded Miden AccountId format @@ -292,6 +296,8 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a mock_chain.add_pending_executed_transaction(&claim_executed)?; mock_chain.prove_next_block()?; + println!("tx 3"); + // TX3: EXECUTE MINT NOTE AGAINST AGGFAUCET (mints asset, creates P2ID note) // -------------------------------------------------------------------------------------------- let mint_tx_context = mock_chain diff --git a/crates/miden-testing/tests/agglayer/config_bridge.rs b/crates/miden-testing/tests/agglayer/config_bridge.rs index 3a56f0f5a1..9dc9b29c3f 100644 --- a/crates/miden-testing/tests/agglayer/config_bridge.rs +++ b/crates/miden-testing/tests/agglayer/config_bridge.rs @@ -62,7 +62,8 @@ async fn test_config_agg_bridge_registers_faucet() -> anyhow::Result<()> { // CREATE CONFIG_AGG_BRIDGE NOTE // Use a dummy origin token address for this test - let origin_token_address = EthAddressFormat::from_hex("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + let origin_token_address = + EthAddressFormat::from_hex("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); let config_note = ConfigAggBridgeNote::create( faucet_to_register, &origin_token_address, From ce6e21d12c31ce0b21c0f2443bbe002fca278787 Mon Sep 17 00:00:00 2001 From: riemann Date: Sat, 28 Feb 2026 01:18:01 +0300 Subject: [PATCH 04/29] feat: working e2e CLAIM flow reordering --- crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm | 4 ++++ crates/miden-standards/asm/standards/notes/mint.masm | 3 +++ crates/miden-testing/tests/agglayer/bridge_in.rs | 3 +++ crates/miden-testing/tests/scripts/faucet.rs | 2 ++ 4 files changed, 12 insertions(+) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index 76a285f843..84315988ce 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -600,6 +600,8 @@ proc build_mint_output_note procref.::miden::standards::notes::p2id::main # => [P2ID_SCRIPT_ROOT(4), pad(16)] + exec.word::reverse + push.MINT_NOTE_STORAGE_MEM_ADDR add.8 mem_store push.MINT_NOTE_STORAGE_MEM_ADDR add.9 mem_store push.MINT_NOTE_STORAGE_MEM_ADDR add.10 mem_store @@ -610,6 +612,8 @@ proc build_mint_output_note mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR # => [SERIAL_NUM(4), pad(16)] + exec.word::reverse + push.MINT_NOTE_STORAGE_MEM_ADDR add.12 mem_store push.MINT_NOTE_STORAGE_MEM_ADDR add.13 mem_store push.MINT_NOTE_STORAGE_MEM_ADDR add.14 mem_store diff --git a/crates/miden-standards/asm/standards/notes/mint.masm b/crates/miden-standards/asm/standards/notes/mint.masm index da5062fedf..d1e5811d49 100644 --- a/crates/miden-standards/asm/standards/notes/mint.masm +++ b/crates/miden-standards/asm/standards/notes/mint.masm @@ -95,6 +95,9 @@ pub proc main push.OUTPUT_PUBLIC_NOTE_STORAGE_ADDR # => [storage_ptr, num_output_note_storage, SERIAL_NUM, SCRIPT_ROOT, pad(8)] + + push.202 debug.stack drop + exec.note::build_recipient # => [RECIPIENT, pad(12)] diff --git a/crates/miden-testing/tests/agglayer/bridge_in.rs b/crates/miden-testing/tests/agglayer/bridge_in.rs index 47a6c05c76..2263c6a790 100644 --- a/crates/miden-testing/tests/agglayer/bridge_in.rs +++ b/crates/miden-testing/tests/agglayer/bridge_in.rs @@ -26,6 +26,7 @@ use miden_protocol::transaction::OutputNote; use miden_protocol::{Felt, FieldElement}; use miden_standards::account::wallets::BasicWallet; use miden_standards::code_builder::CodeBuilder; +use miden_standards::note::P2idNote; use miden_standards::testing::account_component::IncrNonceAuthComponent; use miden_testing::utils::create_p2id_note_exact; use miden_testing::{AccountState, Auth, MockChain, TransactionContextBuilder}; @@ -302,10 +303,12 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a // -------------------------------------------------------------------------------------------- let mint_tx_context = mock_chain .build_tx_context(agglayer_faucet.id(), &[mint_output_note.id()], &[])? + .add_note_script(P2idNote::script()) .build()?; let mint_executed = mint_tx_context.execute().await?; + println!("311"); // VERIFY P2ID NOTE WAS CREATED BY THE FAUCET // -------------------------------------------------------------------------------------------- diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 826e5ea42d..f78e988140 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -1234,6 +1234,8 @@ async fn test_mint_note_output_note_types(#[case] note_type: NoteType) -> anyhow ) .unwrap(); + println!("p2id script: {:?}", p2id_mint_output_note.script().root()); + // Create MINT note based on note type let mint_storage = match note_type { NoteType::Private => { From 57cfbb542384047d2f38ae6525036df2fd179b77 Mon Sep 17 00:00:00 2001 From: riemann Date: Sat, 28 Feb 2026 01:22:59 +0300 Subject: [PATCH 05/29] fix: cleanup rm debug.stack --- crates/miden-agglayer/asm/note_scripts/CLAIM.masm | 5 ----- crates/miden-standards/asm/standards/notes/mint.masm | 9 --------- crates/miden-testing/tests/agglayer/bridge_in.rs | 10 ---------- crates/miden-testing/tests/scripts/faucet.rs | 6 ------ 4 files changed, 30 deletions(-) diff --git a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm index da40de4910..a963b0e93f 100644 --- a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm +++ b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm @@ -158,17 +158,12 @@ begin movdn.8 # => [PROOF_DATA_KEY, LEAF_DATA_KEY, faucet_mint_amount, pad(16)] - push.111 debug.stack drop - # call the Bridge Claim procedure call.bridge::claim # => [pad(16), pad(7)] - sdepth push.666 debug.stack drop drop - # a call invocation consumes and returns 16 elements, but we had trailing padding dropw dropw dropw - sdepth push.777 debug.stack drop drop # => [pad(16)] end diff --git a/crates/miden-standards/asm/standards/notes/mint.masm b/crates/miden-standards/asm/standards/notes/mint.masm index d1e5811d49..65e8a82a90 100644 --- a/crates/miden-standards/asm/standards/notes/mint.masm +++ b/crates/miden-standards/asm/standards/notes/mint.masm @@ -73,8 +73,6 @@ pub proc main u32gte.MINT_NOTE_MIN_NUM_STORAGE_ITEMS_PUBLIC # => [is_public_output_note, num_storage_items, storage_ptr, pad(16)] - push.101 debug.stack drop - if.true # public output note creation # => [num_storage_items, storage_ptr, pad(16)] @@ -95,14 +93,9 @@ pub proc main push.OUTPUT_PUBLIC_NOTE_STORAGE_ADDR # => [storage_ptr, num_output_note_storage, SERIAL_NUM, SCRIPT_ROOT, pad(8)] - - push.202 debug.stack drop - exec.note::build_recipient # => [RECIPIENT, pad(12)] - push.222 debug.stack drop - # push note_type, and load tag and amount push.OUTPUT_NOTE_TYPE_PUBLIC mem_load.0 mem_load.1 @@ -126,8 +119,6 @@ pub proc main end # => [amount, tag, note_type, RECIPIENT, pad(12)] - push.333 debug.stack drop - # distribute expects 9 pad elements, returns 15 and 12 are provided here. # so the total number of pads after calling is 12 + (15-9) = 18 call.network_faucet::distribute diff --git a/crates/miden-testing/tests/agglayer/bridge_in.rs b/crates/miden-testing/tests/agglayer/bridge_in.rs index 2263c6a790..c3ca8a28e7 100644 --- a/crates/miden-testing/tests/agglayer/bridge_in.rs +++ b/crates/miden-testing/tests/agglayer/bridge_in.rs @@ -164,13 +164,6 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a ); builder.add_account(agglayer_faucet.clone())?; - println!("origin address: {:?}", leaf_data.origin_token_address.to_elements()); - println!( - "agg faucet id: {:?} {:?}", - agglayer_faucet.id().prefix(), - agglayer_faucet.id().suffix() - ); - // Get the destination account ID from the leaf data. // This requires the destination_address to be in the embedded Miden AccountId format // (first 4 bytes must be zero). @@ -297,8 +290,6 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a mock_chain.add_pending_executed_transaction(&claim_executed)?; mock_chain.prove_next_block()?; - println!("tx 3"); - // TX3: EXECUTE MINT NOTE AGAINST AGGFAUCET (mints asset, creates P2ID note) // -------------------------------------------------------------------------------------------- let mint_tx_context = mock_chain @@ -308,7 +299,6 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a let mint_executed = mint_tx_context.execute().await?; - println!("311"); // VERIFY P2ID NOTE WAS CREATED BY THE FAUCET // -------------------------------------------------------------------------------------------- diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index f78e988140..2db9f3b220 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -597,9 +597,6 @@ async fn network_faucet_mint() -> anyhow::Result<()> { &mut rng, )?; - println!("mint note script: {:?}", mint_note.script().root()); - println!("mint note storage: {:?}", mint_note.storage().to_elements()); - // Add the MINT note to the mock chain builder.add_output_note(OutputNote::Full(mint_note.clone())); let mut mock_chain = builder.build()?; @@ -1263,9 +1260,6 @@ async fn test_mint_note_output_note_types(#[case] note_type: NoteType) -> anyhow &mut rng, )?; - println!("mint note public storage: {:?}", mint_note.storage().to_elements()); - println!("num elements: {:?}", mint_note.storage().num_items()); - builder.add_output_note(OutputNote::Full(mint_note.clone())); let mut mock_chain = builder.build()?; From c8d1afb052c5536bcf95df50378b4d946188109c Mon Sep 17 00:00:00 2001 From: riemann Date: Mon, 2 Mar 2026 13:34:28 +0300 Subject: [PATCH 06/29] refactor: improve readability of MINT note memory addresses --- .../asm/agglayer/bridge/bridge_in.masm | 72 +++++++++---------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index 84315988ce..f25fab772a 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -87,9 +87,23 @@ const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_5 = 554 const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_6 = 555 const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_7 = 556 -# Memory for MINT note output construction -const MINT_NOTE_STORAGE_MEM_ADDR = 800 -const P2ID_RECIPIENT_STORAGE_MEM_ADDR = 0 +# Memory Addresses for MINT note output construction +const MINT_NOTE_STORAGE_MEM_ADDR_0 = 800 +const MINT_NOTE_STORAGE_DEST_TAG = 800 +const MINT_NOTE_STORAGE_NATIVE_AMOUNT = 801 +const MINT_NOTE_STORAGE_ATTACHMENT_KIND = 802 +const MINT_NOTE_STORAGE_ATTACHMENT_SCHEME = 803 +const MINT_NOTE_STORAGE_ATTACHMENT = 804 +const MINT_NOTE_STORAGE_OUTPUT_SCRIPT_ROOT = 808 +const MINT_NOTE_STORAGE_OUTPUT_SERIAL_NUM = 812 +const MINT_NOTE_STORAGE_OUTPUT_NOTE_INPUT = 816 + +# MINT note storage layout (public mode, 18 items): +# [0]: tag, [1]: amount, [2]: attachment_kind, [3]: attachment_scheme, +# [4-7]: ATTACHMENT, [8-11]: P2ID_SCRIPT_ROOT, [12-15]: SERIAL_NUM, +# [16]: account_id_suffix, [17]: account_id_prefix +const MINT_NOTE_NUM_STORAGE_ITEMS = 18 +const MINT_NOTE_TYPE_PUBLIC = 1 # claim memory locals const CLAIM_PREFIX_MEM_LOC = 8 @@ -105,13 +119,6 @@ const BUILD_MINT_SUFFIX_MEM_LOC = 9 const BUILD_MINT_FAUCET_PREFIX_LOC = 10 const BUILD_MINT_FAUCET_SUFFIX_LOC = 11 -# MINT note storage layout (public mode, 18 items): -# [0]: tag, [1]: amount, [2]: attachment_kind, [3]: attachment_scheme, -# [4-7]: ATTACHMENT, [8-11]: P2ID_SCRIPT_ROOT, [12-15]: SERIAL_NUM, -# [16]: account_id_suffix, [17]: account_id_prefix -const MINT_NOTE_NUM_STORAGE_ITEMS = 18 -const MINT_NOTE_TYPE_PUBLIC = 1 - # P2ID output note constants const P2ID_NOTE_NUM_STORAGE_ITEMS = 2 const OUTPUT_NOTE_TYPE_PUBLIC = 1 @@ -574,79 +581,66 @@ proc build_mint_output_note # Write tag to MINT note storage [0] # Stack: [dest_tag, native_amount, ...] - push.MINT_NOTE_STORAGE_MEM_ADDR mem_store + mem_store.MINT_NOTE_STORAGE_DEST_TAG # => [native_amount, pad(16)] # Write amount to MINT note storage [1] - push.MINT_NOTE_STORAGE_MEM_ADDR add.1 mem_store + mem_store.MINT_NOTE_STORAGE_NATIVE_AMOUNT # => [pad(16)] # Write attachment_kind = 0 [2] - push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.2 mem_store + push.0 mem_store.MINT_NOTE_STORAGE_ATTACHMENT_KIND # => [pad(16)] # Write attachment_scheme = 0 [3] - push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.3 mem_store + push.0 mem_store.MINT_NOTE_STORAGE_ATTACHMENT_SCHEME # => [pad(16)] # Write ATTACHMENT = [0, 0, 0, 0] [4..7] - push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.4 mem_store - push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.5 mem_store - push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.6 mem_store - push.0 push.MINT_NOTE_STORAGE_MEM_ADDR add.7 mem_store + padw mem_storew_be.MINT_NOTE_STORAGE_ATTACHMENT dropw # => [pad(16)] # Write P2ID_SCRIPT_ROOT to MINT note storage [8..11] procref.::miden::standards::notes::p2id::main - # => [P2ID_SCRIPT_ROOT(4), pad(16)] + # => [P2ID_SCRIPT_ROOT, pad(16)] - exec.word::reverse - - push.MINT_NOTE_STORAGE_MEM_ADDR add.8 mem_store - push.MINT_NOTE_STORAGE_MEM_ADDR add.9 mem_store - push.MINT_NOTE_STORAGE_MEM_ADDR add.10 mem_store - push.MINT_NOTE_STORAGE_MEM_ADDR add.11 mem_store + mem_storew_be.MINT_NOTE_STORAGE_OUTPUT_SCRIPT_ROOT dropw # => [pad(16)] # Write SERIAL_NUM (PROOF_DATA_KEY) to MINT note storage [12..15] mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR - # => [SERIAL_NUM(4), pad(16)] - - exec.word::reverse + # => [SERIAL_NUM, pad(16)] - push.MINT_NOTE_STORAGE_MEM_ADDR add.12 mem_store - push.MINT_NOTE_STORAGE_MEM_ADDR add.13 mem_store - push.MINT_NOTE_STORAGE_MEM_ADDR add.14 mem_store - push.MINT_NOTE_STORAGE_MEM_ADDR add.15 mem_store + mem_storew_be.MINT_NOTE_STORAGE_OUTPUT_SERIAL_NUM dropw # => [pad(16)] # Write P2ID storage items: [account_id_suffix, account_id_prefix] [16..17] loc_load.BUILD_MINT_SUFFIX_MEM_LOC - push.MINT_NOTE_STORAGE_MEM_ADDR add.16 mem_store + mem_store.MINT_NOTE_STORAGE_OUTPUT_NOTE_INPUT # => [pad(16)] loc_load.BUILD_MINT_PREFIX_MEM_LOC - push.MINT_NOTE_STORAGE_MEM_ADDR add.17 mem_store + push.MINT_NOTE_STORAGE_OUTPUT_NOTE_INPUT add.1 mem_store # => [pad(16)] # Now create the MINT output note # Get the MINT note script root procref.::miden::standards::notes::mint::main - # => [MINT_SCRIPT_ROOT(4), pad(16)] + # => [MINT_SCRIPT_ROOT, pad(16)] # Generate a serial number for the MINT note (use PROOF_DATA_KEY) swapw mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR - # => [MINT_SERIAL_NUM(4), MINT_SCRIPT_ROOT(4), pad(16)] + # => [MINT_SERIAL_NUM, MINT_SCRIPT_ROOT, pad(16)] # Build the MINT note recipient push.MINT_NOTE_NUM_STORAGE_ITEMS # => [num_storage_items, MINT_SERIAL_NUM, MINT_SCRIPT_ROOT, pad(16)] - push.MINT_NOTE_STORAGE_MEM_ADDR + push.MINT_NOTE_STORAGE_MEM_ADDR_0 # => [storage_ptr, num_storage_items, MINT_SERIAL_NUM, MINT_SCRIPT_ROOT, pad(16)] exec.note::build_recipient - # => [MINT_RECIPIENT(4), pad(16)] + # => [MINT_RECIPIENT, pad(16)] # Create the MINT output note targeting the faucet push.MINT_NOTE_TYPE_PUBLIC @@ -673,7 +667,7 @@ proc build_mint_output_note # => [faucet_prefix, faucet_suffix, exec_hint, note_idx, pad(16)] exec.network_account_target::new - # => [attachment_scheme, attachment_kind, ATTACHMENT(4), note_idx, pad(16)] + # => [attachment_scheme, attachment_kind, ATTACHMENT, note_idx, pad(16)] # Rearrange for set_attachment: [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] movup.6 From 7fd9d766614771dc963c677ef4ea77ec7c6a4ed4 Mon Sep 17 00:00:00 2001 From: riemann Date: Mon, 2 Mar 2026 13:48:46 +0300 Subject: [PATCH 07/29] refactor: fix stack comments --- .../asm/agglayer/bridge/bridge_config.masm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm index 3da74af354..d5e2cf1e33 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm @@ -146,21 +146,21 @@ pub proc register_faucet # Hash the token address exec.hash_token_address - # => [TOKEN_ADDR_HASH(4), faucet_id_prefix, faucet_id_suffix, pad(9)] + # => [TOKEN_ADDR_HASH, faucet_id_prefix, faucet_id_suffix, pad(9)] # Build VALUE = [faucet_id_prefix, faucet_id_suffix, 0, 0] movup.5 movup.5 - # => [faucet_id_prefix, faucet_id_suffix, TOKEN_ADDR_HASH(4), pad(9)] + # => [faucet_id_prefix, faucet_id_suffix, TOKEN_ADDR_HASH, pad(9)] push.0.0 movup.3 movup.3 - # => [faucet_id_prefix, faucet_id_suffix, 0, 0, TOKEN_ADDR_HASH(4), pad(9)] + # => [faucet_id_prefix, faucet_id_suffix, 0, 0, TOKEN_ADDR_HASH, pad(9)] swapw - # => [TOKEN_ADDR_HASH(4), faucet_id_prefix, faucet_id_suffix, 0, 0, pad(9)] + # => [TOKEN_ADDR_HASH, faucet_id_prefix, faucet_id_suffix, 0, 0, pad(9)] push.TOKEN_REGISTRY_SLOT[0..2] exec.native_account::set_map_item - # => [OLD_VALUE(4), pad(9)] + # => [OLD_VALUE, pad(9)] dropw # => [pad(9)] @@ -209,7 +209,7 @@ end pub proc lookup_faucet_by_token_address # Hash the token address exec.hash_token_address - # => [TOKEN_ADDR_HASH(4)] + # => [TOKEN_ADDR_HASH] push.TOKEN_REGISTRY_SLOT[0..2] exec.active_account::get_map_item @@ -237,7 +237,7 @@ end #! Writes the 5 felts to memory and computes the RPO hash. #! #! Inputs: [origin_token_addr(5)] -#! Outputs: [TOKEN_ADDR_HASH(4)] +#! Outputs: [TOKEN_ADDR_HASH] #! #! Invocation: exec proc hash_token_address @@ -252,7 +252,7 @@ proc hash_token_address # Hash the token address: rpo256::hash_elements(num_elements=5, start_ptr) push.5 push.TOKEN_ADDR_HASH_PTR exec.rpo256::hash_elements - # => [TOKEN_ADDR_HASH(4)] + # => [TOKEN_ADDR_HASH] end #! Asserts that the note sender matches the bridge admin stored in account storage. From 8dff7929fbc07d00796c38924a7be35649da2278 Mon Sep 17 00:00:00 2001 From: riemann Date: Mon, 2 Mar 2026 17:39:59 +0300 Subject: [PATCH 08/29] fix: rm println statements --- crates/miden-testing/tests/agglayer/config_bridge.rs | 3 --- crates/miden-testing/tests/scripts/faucet.rs | 2 -- 2 files changed, 5 deletions(-) diff --git a/crates/miden-testing/tests/agglayer/config_bridge.rs b/crates/miden-testing/tests/agglayer/config_bridge.rs index 9dc9b29c3f..3fc9311ba5 100644 --- a/crates/miden-testing/tests/agglayer/config_bridge.rs +++ b/crates/miden-testing/tests/agglayer/config_bridge.rs @@ -72,9 +72,6 @@ async fn test_config_agg_bridge_registers_faucet() -> anyhow::Result<()> { builder.rng_mut(), )?; - println!("origin token address as vec: {:?}", origin_token_address.to_elements()); - println!("faucet id: {:?} {:?}", faucet_to_register.prefix(), faucet_to_register.suffix()); - builder.add_output_note(OutputNote::Full(config_note.clone())); let mock_chain = builder.build()?; diff --git a/crates/miden-testing/tests/scripts/faucet.rs b/crates/miden-testing/tests/scripts/faucet.rs index 2db9f3b220..4f8afa26f9 100644 --- a/crates/miden-testing/tests/scripts/faucet.rs +++ b/crates/miden-testing/tests/scripts/faucet.rs @@ -1231,8 +1231,6 @@ async fn test_mint_note_output_note_types(#[case] note_type: NoteType) -> anyhow ) .unwrap(); - println!("p2id script: {:?}", p2id_mint_output_note.script().root()); - // Create MINT note based on note type let mint_storage = match note_type { NoteType::Private => { From 2225fe0f476db371d7082c455b68126488798de7 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:41:24 +0300 Subject: [PATCH 09/29] Update crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Co-authored-by: Andrey Khmuro --- crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm index d5e2cf1e33..1fd24527cc 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm @@ -146,7 +146,7 @@ pub proc register_faucet # Hash the token address exec.hash_token_address - # => [TOKEN_ADDR_HASH, faucet_id_prefix, faucet_id_suffix, pad(9)] + # => [TOKEN_ADDR_HASH, faucet_id_prefix, faucet_id_suffix, pad(10)] # Build VALUE = [faucet_id_prefix, faucet_id_suffix, 0, 0] movup.5 movup.5 From 7588547fcbfdd6163837685d500da5ea1d9d6400 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:41:34 +0300 Subject: [PATCH 10/29] Update crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Co-authored-by: Andrey Khmuro --- crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm index 1fd24527cc..94fb91a87f 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm @@ -160,7 +160,7 @@ pub proc register_faucet push.TOKEN_REGISTRY_SLOT[0..2] exec.native_account::set_map_item - # => [OLD_VALUE, pad(9)] + # => [OLD_VALUE, pad(12)] dropw # => [pad(9)] From bd4c3ef85b68412299a8cfbe06c8852fe869ddcf Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:41:43 +0300 Subject: [PATCH 11/29] Update crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Co-authored-by: Andrey Khmuro --- crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm index 94fb91a87f..77748a7678 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm @@ -163,7 +163,7 @@ pub proc register_faucet # => [OLD_VALUE, pad(12)] dropw - # => [pad(9)] + # => [pad(16)] end #! Asserts that a faucet is registered in the bridge's faucet registry. From 7e5d734f20c869ee625d8aecdff1266b35c23b9f Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:41:58 +0300 Subject: [PATCH 12/29] Update crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Co-authored-by: Andrey Khmuro --- crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm index 77748a7678..5e1175b696 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm @@ -213,7 +213,7 @@ pub proc lookup_faucet_by_token_address push.TOKEN_REGISTRY_SLOT[0..2] exec.active_account::get_map_item - # => [VALUE(4)] = [faucet_id_prefix, faucet_id_suffix, 0, 0] + # => [faucet_id_prefix, faucet_id_suffix, 0, 0] # Assert the token is registered: faucet_id_prefix is always non-zero for valid account IDs. dup.1 dup.1 From 512df0cee5eebfb8aa6904f4590168c2a243b166 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:42:14 +0300 Subject: [PATCH 13/29] Update crates/miden-agglayer/asm/note_scripts/CLAIM.masm Co-authored-by: Andrey Khmuro --- crates/miden-agglayer/asm/note_scripts/CLAIM.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm index a963b0e93f..7eea0ff834 100644 --- a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm +++ b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm @@ -160,7 +160,7 @@ begin # call the Bridge Claim procedure call.bridge::claim - # => [pad(16), pad(7)] + # => [pad(16), pad(9)] # a call invocation consumes and returns 16 elements, but we had trailing padding dropw dropw dropw From 74cb97efb42f260c9aa214c0d81b54adea71fb18 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:43:06 +0300 Subject: [PATCH 14/29] Update crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Co-authored-by: Andrey Khmuro --- .../asm/agglayer/bridge/bridge_config.masm | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm index 5e1175b696..390b54e0bb 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm @@ -216,16 +216,13 @@ pub proc lookup_faucet_by_token_address # => [faucet_id_prefix, faucet_id_suffix, 0, 0] # Assert the token is registered: faucet_id_prefix is always non-zero for valid account IDs. - dup.1 dup.1 - # => [faucet_id_prefix, faucet_id_suffix, faucet_id_prefix, faucet_id_suffix, 0, 0] + dup.1 dup.1 movup.5 movup.5 + # => [0, 0, faucet_id_prefix, faucet_id_suffix, faucet_id_prefix, faucet_id_suffix] - eq.0 swap eq.0 and - # => [is_both_zero, faucet_id_prefix, faucet_id_suffix, 0, 0] + exec.account_id::is_equal + # => [is_id_zero, faucet_id_prefix, faucet_id_suffix] assertz.err=ERR_TOKEN_NOT_REGISTERED - # => [faucet_id_prefix, faucet_id_suffix, 0, 0] - - movup.2 drop movup.2 drop # => [faucet_id_prefix, faucet_id_suffix] end From 7f5ba6f60d5defa2d456defbbd1981d09b7efcba Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:44:04 +0300 Subject: [PATCH 15/29] Update crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Co-authored-by: Andrey Khmuro --- crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm index 390b54e0bb..3c65c11b3e 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm @@ -239,10 +239,7 @@ end #! Invocation: exec proc hash_token_address # Write origin_token_addr[5] to memory for hashing - mem_store.TOKEN_ADDR_HASH_PTR - push.TOKEN_ADDR_HASH_PTR add.1 mem_store - push.TOKEN_ADDR_HASH_PTR add.2 mem_store - push.TOKEN_ADDR_HASH_PTR add.3 mem_store + mem_storew_le.TOKEN_ADDR_HASH_PTR dropw push.TOKEN_ADDR_HASH_PTR add.4 mem_store # => [] From a77b9a24f31530aeaf54fdf0e660d68fca2d1b15 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:44:17 +0300 Subject: [PATCH 16/29] Update crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm Co-authored-by: Andrey Khmuro --- crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index f25fab772a..3073bb761e 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -308,7 +308,7 @@ end pub proc claim # Write output note faucet amount to memory movup.8 mem_store.CLAIM_OUTPUT_NOTE_FAUCET_AMOUNT - # => [PROOF_DATA_KEY, LEAF_DATA_KEY, pad(7)] + # => [PROOF_DATA_KEY, LEAF_DATA_KEY, pad(8)] # Check AdviceMap values hash to keys & write CLAIM inputs & DATA_KEYs to global memory exec.claim_batch_pipe_double_words From 6a602dfc00251ca0eae67b24b1911e6579077944 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:44:30 +0300 Subject: [PATCH 17/29] Update crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm Co-authored-by: Andrey Khmuro --- crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index 3073bb761e..d758804709 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -312,7 +312,7 @@ pub proc claim # Check AdviceMap values hash to keys & write CLAIM inputs & DATA_KEYs to global memory exec.claim_batch_pipe_double_words - # => [pad(7)] + # => [pad(16)] # validate_claim will overwrite memory in-place, so we need to load the account and amount # before calling validate_claim and store it in memory locals From 433dcc4223a45a3d020a6ac4928735d5d185a7b2 Mon Sep 17 00:00:00 2001 From: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:54:55 +0300 Subject: [PATCH 18/29] Update crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm Co-authored-by: Andrey Khmuro --- crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index d758804709..7a14f2a788 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -545,12 +545,8 @@ end @locals(12) # 0-3: amount_lo, 4-7: amount_hi, 8: prefix, 9: suffix, 10: faucet_prefix, 11: faucet_suffix proc build_mint_output_note # save prefix and suffix to local memory for later use - dup loc_store.BUILD_MINT_PREFIX_MEM_LOC - swap dup loc_store.BUILD_MINT_SUFFIX_MEM_LOC swap - # => [prefix, suffix, AMOUNT[0], AMOUNT[1], ...] - - # drop prefix and suffix from stack (already saved in locals) - swap drop drop + loc_store.BUILD_MINT_PREFIX_MEM_LOC + loc_store.BUILD_MINT_SUFFIX_MEM_LOC # => [AMOUNT[0], AMOUNT[1], ...] # store amount in memory locals From 4450f37255b9a925f100188fd85274721c447dfc Mon Sep 17 00:00:00 2001 From: riemann Date: Mon, 2 Mar 2026 18:45:46 +0300 Subject: [PATCH 19/29] refactor: improve bridge_in doc comments --- .../asm/agglayer/bridge/bridge_in.masm | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index 7a14f2a788..ef83f5391e 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -268,13 +268,12 @@ end #! Validates a claim against the AggLayer bridge and creates a MINT note for the aggfaucet. #! -#! This procedure is called by the CLAIM note script when consumed by the bridge account. -#! It validates the Merkle proof directly (no FPI needed since bridge is the native account), -#! then looks up the faucet account ID from the token registry using the origin token address -#! from the leaf data, and creates a MINT note targeting the aggfaucet. +#! This procedure is called by the CLAIM note script. It validates the Merkle proof and then +#! looks up the faucet account ID from the token registry using the origin token address from +#! the leaf data, and creates a MINT note targeting the aggfaucet. #! -#! The MINT note uses the standard MintNote pattern (private mode) with 12 storage items: -#! [tag, amount, attachment_kind(0), attachment_scheme(0), ATTACHMENT(0,0,0,0), RECIPIENT_DIGEST(4)] +#! The MINT note uses the standard MintNote pattern (public mode) with 16+ storage items: +#! [tag, amount, attachment_kind(0), attachment_scheme(0), ATTACHMENT(0,0,0,0), RECIPIENT_DIGEST(4), [STORAGE]] #! #! Inputs: [PROOF_DATA_KEY, LEAF_DATA_KEY, faucet_mint_amount, pad(7)] #! Outputs: [pad(16)] @@ -314,8 +313,6 @@ pub proc claim exec.claim_batch_pipe_double_words # => [pad(16)] - # validate_claim will overwrite memory in-place, so we need to load the account and amount - # before calling validate_claim and store it in memory locals exec.get_destination_account_id_data loc_store.CLAIM_PREFIX_MEM_LOC loc_store.CLAIM_SUFFIX_MEM_LOC # => [pad(7)] @@ -524,7 +521,9 @@ end #! Builds a PUBLIC MINT output note targeting the aggfaucet. #! -#! The MINT note uses public mode (18 storage items) so the faucet creates a PUBLIC P2ID note. +#! The MINT note uses public mode (16 storage items + 2 for target AccountId) so the AggFaucet creates +#! a PUBLIC P2ID note on consumption. +#! #! Storage layout: #! - [0]: tag (note tag for the P2ID output note, targeting the destination account) #! - [1]: amount (the scaled-down Miden amount to mint) From eb336683282bc47f1ee3567089e97f54f8a87cbb Mon Sep 17 00:00:00 2001 From: riemann Date: Tue, 3 Mar 2026 15:10:14 +0300 Subject: [PATCH 20/29] refactor: add constants for memory addresses --- .../miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm b/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm index eeab846cf8..e9d9f1f967 100644 --- a/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm +++ b/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm @@ -10,6 +10,10 @@ use miden::standards::attachments::network_account_target const STORAGE_START_PTR = 0 const CONFIG_AGG_BRIDGE_NUM_STORAGE_ITEMS = 7 +const FAUCET_ID_SUFFIX = 6 +const FAUCET_ID_PREFIX = 5 +const ORIGIN_TOKEN_ADDR_4 = 4 + # ERRORS # ================================================================================================= @@ -61,7 +65,7 @@ begin # register_faucet expects: [origin_token_addr(5), faucet_id_prefix, faucet_id_suffix, pad(9)] # Load all 7 values individually in the correct order - mem_load.6 mem_load.5 mem_load.4 + mem_load.FAUCET_ID_SUFFIX mem_load.FAUCET_ID_PREFIX mem_load.ORIGIN_TOKEN_ADDR_4 # => [addr4, faucet_id_prefix, faucet_id_suffix, pad(12)] padw mem_loadw_le.STORAGE_START_PTR From 1f6e699f2809beea3706653f75397e4bed467977 Mon Sep 17 00:00:00 2001 From: riemann Date: Tue, 3 Mar 2026 15:16:36 +0300 Subject: [PATCH 21/29] refactor: cleanup memory layout in bridge_in --- .../asm/agglayer/bridge/bridge_in.masm | 68 ++++++++++--------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index ef83f5391e..be5661bcd8 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -31,7 +31,30 @@ const ERR_INVALID_CLAIM_PROOF = "invalid claim proof" # CONSTANTS # ================================================================================================= -# Memory pointers for proof data layout +# Data sizes +# ------------------------------------------------------------------------------------------------- + +const PROOF_DATA_WORD_LEN = 134 +# the number of words (4 felts each) in the advice map leaf data +const LEAF_DATA_NUM_WORDS = 8 +const CLAIM_PROOF_DATA_WORD_LEN = 134 +const CLAIM_LEAF_DATA_WORD_LEN = 8 + +# MINT note storage layout (public mode, 18 items): +# [0]: tag, [1]: amount, [2]: attachment_kind, [3]: attachment_scheme, +# [4-7]: ATTACHMENT, [8-11]: P2ID_SCRIPT_ROOT, [12-15]: SERIAL_NUM, +# [16]: account_id_suffix, [17]: account_id_prefix +const MINT_NOTE_NUM_STORAGE_ITEMS = 18 +const MINT_NOTE_TYPE_PUBLIC = 1 + +# P2ID output note constants +const P2ID_NOTE_NUM_STORAGE_ITEMS = 2 +const OUTPUT_NOTE_TYPE_PUBLIC = 1 + +# Global memory pointers +# ------------------------------------------------------------------------------------------------- + +# Memory pointers for proof data layout (used by verify_leaf / get_leaf_value) const PROOF_DATA_PTR = 0 const SMT_PROOF_LOCAL_EXIT_ROOT_PTR = 0 # local SMT proof is first const GLOBAL_INDEX_PTR = PROOF_DATA_PTR + 2 * 256 # 512 @@ -41,19 +64,6 @@ const MAINNET_EXIT_ROOT_PTR = EXIT_ROOTS_PTR # it's the first exit root # the memory address where leaf data is stored for get_leaf_value const LEAF_DATA_START_PTR = 0 -# The offset of the first half of the current Keccak256 hash value in the local memory of the -# `calculate_root` procedure. -const CUR_HASH_LO_LOCAL = 0 - -# The offset of the second half of the current Keccak256 hash value in the local memory of the -# `calculate_root` procedure. -const CUR_HASH_HI_LOCAL = 4 - -# Data sizes -const PROOF_DATA_WORD_LEN = 134 -# the number of words (4 felts each) in the advice map leaf data -const LEAF_DATA_NUM_WORDS = 8 - # Memory pointers for piped advice map data (used by claim procedure) const CLAIM_PROOF_DATA_START_PTR = 0 const CLAIM_LEAF_DATA_START_PTR = 536 @@ -63,10 +73,6 @@ const CLAIM_OUTPUT_NOTE_FAUCET_AMOUNT = 568 const CLAIM_PROOF_DATA_KEY_MEM_ADDR = 700 const CLAIM_LEAF_DATA_KEY_MEM_ADDR = 704 -# Data sizes for claim -const CLAIM_PROOF_DATA_WORD_LEN = 134 -const CLAIM_LEAF_DATA_WORD_LEN = 8 - # Memory addresses for leaf data fields (derived from leaf data layout at CLAIM_LEAF_DATA_START_PTR=536) const ORIGIN_TOKEN_ADDRESS_0 = 538 const ORIGIN_TOKEN_ADDRESS_1 = 539 @@ -87,7 +93,7 @@ const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_5 = 554 const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_6 = 555 const OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_7 = 556 -# Memory Addresses for MINT note output construction +# Memory addresses for MINT note output construction const MINT_NOTE_STORAGE_MEM_ADDR_0 = 800 const MINT_NOTE_STORAGE_DEST_TAG = 800 const MINT_NOTE_STORAGE_NATIVE_AMOUNT = 801 @@ -98,20 +104,20 @@ const MINT_NOTE_STORAGE_OUTPUT_SCRIPT_ROOT = 808 const MINT_NOTE_STORAGE_OUTPUT_SERIAL_NUM = 812 const MINT_NOTE_STORAGE_OUTPUT_NOTE_INPUT = 816 -# MINT note storage layout (public mode, 18 items): -# [0]: tag, [1]: amount, [2]: attachment_kind, [3]: attachment_scheme, -# [4-7]: ATTACHMENT, [8-11]: P2ID_SCRIPT_ROOT, [12-15]: SERIAL_NUM, -# [16]: account_id_suffix, [17]: account_id_prefix -const MINT_NOTE_NUM_STORAGE_ITEMS = 18 -const MINT_NOTE_TYPE_PUBLIC = 1 +# Local memory offsets +# ------------------------------------------------------------------------------------------------- -# claim memory locals -const CLAIM_PREFIX_MEM_LOC = 8 -const CLAIM_SUFFIX_MEM_LOC = 9 +# Offsets in the local memory of the `calculate_root` procedure +const CUR_HASH_LO_LOCAL = 0 # first half of the current Keccak256 hash value +const CUR_HASH_HI_LOCAL = 4 # second half of the current Keccak256 hash value + +# Offsets in the local memory of the `claim` procedure const CLAIM_AMOUNT_MEM_LOC_0 = 0 const CLAIM_AMOUNT_MEM_LOC_1 = 4 +const CLAIM_PREFIX_MEM_LOC = 8 +const CLAIM_SUFFIX_MEM_LOC = 9 -# build_mint_output_note memory locals +# Offsets in the local memory of the `build_mint_output_note` procedure const BUILD_MINT_AMOUNT_MEM_LOC_0 = 0 const BUILD_MINT_AMOUNT_MEM_LOC_1 = 4 const BUILD_MINT_PREFIX_MEM_LOC = 8 @@ -119,10 +125,6 @@ const BUILD_MINT_SUFFIX_MEM_LOC = 9 const BUILD_MINT_FAUCET_PREFIX_LOC = 10 const BUILD_MINT_FAUCET_SUFFIX_LOC = 11 -# P2ID output note constants -const P2ID_NOTE_NUM_STORAGE_ITEMS = 2 -const OUTPUT_NOTE_TYPE_PUBLIC = 1 - # PUBLIC INTERFACE # ================================================================================================= From 3d2fa158e5d26b8dce0860155bd3bc28fcb93a51 Mon Sep 17 00:00:00 2001 From: riemann Date: Tue, 3 Mar 2026 15:18:38 +0300 Subject: [PATCH 22/29] fix: update comment in CLAIM note --- crates/miden-agglayer/asm/note_scripts/CLAIM.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm index 7eea0ff834..5adbcf3286 100644 --- a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm +++ b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm @@ -22,7 +22,7 @@ const FAUCET_MINT_AMOUNT = 568 const ERR_CLAIM_TARGET_ACCT_MISMATCH = "CLAIM note attachment target account does not match consuming account" -#! Reads claim data from memory and inserts it into the advice map under three separate keys. +#! Reads claim data from memory and inserts it into the advice map under two separate keys. #! #! This procedure organizes the claim note data into three logical groups and inserts them #! into the advice map under separate keys for easier access. From 66ae6d3036b7f8a2a1a81917a0e108b26ee3a39c Mon Sep 17 00:00:00 2001 From: riemann Date: Tue, 3 Mar 2026 15:22:32 +0300 Subject: [PATCH 23/29] refactor: remove redundant dropw --- crates/miden-agglayer/asm/note_scripts/CLAIM.masm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm index 5adbcf3286..0ae7df90f3 100644 --- a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm +++ b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm @@ -163,7 +163,7 @@ begin # => [pad(16), pad(9)] # a call invocation consumes and returns 16 elements, but we had trailing padding - dropw dropw dropw + dropw dropw drop # => [pad(16)] end From 435c5d03f2d654b87e1783c0714eac3f5a31a65e Mon Sep 17 00:00:00 2001 From: riemann Date: Wed, 4 Mar 2026 13:38:15 +0300 Subject: [PATCH 24/29] refactor: use execution_hint::ALWAYS --- crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index be5661bcd8..b7b5c47f91 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -11,6 +11,7 @@ use miden::protocol::note use miden::protocol::output_note use miden::standards::note_tag use miden::standards::attachments::network_account_target +use miden::standards::note::execution_hint::ALWAYS # TYPE ALIASES # ================================================================================================= @@ -658,7 +659,7 @@ proc build_mint_output_note # NetworkAccountTarget attachment: targets the faucet so only it can consume the note # network_account_target::new expects [prefix, suffix, exec_hint] # and returns [attachment_scheme, attachment_kind, ATTACHMENT] - push.1 # exec_hint = ALWAYS + push.ALWAYS # exec_hint = ALWAYS loc_load.BUILD_MINT_FAUCET_SUFFIX_LOC loc_load.BUILD_MINT_FAUCET_PREFIX_LOC # => [faucet_prefix, faucet_suffix, exec_hint, note_idx, pad(16)] From 55ff197478dc5452ccf17ef136ad1ce5faa59fc4 Mon Sep 17 00:00:00 2001 From: riemann Date: Wed, 4 Mar 2026 13:40:23 +0300 Subject: [PATCH 25/29] refactor: rm redundant const note type --- crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index b7b5c47f91..a83ce7a8f4 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -46,7 +46,6 @@ const CLAIM_LEAF_DATA_WORD_LEN = 8 # [4-7]: ATTACHMENT, [8-11]: P2ID_SCRIPT_ROOT, [12-15]: SERIAL_NUM, # [16]: account_id_suffix, [17]: account_id_prefix const MINT_NOTE_NUM_STORAGE_ITEMS = 18 -const MINT_NOTE_TYPE_PUBLIC = 1 # P2ID output note constants const P2ID_NOTE_NUM_STORAGE_ITEMS = 2 @@ -641,7 +640,7 @@ proc build_mint_output_note # => [MINT_RECIPIENT, pad(16)] # Create the MINT output note targeting the faucet - push.MINT_NOTE_TYPE_PUBLIC + push.OUTPUT_NOTE_TYPE_PUBLIC # => [note_type, MINT_RECIPIENT, pad(16)] # Compute note tag targeting the faucet account From d0b0a7825ad6ad629229e83380d1c4a85d40b6c5 Mon Sep 17 00:00:00 2001 From: riemann Date: Wed, 4 Mar 2026 14:42:48 +0300 Subject: [PATCH 26/29] refactor: simplify loc_store/load ops in bridge_in --- .../asm/agglayer/bridge/bridge_in.masm | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index a83ce7a8f4..b6204f4338 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -305,7 +305,7 @@ end #! - the origin token address is not registered in the bridge's token registry. #! #! Invocation: call -@locals(12) # 0-3: amount_lo, 4-7: amount_hi, 8: dest_prefix, 9: dest_suffix, 10: faucet_prefix, 11: faucet_suffix +@locals(10) # 0-3: amount_lo, 4-7: amount_hi, 8: dest_prefix, 9: dest_suffix pub proc claim # Write output note faucet amount to memory movup.8 mem_store.CLAIM_OUTPUT_NOTE_FAUCET_AMOUNT @@ -323,16 +323,6 @@ pub proc claim loc_storew_be.CLAIM_AMOUNT_MEM_LOC_0 dropw loc_storew_be.CLAIM_AMOUNT_MEM_LOC_1 dropw # => [pad(7)] - # Look up the faucet account ID from the origin token address in the leaf data - exec.get_origin_token_address - # => [origin_token_addr(5), pad(7)] - - exec.bridge_config::lookup_faucet_by_token_address - # => [faucet_id_prefix, faucet_id_suffix, pad(7)] - - loc_store.BUILD_MINT_FAUCET_PREFIX_LOC loc_store.BUILD_MINT_FAUCET_SUFFIX_LOC - # => [pad(7)] - # VALIDATE CLAIM - call verify_leaf_bridge directly (bridge is the native account) mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR # => [PROOF_DATA_KEY, pad(7)] @@ -643,6 +633,16 @@ proc build_mint_output_note push.OUTPUT_NOTE_TYPE_PUBLIC # => [note_type, MINT_RECIPIENT, pad(16)] + # Look up the faucet account ID from the origin token address in the leaf data + exec.get_origin_token_address + # => [origin_token_addr(5), note_type, MINT_RECIPIENT, pad(16)] + + exec.bridge_config::lookup_faucet_by_token_address + # => [faucet_id_prefix, faucet_id_suffix, note_type, MINT_RECIPIENT, pad(16)] + + loc_store.BUILD_MINT_FAUCET_PREFIX_LOC loc_store.BUILD_MINT_FAUCET_SUFFIX_LOC + # => [note_type, MINT_RECIPIENT, pad(16)] + # Compute note tag targeting the faucet account loc_load.BUILD_MINT_FAUCET_PREFIX_LOC # => [faucet_prefix, note_type, MINT_RECIPIENT, pad(16)] From 46cb5f8ee6a3f43f4b292e5a16cffa3cdef6d2fa Mon Sep 17 00:00:00 2001 From: riemann Date: Wed, 4 Mar 2026 15:02:34 +0300 Subject: [PATCH 27/29] refactor: decompose build_mint_output_note into modular helpers --- .../asm/agglayer/bridge/bridge_in.masm | 168 ++++++++++-------- 1 file changed, 92 insertions(+), 76 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index b6204f4338..1df2fd5401 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -112,18 +112,12 @@ const CUR_HASH_LO_LOCAL = 0 # first half of the current Keccak256 hash value const CUR_HASH_HI_LOCAL = 4 # second half of the current Keccak256 hash value # Offsets in the local memory of the `claim` procedure -const CLAIM_AMOUNT_MEM_LOC_0 = 0 -const CLAIM_AMOUNT_MEM_LOC_1 = 4 -const CLAIM_PREFIX_MEM_LOC = 8 -const CLAIM_SUFFIX_MEM_LOC = 9 - -# Offsets in the local memory of the `build_mint_output_note` procedure -const BUILD_MINT_AMOUNT_MEM_LOC_0 = 0 -const BUILD_MINT_AMOUNT_MEM_LOC_1 = 4 -const BUILD_MINT_PREFIX_MEM_LOC = 8 -const BUILD_MINT_SUFFIX_MEM_LOC = 9 -const BUILD_MINT_FAUCET_PREFIX_LOC = 10 -const BUILD_MINT_FAUCET_SUFFIX_LOC = 11 +const CLAIM_PREFIX_MEM_LOC = 0 +const CLAIM_SUFFIX_MEM_LOC = 1 + +# Offsets in the local memory of the `create_mint_note_with_attachment` procedure +const CREATE_MINT_FAUCET_PREFIX_LOC = 0 +const CREATE_MINT_FAUCET_SUFFIX_LOC = 1 # PUBLIC INTERFACE # ================================================================================================= @@ -270,12 +264,12 @@ end #! Validates a claim against the AggLayer bridge and creates a MINT note for the aggfaucet. #! -#! This procedure is called by the CLAIM note script. It validates the Merkle proof and then -#! looks up the faucet account ID from the token registry using the origin token address from +#! This procedure is called by the CLAIM note script. It validates the Merkle proof and then +#! looks up the faucet account ID from the token registry using the origin token address from #! the leaf data, and creates a MINT note targeting the aggfaucet. #! -#! The MINT note uses the standard MintNote pattern (public mode) with 16+ storage items: -#! [tag, amount, attachment_kind(0), attachment_scheme(0), ATTACHMENT(0,0,0,0), RECIPIENT_DIGEST(4), [STORAGE]] +#! The MINT note uses the standard MINT note pattern (public mode) with 18 storage items. +#! See `write_mint_note_storage` for the full storage layout. #! #! Inputs: [PROOF_DATA_KEY, LEAF_DATA_KEY, faucet_mint_amount, pad(7)] #! Outputs: [pad(16)] @@ -305,7 +299,7 @@ end #! - the origin token address is not registered in the bridge's token registry. #! #! Invocation: call -@locals(10) # 0-3: amount_lo, 4-7: amount_hi, 8: dest_prefix, 9: dest_suffix +@locals(2) # 0: dest_prefix, 1: dest_suffix pub proc claim # Write output note faucet amount to memory movup.8 mem_store.CLAIM_OUTPUT_NOTE_FAUCET_AMOUNT @@ -319,10 +313,6 @@ pub proc claim loc_store.CLAIM_PREFIX_MEM_LOC loc_store.CLAIM_SUFFIX_MEM_LOC # => [pad(7)] - exec.get_raw_claim_amount - loc_storew_be.CLAIM_AMOUNT_MEM_LOC_0 dropw loc_storew_be.CLAIM_AMOUNT_MEM_LOC_1 dropw - # => [pad(7)] - # VALIDATE CLAIM - call verify_leaf_bridge directly (bridge is the native account) mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR # => [PROOF_DATA_KEY, pad(7)] @@ -335,11 +325,8 @@ pub proc claim # => [pad(16)] # Build MINT output note targeting the aggfaucet - loc_loadw_be.CLAIM_AMOUNT_MEM_LOC_1 swapw loc_loadw_be.CLAIM_AMOUNT_MEM_LOC_0 - # => [AMOUNT[0], AMOUNT[1], pad(8)] - loc_load.CLAIM_SUFFIX_MEM_LOC loc_load.CLAIM_PREFIX_MEM_LOC - # => [prefix, suffix, AMOUNT[0], AMOUNT[1], pad(8)] + # => [prefix, suffix, pad(16)] exec.build_mint_output_note # => [pad(16)] @@ -513,8 +500,34 @@ end #! Builds a PUBLIC MINT output note targeting the aggfaucet. #! -#! The MINT note uses public mode (16 storage items + 2 for target AccountId) so the AggFaucet creates -#! a PUBLIC P2ID note on consumption. +#! The MINT note uses public mode (18 storage items) so the AggFaucet creates +#! a PUBLIC P2ID note on consumption. This procedure orchestrates three steps: +#! 1. Write all 18 MINT note storage items to global memory. +#! 2. Build the MINT note recipient digest from the storage. +#! 3. Look up the faucet, create the output note, and set the attachment. +#! +#! Inputs: [prefix, suffix, pad(16)] +#! Outputs: [pad(16)] +#! +#! Where: +#! - prefix, suffix: destination account ID +#! +#! Invocation: exec +proc build_mint_output_note + # Step 1: Write all 18 MINT note storage items to global memory + exec.write_mint_note_storage + # => [pad(16)] + + # Step 2: Build the MINT note recipient digest + exec.build_mint_recipient + # => [MINT_RECIPIENT, pad(16)] + + # Step 3: Create the output note and set the faucet attachment + exec.create_mint_note_with_attachment + # => [pad(16)] +end + +#! Writes all 18 MINT note storage items to global memory. #! #! Storage layout: #! - [0]: tag (note tag for the P2ID output note, targeting the destination account) @@ -527,60 +540,49 @@ end #! - [16]: account_id_suffix (destination account suffix) #! - [17]: account_id_prefix (destination account prefix) #! -#! Inputs: [prefix, suffix, AMOUNT[0], AMOUNT[1]] -#! Outputs: [] +#! Inputs: [prefix, suffix, pad(16)] +#! Outputs: [pad(16)] #! -#! Where: -#! - prefix, suffix: destination account ID -#! - AMOUNT[0], AMOUNT[1]: raw U256 claim amount (8 felts) -@locals(12) # 0-3: amount_lo, 4-7: amount_hi, 8: prefix, 9: suffix, 10: faucet_prefix, 11: faucet_suffix -proc build_mint_output_note - # save prefix and suffix to local memory for later use - loc_store.BUILD_MINT_PREFIX_MEM_LOC - loc_store.BUILD_MINT_SUFFIX_MEM_LOC - # => [AMOUNT[0], AMOUNT[1], ...] +#! Invocation: exec +proc write_mint_note_storage + # Write P2ID storage items first (before prefix is consumed): [16..17] + # Write account_id_suffix [16] + dup.1 mem_store.MINT_NOTE_STORAGE_OUTPUT_NOTE_INPUT + # => [prefix, suffix, pad(16)] - # store amount in memory locals - loc_storew_be.BUILD_MINT_AMOUNT_MEM_LOC_0 dropw loc_storew_be.BUILD_MINT_AMOUNT_MEM_LOC_1 dropw - # => [pad(16)] + # Write account_id_prefix [17] + dup push.MINT_NOTE_STORAGE_OUTPUT_NOTE_INPUT add.1 mem_store + # => [prefix, suffix, pad(16)] # Get the native amount from the pre-computed miden_claim_amount mem_load.CLAIM_OUTPUT_NOTE_FAUCET_AMOUNT - # => [native_amount, pad(16)] + # => [native_amount, prefix, suffix, pad(16)] - # Compute the note tag for the destination account - loc_load.BUILD_MINT_PREFIX_MEM_LOC - # => [dest_prefix, native_amount, pad(16)] + # Compute the note tag for the destination account (consumes prefix) + swap + # => [prefix, native_amount, suffix, pad(16)] exec.note_tag::create_account_target - # => [dest_tag, native_amount, pad(16)] - - # Store MINT note storage items in memory (public mode, 18 items): - # [0]: dest_tag (tag for the P2ID output note) - # [1]: native_amount - # [2]: attachment_kind = 0 - # [3]: attachment_scheme = 0 - # [4..7]: ATTACHMENT = [0, 0, 0, 0] - # [8..11]: P2ID_SCRIPT_ROOT - # [12..15]: SERIAL_NUM (PROOF_DATA_KEY) - # [16]: account_id_suffix - # [17]: account_id_prefix + # => [dest_tag, native_amount, suffix, pad(16)] # Write tag to MINT note storage [0] - # Stack: [dest_tag, native_amount, ...] mem_store.MINT_NOTE_STORAGE_DEST_TAG - # => [native_amount, pad(16)] + # => [native_amount, suffix, pad(16)] # Write amount to MINT note storage [1] - mem_store.MINT_NOTE_STORAGE_NATIVE_AMOUNT + mem_store.MINT_NOTE_STORAGE_NATIVE_AMOUNT + # => [suffix, pad(16)] + + # suffix is no longer needed, drop it + drop # => [pad(16)] # Write attachment_kind = 0 [2] - push.0 mem_store.MINT_NOTE_STORAGE_ATTACHMENT_KIND + push.0 mem_store.MINT_NOTE_STORAGE_ATTACHMENT_KIND # => [pad(16)] # Write attachment_scheme = 0 [3] - push.0 mem_store.MINT_NOTE_STORAGE_ATTACHMENT_SCHEME + push.0 mem_store.MINT_NOTE_STORAGE_ATTACHMENT_SCHEME # => [pad(16)] # Write ATTACHMENT = [0, 0, 0, 0] [4..7] @@ -600,17 +602,18 @@ proc build_mint_output_note mem_storew_be.MINT_NOTE_STORAGE_OUTPUT_SERIAL_NUM dropw # => [pad(16)] +end - # Write P2ID storage items: [account_id_suffix, account_id_prefix] [16..17] - loc_load.BUILD_MINT_SUFFIX_MEM_LOC - mem_store.MINT_NOTE_STORAGE_OUTPUT_NOTE_INPUT - # => [pad(16)] - - loc_load.BUILD_MINT_PREFIX_MEM_LOC - push.MINT_NOTE_STORAGE_OUTPUT_NOTE_INPUT add.1 mem_store - # => [pad(16)] - - # Now create the MINT output note +#! Builds the MINT note recipient digest from the storage items already written to global memory. +#! +#! Uses the MINT note script root and PROOF_DATA_KEY as serial number, then calls +#! `note::build_recipient` with the storage pointer and item count. +#! +#! Inputs: [pad(16)] +#! Outputs: [MINT_RECIPIENT, pad(16)] +#! +#! Invocation: exec +proc build_mint_recipient # Get the MINT note script root procref.::miden::standards::notes::mint::main # => [MINT_SCRIPT_ROOT, pad(16)] @@ -628,7 +631,20 @@ proc build_mint_output_note exec.note::build_recipient # => [MINT_RECIPIENT, pad(16)] +end +#! Creates the MINT output note and sets the NetworkAccountTarget attachment on it. +#! +#! Looks up the faucet account ID from the origin token address in the leaf data, +#! creates a public output note with no assets, and sets the attachment so only the +#! target faucet can consume the note. +#! +#! Inputs: [MINT_RECIPIENT, pad(16)] +#! Outputs: [pad(16)] +#! +#! Invocation: exec +@locals(2) # 0: faucet_prefix, 1: faucet_suffix +proc create_mint_note_with_attachment # Create the MINT output note targeting the faucet push.OUTPUT_NOTE_TYPE_PUBLIC # => [note_type, MINT_RECIPIENT, pad(16)] @@ -640,11 +656,11 @@ proc build_mint_output_note exec.bridge_config::lookup_faucet_by_token_address # => [faucet_id_prefix, faucet_id_suffix, note_type, MINT_RECIPIENT, pad(16)] - loc_store.BUILD_MINT_FAUCET_PREFIX_LOC loc_store.BUILD_MINT_FAUCET_SUFFIX_LOC + loc_store.CREATE_MINT_FAUCET_PREFIX_LOC loc_store.CREATE_MINT_FAUCET_SUFFIX_LOC # => [note_type, MINT_RECIPIENT, pad(16)] # Compute note tag targeting the faucet account - loc_load.BUILD_MINT_FAUCET_PREFIX_LOC + loc_load.CREATE_MINT_FAUCET_PREFIX_LOC # => [faucet_prefix, note_type, MINT_RECIPIENT, pad(16)] exec.note_tag::create_account_target @@ -659,8 +675,8 @@ proc build_mint_output_note # network_account_target::new expects [prefix, suffix, exec_hint] # and returns [attachment_scheme, attachment_kind, ATTACHMENT] push.ALWAYS # exec_hint = ALWAYS - loc_load.BUILD_MINT_FAUCET_SUFFIX_LOC - loc_load.BUILD_MINT_FAUCET_PREFIX_LOC + loc_load.CREATE_MINT_FAUCET_SUFFIX_LOC + loc_load.CREATE_MINT_FAUCET_PREFIX_LOC # => [faucet_prefix, faucet_suffix, exec_hint, note_idx, pad(16)] exec.network_account_target::new From 5503ae3fba141b6f095c6d48c42e41c8391c3237 Mon Sep 17 00:00:00 2001 From: riemann Date: Wed, 4 Mar 2026 15:13:45 +0300 Subject: [PATCH 28/29] refactor: remove pad(x) stack comments from exec'ed procs --- .../asm/agglayer/bridge/bridge_in.masm | 89 +++++++++---------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index 1df2fd5401..d0237ab3bd 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -506,8 +506,8 @@ end #! 2. Build the MINT note recipient digest from the storage. #! 3. Look up the faucet, create the output note, and set the attachment. #! -#! Inputs: [prefix, suffix, pad(16)] -#! Outputs: [pad(16)] +#! Inputs: [prefix, suffix] +#! Outputs: [] #! #! Where: #! - prefix, suffix: destination account ID @@ -516,15 +516,15 @@ end proc build_mint_output_note # Step 1: Write all 18 MINT note storage items to global memory exec.write_mint_note_storage - # => [pad(16)] + # => [] # Step 2: Build the MINT note recipient digest exec.build_mint_recipient - # => [MINT_RECIPIENT, pad(16)] + # => [MINT_RECIPIENT] # Step 3: Create the output note and set the faucet attachment exec.create_mint_note_with_attachment - # => [pad(16)] + # => [] end #! Writes all 18 MINT note storage items to global memory. @@ -540,68 +540,67 @@ end #! - [16]: account_id_suffix (destination account suffix) #! - [17]: account_id_prefix (destination account prefix) #! -#! Inputs: [prefix, suffix, pad(16)] -#! Outputs: [pad(16)] +#! Inputs: [prefix, suffix] +#! Outputs: [] #! #! Invocation: exec proc write_mint_note_storage # Write P2ID storage items first (before prefix is consumed): [16..17] # Write account_id_suffix [16] dup.1 mem_store.MINT_NOTE_STORAGE_OUTPUT_NOTE_INPUT - # => [prefix, suffix, pad(16)] + # => [prefix, suffix] # Write account_id_prefix [17] dup push.MINT_NOTE_STORAGE_OUTPUT_NOTE_INPUT add.1 mem_store - # => [prefix, suffix, pad(16)] + # => [prefix, suffix] + + swap drop + # => [prefix] # Get the native amount from the pre-computed miden_claim_amount mem_load.CLAIM_OUTPUT_NOTE_FAUCET_AMOUNT - # => [native_amount, prefix, suffix, pad(16)] + # => [native_amount, prefix] # Compute the note tag for the destination account (consumes prefix) swap - # => [prefix, native_amount, suffix, pad(16)] + # => [prefix, native_amount] exec.note_tag::create_account_target - # => [dest_tag, native_amount, suffix, pad(16)] + # => [dest_tag, native_amount] # Write tag to MINT note storage [0] mem_store.MINT_NOTE_STORAGE_DEST_TAG - # => [native_amount, suffix, pad(16)] + # => [native_amount] # Write amount to MINT note storage [1] mem_store.MINT_NOTE_STORAGE_NATIVE_AMOUNT - # => [suffix, pad(16)] - - # suffix is no longer needed, drop it - drop - # => [pad(16)] + # => [] # Write attachment_kind = 0 [2] push.0 mem_store.MINT_NOTE_STORAGE_ATTACHMENT_KIND - # => [pad(16)] + # => [] # Write attachment_scheme = 0 [3] push.0 mem_store.MINT_NOTE_STORAGE_ATTACHMENT_SCHEME - # => [pad(16)] + # => [] # Write ATTACHMENT = [0, 0, 0, 0] [4..7] padw mem_storew_be.MINT_NOTE_STORAGE_ATTACHMENT dropw - # => [pad(16)] + # => [] # Write P2ID_SCRIPT_ROOT to MINT note storage [8..11] procref.::miden::standards::notes::p2id::main - # => [P2ID_SCRIPT_ROOT, pad(16)] + # => [P2ID_SCRIPT_ROOT] mem_storew_be.MINT_NOTE_STORAGE_OUTPUT_SCRIPT_ROOT dropw - # => [pad(16)] + # => [] # Write SERIAL_NUM (PROOF_DATA_KEY) to MINT note storage [12..15] mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR - # => [SERIAL_NUM, pad(16)] + # => [SERIAL_NUM] mem_storew_be.MINT_NOTE_STORAGE_OUTPUT_SERIAL_NUM dropw - # => [pad(16)] + # => [] end #! Builds the MINT note recipient digest from the storage items already written to global memory. @@ -609,28 +608,28 @@ end #! Uses the MINT note script root and PROOF_DATA_KEY as serial number, then calls #! `note::build_recipient` with the storage pointer and item count. #! -#! Inputs: [pad(16)] -#! Outputs: [MINT_RECIPIENT, pad(16)] +#! Inputs: [] +#! Outputs: [MINT_RECIPIENT] #! #! Invocation: exec proc build_mint_recipient # Get the MINT note script root procref.::miden::standards::notes::mint::main - # => [MINT_SCRIPT_ROOT, pad(16)] + # => [MINT_SCRIPT_ROOT] # Generate a serial number for the MINT note (use PROOF_DATA_KEY) swapw mem_loadw_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR - # => [MINT_SERIAL_NUM, MINT_SCRIPT_ROOT, pad(16)] + # => [MINT_SERIAL_NUM, MINT_SCRIPT_ROOT] # Build the MINT note recipient push.MINT_NOTE_NUM_STORAGE_ITEMS - # => [num_storage_items, MINT_SERIAL_NUM, MINT_SCRIPT_ROOT, pad(16)] + # => [num_storage_items, MINT_SERIAL_NUM, MINT_SCRIPT_ROOT] push.MINT_NOTE_STORAGE_MEM_ADDR_0 - # => [storage_ptr, num_storage_items, MINT_SERIAL_NUM, MINT_SCRIPT_ROOT, pad(16)] + # => [storage_ptr, num_storage_items, MINT_SERIAL_NUM, MINT_SCRIPT_ROOT] exec.note::build_recipient - # => [MINT_RECIPIENT, pad(16)] + # => [MINT_RECIPIENT] end #! Creates the MINT output note and sets the NetworkAccountTarget attachment on it. @@ -639,36 +638,36 @@ end #! creates a public output note with no assets, and sets the attachment so only the #! target faucet can consume the note. #! -#! Inputs: [MINT_RECIPIENT, pad(16)] -#! Outputs: [pad(16)] +#! Inputs: [MINT_RECIPIENT] +#! Outputs: [] #! #! Invocation: exec @locals(2) # 0: faucet_prefix, 1: faucet_suffix proc create_mint_note_with_attachment # Create the MINT output note targeting the faucet push.OUTPUT_NOTE_TYPE_PUBLIC - # => [note_type, MINT_RECIPIENT, pad(16)] + # => [note_type, MINT_RECIPIENT] # Look up the faucet account ID from the origin token address in the leaf data exec.get_origin_token_address - # => [origin_token_addr(5), note_type, MINT_RECIPIENT, pad(16)] + # => [origin_token_addr(5), note_type, MINT_RECIPIENT] exec.bridge_config::lookup_faucet_by_token_address - # => [faucet_id_prefix, faucet_id_suffix, note_type, MINT_RECIPIENT, pad(16)] + # => [faucet_id_prefix, faucet_id_suffix, note_type, MINT_RECIPIENT] loc_store.CREATE_MINT_FAUCET_PREFIX_LOC loc_store.CREATE_MINT_FAUCET_SUFFIX_LOC - # => [note_type, MINT_RECIPIENT, pad(16)] + # => [note_type, MINT_RECIPIENT] # Compute note tag targeting the faucet account loc_load.CREATE_MINT_FAUCET_PREFIX_LOC - # => [faucet_prefix, note_type, MINT_RECIPIENT, pad(16)] + # => [faucet_prefix, note_type, MINT_RECIPIENT] exec.note_tag::create_account_target - # => [faucet_tag, note_type, MINT_RECIPIENT, pad(16)] + # => [faucet_tag, note_type, MINT_RECIPIENT] # Create the output note (no assets - MINT notes carry no assets) exec.output_note::create - # => [note_idx, pad(16)] + # => [note_idx] # Set the attachment on the MINT note to target the faucet account # NetworkAccountTarget attachment: targets the faucet so only it can consume the note @@ -677,17 +676,17 @@ proc create_mint_note_with_attachment push.ALWAYS # exec_hint = ALWAYS loc_load.CREATE_MINT_FAUCET_SUFFIX_LOC loc_load.CREATE_MINT_FAUCET_PREFIX_LOC - # => [faucet_prefix, faucet_suffix, exec_hint, note_idx, pad(16)] + # => [faucet_prefix, faucet_suffix, exec_hint, note_idx] exec.network_account_target::new - # => [attachment_scheme, attachment_kind, ATTACHMENT, note_idx, pad(16)] + # => [attachment_scheme, attachment_kind, ATTACHMENT, note_idx] # Rearrange for set_attachment: [note_idx, attachment_scheme, attachment_kind, ATTACHMENT] movup.6 - # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT(4), pad(16)] + # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT(4)] exec.output_note::set_attachment - # => [pad(16)] + # => [] end #! Computes the root of the SMT based on the provided Merkle path, leaf value and leaf index. From b22874f8185152f46e2431a449b0144de82c4a05 Mon Sep 17 00:00:00 2001 From: riemann Date: Wed, 4 Mar 2026 15:33:23 +0300 Subject: [PATCH 29/29] feat: add constants for attachement types --- .../asm/agglayer/bridge/bridge_in.masm | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index d0237ab3bd..09c52ac01a 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -51,6 +51,10 @@ const MINT_NOTE_NUM_STORAGE_ITEMS = 18 const P2ID_NOTE_NUM_STORAGE_ITEMS = 2 const OUTPUT_NOTE_TYPE_PUBLIC = 1 +# P2ID attachment constants (the P2ID note created by the faucet has no attachment) +const P2ID_ATTACHMENT_KIND_NONE = 0 +const P2ID_ATTACHMENT_SCHEME_NONE = 0 + # Global memory pointers # ------------------------------------------------------------------------------------------------- @@ -576,15 +580,16 @@ proc write_mint_note_storage mem_store.MINT_NOTE_STORAGE_NATIVE_AMOUNT # => [] - # Write attachment_kind = 0 [2] - push.0 mem_store.MINT_NOTE_STORAGE_ATTACHMENT_KIND + # Write P2ID attachment fields (the P2ID note has no attachment) + # attachment_kind = NONE [2] + push.P2ID_ATTACHMENT_KIND_NONE mem_store.MINT_NOTE_STORAGE_ATTACHMENT_KIND # => [] - # Write attachment_scheme = 0 [3] - push.0 mem_store.MINT_NOTE_STORAGE_ATTACHMENT_SCHEME + # attachment_scheme = NONE [3] + push.P2ID_ATTACHMENT_SCHEME_NONE mem_store.MINT_NOTE_STORAGE_ATTACHMENT_SCHEME # => [] - # Write ATTACHMENT = [0, 0, 0, 0] [4..7] + # ATTACHMENT = empty word [4..7] padw mem_storew_be.MINT_NOTE_STORAGE_ATTACHMENT dropw # => []