Skip to content

feat: add lock / unlock functionality to agg_bridge for native miden assets#2771

Merged
mmagician merged 30 commits intoagglayerfrom
ajl-agg-bridge-lock-unlock
May 7, 2026
Merged

feat: add lock / unlock functionality to agg_bridge for native miden assets#2771
mmagician merged 30 commits intoagglayerfrom
ajl-agg-bridge-lock-unlock

Conversation

@partylikeits1983
Copy link
Copy Markdown
Contributor

@partylikeits1983 partylikeits1983 commented Apr 14, 2026

Closes #2700. Adds a lock/unlock path for Miden-native tokens to the AggLayer bridge, mirroring PolygonZkEVMBridgeV2.claimAsset()'s handling of originNetwork == networkID.

The bridge no longer needs to be the faucet's owner to support it, so user-created faucets are now bridgeable once the bridge admin registers them via a CONFIG_AGG_BRIDGE note with is_native = true. Registration is still admin-gated (register_faucet is caller-restricted); what changed is the ownership requirement.

Approach

Native faucet branch, end-to-end:

  • Bridge-out: if is_native, call lock_assetnative_account::add_asset to park the asset in the bridge's own vault. No BURN note is emitted.
  • Bridge-in: if is_native, call unlock_and_sendnative_account::remove_asset + p2id::new to emit a P2ID note to the recipient. No MINT note, no faucet ntx. PROOF_DATA_KEY is used as the serial number so the note commitment is deterministic per claim.
  • LET leaf construction and the existing non-native burn/mint flow are untouched.

Metadata moved to the bridge. All three FPI calls from the bridge into the faucet (asset_to_origin_asset, get_metadata_hash, get_scale) are replaced with bridge-local reads out of a new faucet_metadata_map.

A single map with four faucet-ID-keyed sub-keys holds everything:

Sub-key Value
[0, 0, fid_s, fid_p] [addr0, addr1, addr2, addr3]
[1, 0, fid_s, fid_p] [addr4, origin_network, scale, 0]
[2, 0, fid_s, fid_p] [mh_lo0..3]
[3, 0, fid_s, fid_p] [mh_hi0..3]

faucet_registry_map is extended from [1, 0, 0, 0] to [1, is_native, 0, 0], which is backward-compatible since existing entries have is_native = 0 implicitly. CONFIG_AGG_BRIDGE carries the full metadata payload at registration (split across two calls to fit the 16-element stack).

AggLayer faucet slimmed. With metadata on the bridge, the faucet's conversion-info / metadata-hash slots and its asset_to_origin_asset / get_metadata_hash / get_scale procs are dead code. Removed: the AggLayerFaucet component now only re-exports mint_and_send + burn on top of Ownable2Step + OwnerControlled. A Miden-native faucet which wants to integrate with Agglayer, now is just a plain network fungible faucet, no AggLayer-specific storage or FPI surface needed.

Follow-ups

Not in this PR; filing separately per @bobbinth's comments in the original issue for this PR:

@partylikeits1983 partylikeits1983 changed the title feat: add scale data to bridge storage feat: add lock / unlock functionality to agg_bridge for native miden assets Apr 16, 2026
@mmagician mmagician added agglayer PRs or issues related to AggLayer bridging integration pr-from-maintainers PRs that come from internal contributors or integration partners. They should be given priority labels Apr 16, 2026
@partylikeits1983 partylikeits1983 force-pushed the ajl-agg-bridge-lock-unlock branch from 650b79d to d1d9938 Compare April 17, 2026 17:47
@partylikeits1983 partylikeits1983 marked this pull request as ready for review April 20, 2026 17:27
@partylikeits1983 partylikeits1983 self-assigned this Apr 20, 2026
Copy link
Copy Markdown
Contributor

@Fumuran Fumuran left a comment

Choose a reason for hiding this comment

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

Looks good, thank you!
It is a partial review, for now I only reviewed masm code. Mostly formatting suggestions and code optimizations

Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Outdated
Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Outdated
Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm
Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Outdated
Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Outdated
Comment thread crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm Outdated
Comment thread crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm Outdated
Comment thread crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm Outdated
Comment thread crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm Outdated
Comment thread crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm Outdated
Copy link
Copy Markdown
Contributor

@Fumuran Fumuran left a comment

Choose a reason for hiding this comment

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

Rust part looks great, thank you! I left just one question inline

Comment thread crates/miden-agglayer/src/config_note.rs Outdated
@partylikeits1983
Copy link
Copy Markdown
Contributor Author

@Fumuran I went with your suggestion, and packaged ConfigAggBridgeNote::create's six faucet-metadata parameters into a new ConversionMetadata struct with a to_elements() helper.

Separately, extracted build_mint_output_note, unlock_and_send, and their MINT/UNLOCK memory constants from bridge_in.masm into a new bridge_in_output.masm module (otherwise bridge_in.masm was getting too big), and updated the SPEC reference accordingly.

Copy link
Copy Markdown
Contributor

@Fumuran Fumuran left a comment

Choose a reason for hiding this comment

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

Looks great! For now (I see that currently PR is in draft mode) I have just one small masm-related comment.

Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm Outdated
Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm Outdated
@partylikeits1983 partylikeits1983 force-pushed the ajl-agg-bridge-lock-unlock branch from d5766f2 to 90ca6e4 Compare April 30, 2026 02:09
@partylikeits1983 partylikeits1983 marked this pull request as ready for review April 30, 2026 02:10
Make CLAIM_OUTPUT_NOTE_FAUCET_AMOUNT and CLAIM_PROOF_DATA_KEY_MEM_ADDR
public in bridge_in.masm and import them from bridge_in_output.masm,
removing the duplicate definitions and the "keep in sync" comments.
Move UNLOCK_*_LOC consts to sit immediately above unlock_and_send so the
local-memory layout lives next to the procedure that uses it.
@partylikeits1983 partylikeits1983 force-pushed the ajl-agg-bridge-lock-unlock branch from 90ca6e4 to e197f46 Compare April 30, 2026 02:13
Resolves conflicts in faucet/mod.masm and src/faucet.rs by taking ours
(metadata moved to bridge); accepts upstream deletion of errors/agglayer.rs
(now generated into OUT_DIR by build.rs).
@partylikeits1983
Copy link
Copy Markdown
Contributor Author

partylikeits1983 commented Apr 30, 2026

@mmagician this is ready for another look. Andrey's comments are all addressed and resolved.

Copy link
Copy Markdown
Collaborator

@mmagician mmagician left a comment

Choose a reason for hiding this comment

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

Great work, the architecture is spot on ✅ There are still many areas of improvement but nothing fundamentally changing the flow

Comment thread crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm Outdated
Comment thread crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm Outdated
Comment thread crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm Outdated
Comment thread crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm Outdated
Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Outdated
Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm
Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_in_output.masm Outdated
Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_in_output.masm
Comment thread crates/miden-agglayer/SPEC.md Outdated
Comment thread crates/miden-agglayer/SPEC.md
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Technically, we don't need this component at all, right? If so, we should delete it (could be done in a follow-up).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think so, although I'd probably wait till we're done with all the agglayer issues, in case we find out something unforeseen in how the agglayer faucet should handle things.

partylikeits1983 and others added 13 commits May 5, 2026 09:27
Co-authored-by: Marti <marcin.gorny.94@protonmail.com>
Resolve conflicts so this PR's lock/unlock + faucet_metadata_map architecture
keeps working with the agglayer changes:

- 0003d0a (#2860): faucet registry now keyed by (origin_network,
  origin_token_address). Threaded origin_network into hash_token_address and
  lookup_faucet_by_token_address. Inside register_faucet, byte-swap
  origin_network before hashing so the hash input matches the LE-packed form
  emitted by claim_note.rs (which is what lookup_faucet_by_token_address
  receives from leaf data). faucet_metadata_map keeps origin_network in raw
  form so bridge_out::convert_asset's existing swap_u32_bytes still produces
  the correct leaf representation.
- 19ecb8b (#2848 / cherry-pick of #2759): bridge_in / bridge_out / SPEC
  updates auto-merged, with assert_destination_id_not_miden_id helper kept on
  the bridge-out side.
- 4e4a496 (#2752): selective LET frontier load/save.

Tests: all 811 nextest cases pass after merge. Renamed PR-side
SimulatedL1ToMiden / SimulatedL2ToMiden / RealL1ToMiden references to the
agglayer-renamed L1ToMiden / L2ToMiden variants (RealL1ToMiden's JSON vector
was deleted in agglayer). Ported the agglayer #2799 cross-network regression
test (test_claim_fails_when_origin_network_unregistered) to the PR's new
ConfigAggBridgeNote::create / create_existing_agglayer_faucet signatures.
…G_AGG_BRIDGE.masm

The 18-felt note storage layout was documented in three places:
the top-of-file comment block, the constant definitions immediately
below, and the docblock above 'begin'. The constants are self-documenting
and the docblock covers it for callers, so drop the top-of-file block.
Mirror mem_store_double_word_unaligned in asm/agglayer/common/utils.masm
and use it in CONFIG_AGG_BRIDGE.masm to load the metadata-hash double word
from offset 10..17, replacing eight individual mem_load.METADATA_HASH_*
ops with a single push.METADATA_HASH_LO_0 exec.utils::mem_load_double_word_unaligned.
… movdn dance

Push the call's 6 trailing pad zeros first, then build the args on top, instead
of pushing args first and then doing 'movdn.15 movdn.15 movdn.15 movdn.15 movdn.15
movdn.15' to move 6 pads to the bottom. The end-state stack is identical; this
just removes a 6-instruction shuffle from each of the two call sites.
Replace literal push.0.0 / push.0.1 / push.0.2 / push.0.3 sub-key
prefixes with named constants FAUCET_METADATA_SUBKEY_{ADDR_LO,ADDR_HI,
HASH_LO,HASH_HI}. Only the metadata-map sites are touched; push.0.0 in
faucet_registry / token_registry contexts is a different map and stays
literal.
…movup.5

Replace movup.9/.8/.7/.6/.5 with five repeated movup.5 starting from
the now-deepest non-address element. Each movup always lifts from depth
5 (instead of climbing 9→5), and the post-iteration stack is identical.
Replace 'dup.4 dup.4 dup.4 dup.4 dup.4' with 'repeat.5 dup.4 end'. The
five-fold dup-from-depth-4 produces the same stack pattern (top word is
the duplicate address with addr0 at the very top) which is what
hash_token_address expects when it stores the 5-felt address into local
memory before computing Poseidon2.
…ing proc

Rewrite get_faucet_metadata_hash to use the same 'prep both keys
first, then swapw between reads' pattern as get_faucet_conversion_info.
The previous shape used 'dup.1 dup.1 ... movup.5 movup.5' to reshuffle
faucet_id around the first read, which read awkwardly next to a sibling
proc with the opposite mechanism. This subsumes the swapw / movup.5
movup.5 oddity Marti called out.
Re-walk stack annotations from proc-entry through Steps 2-4. Pads grow
on pops (via Miden's auto-padding to depth-16 floor) and stay constant
on pushes; the previous comments treated them as if pad+named always
equaled 16, which understated the depth after each new push.
'sink ... below the trailing zeros' was misleading because the stack
direction it implied is the opposite of what movdn.2 does. Reword to
'move is_native past the two trailing zeros'.
The previous comment referenced 'claim's CLAIM_DEST_ID_*_LOCAL' and the
'exec invocations get their own local frame' detail, both of which are
distracting. State the actual reason for the stash: we'll need the
destination ID again after native_account::remove_asset clears the stack.
…e flows

- Section 2.1 (bridge-out): drop FPI references, replace with bridge-local
  faucet_metadata_map reads. Add the is_native dispatch (BURN vs lock_asset).
- Section 2.2 (bridge-in): drop the unconditional MINT description, replace
  with the is_native dispatch (MINT vs remove_asset + direct P2ID). Update
  the token-registry lookup to mention the (address, network) pair.
- Section 2.4 (faucet registration): describe the CONFIG_AGG_BRIDGE 18-felt
  payload and the two-call register_faucet / store_faucet_metadata_hash flow.
- Section 7.1: rename to 'Registering faucets on Miden' (covers both wrapped
  and Miden-native faucets), and add a new sub-section explaining the
  wrapped-vs-native distinction at the dispatch level.
@partylikeits1983
Copy link
Copy Markdown
Contributor Author

partylikeits1983 commented May 5, 2026

@mmagician Thank you for the in depth review! I addressed all of your comments, I think the logic & doc comments are much clearer now. Lmk what you think

Copy link
Copy Markdown
Collaborator

@mmagician mmagician left a comment

Choose a reason for hiding this comment

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

LGTM!

Comment thread crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm Outdated
Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Outdated
Comment on lines +345 to +349
push.0.FAUCET_METADATA_SUBKEY_ADDR_LO
# => [SUBKEY_ADDR_LO, 0, faucet_id_suffix, faucet_id_prefix]

# Prepare the SUBKEY_ADDR_HI KEY on top.
dup.3 dup.3 push.0.FAUCET_METADATA_SUBKEY_ADDR_HI
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This doesn't look right:
dup.3 does not duplicate faucet_id_{prefix/suffix} because the SUBKEY_ADDR_LO is a word, not a single element

Copy link
Copy Markdown
Collaborator

@mmagician mmagician May 7, 2026

Choose a reason for hiding this comment

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

oh I see, it's not a word, but then we should probably not use uppercase for it (as const <CONST NAME> yes, but not on stack comments)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

we can do this in a follow-up PR

Co-authored-by: Marti <marcin.gorny.94@protonmail.com>
@mmagician mmagician enabled auto-merge May 7, 2026 12:54
@mmagician mmagician added this pull request to the merge queue May 7, 2026
Merged via the queue into agglayer with commit 9c9f847 May 7, 2026
15 checks passed
@mmagician mmagician deleted the ajl-agg-bridge-lock-unlock branch May 7, 2026 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agglayer PRs or issues related to AggLayer bridging integration pr-from-maintainers PRs that come from internal contributors or integration partners. They should be given priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants