Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions crates/miden-agglayer/asm/agglayer/faucet/mod.masm
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ 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_SERIAL_NUM_MEM_ADDR = 568
const OUTPUT_NOTE_TAG_MEM_ADDR = 574
const OUTPUT_NOTE_FAUCET_AMOUNT = 575
const OUTPUT_NOTE_TAG_MEM_ADDR = 572
const OUTPUT_NOTE_FAUCET_AMOUNT = 573
const OUTPUT_NOTE_STORAGE_MEM_ADDR = 0
const P2ID_OUTPUT_NOTE_AMOUNT_MEM_PTR = 611

Expand Down Expand Up @@ -392,8 +392,9 @@ end
#! ],
#! OUTPUT_NOTE_DATA_KEY => [
#! output_p2id_serial_num[4], // P2ID note serial number (4 felts, Word)
#! agglayer_faucet_account_id[2], // Agglayer faucet account ID (2 felts, prefix and suffix)
#! output_note_tag[1], // P2ID output note tag
#! miden_claim_amount[1], // Miden claim amount (1 felt)
#! padding[2], // padding (2 felts)
#! ]
Comment on lines 393 to 398
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's wait for this PR to land as it refactors OUTPUT_NOTE_DATA_KEY and removes output_p2id_serial_num

Or could you rebase off of #2484

#! }
#!
Expand Down
57 changes: 13 additions & 44 deletions crates/miden-agglayer/asm/note_scripts/CLAIM.masm
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use miden::agglayer::faucet -> agg_faucet
use miden::protocol::account_id
use miden::protocol::active_account
use miden::protocol::active_note
use miden::protocol::note
use miden::core::crypto::hashes::keccak256
use miden::core::crypto::hashes::rpo256
use miden::core::mem
use miden::standards::attachments::network_account_target

# CONSTANTS
# =================================================================================================
Expand All @@ -18,37 +17,10 @@ const PROOF_DATA_START_PTR = 0
const LEAF_DATA_START_PTR = 536
const OUTPUT_NOTE_DATA_START_PTR = 568

const TARGET_FAUCET_PREFIX_MEM_ADDR = 572
const TARGET_FAUCET_SUFFIX_MEM_ADDR = 573

# ERRORS
# =================================================================================================

const ERR_CLAIM_TARGET_ACCT_MISMATCH = "CLAIM's target account address and transaction address do not match"

#! Asserts that the consuming account matches the target agglayer faucet account.
#!
#! This procedure ensures that only the specified agglayer faucet account can consume
#! this CLAIM note. It assumes that the note storage has already been loaded into memory
#! via active_note::get_storage.
#!
#! Inputs: []
#! Output: []
#!
#! Panics if:
#! - The consuming account ID does not match the target faucet account ID stored in memory
proc assert_aggfaucet_is_consumer
# Load target faucet ID (assumes active_note::get_storage has been called)
mem_load.TARGET_FAUCET_SUFFIX_MEM_ADDR mem_load.TARGET_FAUCET_PREFIX_MEM_ADDR
# => [target_faucet_prefix, target_faucet_suffix]

exec.active_account::get_id
# => [account_id_prefix, account_id_suffix, target_faucet_prefix, target_faucet_suffix]

# ensure only the specified target faucet can consume this CLAIM note, not any other account
exec.account_id::is_equal assert.err=ERR_CLAIM_TARGET_ACCT_MISMATCH
# => []
end
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.
#!
Expand Down Expand Up @@ -78,12 +50,11 @@ end
#! padding[3], // padding (3 felts)
#! ]
#!
#! TODO: Will be removed in future PR
#! OUTPUT_NOTE_DATA_KEY => [
#! output_p2id_serial_num[4], // P2ID note serial number (4 felts, Word)
#! target_faucet_account_id[2], // Target faucet account ID (2 felts, prefix and suffix)
#! output_note_tag[1], // P2ID output note tag
#! miden_claim_amount[1], // Miden claim amount (1 felt)
#! padding[2], // padding (2 felts)
#! ]
#!
#! Invocation: exec
Expand Down Expand Up @@ -139,7 +110,7 @@ end
#! Agglayer Faucet CLAIM script: claims assets by calling the agglayer faucet's claim function.
#!
#! This note can only be consumed by the specific agglayer faucet account whose ID is provided
#! in the note storage (target_faucet_account_id). Upon consumption, it will create a P2ID note.
#! in the note attachment (NetworkAccountTarget). Upon consumption, it will create a P2ID note.
#!
#! Requires that the account exposes:
#! - agglayer::agglayer_faucet::claim procedure.
Expand All @@ -162,9 +133,9 @@ end
#! - metadata [557..564]: 8 felts
#! - padding [565..567]: 3 felts
#! - output_p2id_serial_num [568..571]: 4 felts
#! - target_faucet_account_id [572..573]: 2 felts
#! - output_note_tag [574] : 1 felt
#! - miden_claim_amount [575] : 1 felt
#! - output_note_tag [572] : 1 felt
#! - miden_claim_amount [573] : 1 felt
#! - padding [574..575]: 2 felts
#!
#! Where:
#! - smtProofLocalExitRoot: SMT proof for local exit root (bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH])
Expand All @@ -183,27 +154,25 @@ end
#! - destinationAddress: 20-byte Ethereum address decodable into a Miden AccountId (5 u32 felts)
#! - amount: Amount of tokens (uint256 as 8 u32 felts)
#! - metadata: ABI encoded metadata (fixed size)
#! - padding (3 felts)
#! - output_p2id_serial_num: P2ID note serial number (Word)
#! - target_faucet_account_id: Target agglayer faucet account ID (prefix and suffix). Only this specific
#! account can consume the note - any other account will cause a panic.
#! - output_note_tag: P2ID output note tag
#! - miden_claim_amount: Scaled-down Miden token amount (Felt). This is the Y value computed from
#! scaling down the Ethereum amount (X) by the scale exponent: Y = floor(X / 10^scale_exp)
#!
#! Panics if:
#! - account does not expose claim procedure.
#! - target faucet account ID does not match the consuming account ID.
#! - note attachment target account does not match the consuming account.
begin
dropw
# => [pad(16)]

# Load CLAIM note storage into memory, starting at address 0
push.0 exec.active_note::get_storage drop drop
# Ensure note attachment targets the consuming faucet account.
exec.network_account_target::active_account_matches_target_account
assert.err=ERR_CLAIM_TARGET_ACCT_MISMATCH
# => [pad(16)]

# Check consuming account == aggfaucet
exec.assert_aggfaucet_is_consumer
# Load CLAIM note storage into memory, starting at address 0
push.0 exec.active_note::get_storage drop drop
# => [pad(16)]

exec.write_claim_data_into_advice_map_by_key
Expand Down
29 changes: 14 additions & 15 deletions crates/miden-agglayer/src/claim_note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,38 +172,37 @@ impl SequentialCommit for LeafData {

/// Output note data for CLAIM note creation.
/// Contains note-specific data and can use Miden types.
/// TODO: Remove all but target_faucet_account_id
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: technically should not remove this TODO just yet.

#[derive(Clone)]
pub struct OutputNoteData {
/// P2ID note serial number (4 felts as Word)
pub output_p2id_serial_num: Word,
/// Target agg faucet account ID (2 felts: prefix and suffix)
pub target_faucet_account_id: AccountId,
/// P2ID output note tag
pub output_note_tag: NoteTag,
/// Miden claim amount (scaled-down token amount as Felt)
pub miden_claim_amount: Felt,
}

impl OutputNoteData {
/// Converts the output note data to a vector of field elements for note storage
/// Converts the output note data to a vector of field elements for note storage.
///
/// Layout (8 felts = 2 words):
/// `[serial_num(4), tag(1), miden_claim_amount(1), padding(2)]`
pub fn to_elements(&self) -> Vec<Felt> {
const OUTPUT_NOTE_DATA_ELEMENT_COUNT: usize = 8; // 4 + 2 + 1 + 1 (serial_num + account_id + tag + miden_claim_amount)
const OUTPUT_NOTE_DATA_ELEMENT_COUNT: usize = 8;
let mut elements = Vec::with_capacity(OUTPUT_NOTE_DATA_ELEMENT_COUNT);

// P2ID note serial number (4 felts as Word)
elements.extend(self.output_p2id_serial_num);

// Target faucet account ID (2 felts: prefix and suffix)
elements.push(self.target_faucet_account_id.prefix().as_felt());
elements.push(self.target_faucet_account_id.suffix());

// Output note tag
elements.push(Felt::new(self.output_note_tag.as_u32() as u64));

// Miden claim amount
elements.push(self.miden_claim_amount);

// Padding to keep 8 felts (2 words) for pipe_double_words_preimage_to_memory
elements.extend([Felt::ZERO; 2]);
Comment on lines +203 to +204
Copy link
Contributor

@partylikeits1983 partylikeits1983 Feb 25, 2026

Choose a reason for hiding this comment

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

this actually doesn't have to be padded to 8 felts, (2 words) in NoteStorage for pipe_double_words_preimage_to_memory. But my follow up PR fixes this, so nothing to do here now.


elements
}
}
Expand Down Expand Up @@ -245,24 +244,24 @@ impl TryFrom<ClaimNoteStorage> for NoteStorage {
///
/// # 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.
/// - `sender_account_id`: The account ID of the CLAIM note creator
/// - `rng`: Random number generator for creating the CLAIM note serial number
///
/// # Errors
/// Returns an error if note creation fails.
pub fn create_claim_note<R: FeltRng>(
storage: ClaimNoteStorage,
target_faucet_id: AccountId,
sender_account_id: AccountId,
rng: &mut R,
) -> Result<Note, NoteError> {
let note_storage = NoteStorage::try_from(storage.clone())?;

let attachment = NetworkAccountTarget::new(
storage.output_note_data.target_faucet_account_id,
NoteExecutionHint::Always,
)
.map_err(|e| NoteError::other(e.to_string()))?
.into();
let attachment = NetworkAccountTarget::new(target_faucet_id, NoteExecutionHint::Always)
.map_err(|e| NoteError::other(e.to_string()))?
.into();

let metadata =
NoteMetadata::new(sender_account_id, NoteType::Public).with_attachment(attachment);
Expand Down
4 changes: 2 additions & 2 deletions crates/miden-agglayer/src/errors/agglayer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ pub const ERR_B2AGG_WRONG_NUMBER_OF_ASSETS: MasmError = MasmError::from_static_s
/// Error Message: "bridge not mainnet"
pub const ERR_BRIDGE_NOT_MAINNET: MasmError = MasmError::from_static_str("bridge not mainnet");

/// Error Message: "CLAIM's target account address and transaction address do not match"
pub const ERR_CLAIM_TARGET_ACCT_MISMATCH: MasmError = MasmError::from_static_str("CLAIM's target account address and transaction address do not match");
/// Error Message: "CLAIM note attachment target account does not match consuming account"
pub const ERR_CLAIM_TARGET_ACCT_MISMATCH: MasmError = MasmError::from_static_str("CLAIM note attachment target account does not match consuming account");

/// 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");
Expand Down
8 changes: 6 additions & 2 deletions crates/miden-testing/tests/agglayer/bridge_in.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,14 +210,18 @@ async fn test_bridge_in_claim_to_p2id(#[case] data_source: ClaimDataSource) -> a

let output_note_data = OutputNoteData {
output_p2id_serial_num: serial_num,
target_faucet_account_id: agglayer_faucet.id(),
output_note_tag: NoteTag::with_account_target(destination_account_id),
miden_claim_amount,
};

let claim_inputs = ClaimNoteStorage { proof_data, leaf_data, output_note_data };

let claim_note = create_claim_note(claim_inputs, sender_account.id(), builder.rng_mut())?;
let claim_note = create_claim_note(
claim_inputs,
agglayer_faucet.id(),
sender_account.id(),
builder.rng_mut(),
)?;

// Add the claim note to the builder before building the mock chain
builder.add_output_note(OutputNote::Full(claim_note.clone()));
Expand Down