feat: add lock / unlock functionality to agg_bridge for native miden assets#2771
feat: add lock / unlock functionality to agg_bridge for native miden assets#2771
agg_bridge for native miden assets#2771Conversation
agg_bridge for native miden assets
650b79d to
d1d9938
Compare
Fumuran
left a comment
There was a problem hiding this comment.
Looks good, thank you!
It is a partial review, for now I only reviewed masm code. Mostly formatting suggestions and code optimizations
Fumuran
left a comment
There was a problem hiding this comment.
Rust part looks great, thank you! I left just one question inline
|
@Fumuran I went with your suggestion, and packaged Separately, extracted |
Fumuran
left a comment
There was a problem hiding this comment.
Looks great! For now (I see that currently PR is in draft mode) I have just one small masm-related comment.
…and metadata hash
…h for native tokens
Bumps rand 0.9.2 → 0.9.4 and rand 0.10.0 → 0.10.1 to resolve RUSTSEC-2026-0097 flagged by `cargo deny check` in CI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…masm Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
d5766f2 to
90ca6e4
Compare
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.
90ca6e4 to
e197f46
Compare
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).
|
@mmagician this is ready for another look. Andrey's comments are all addressed and resolved. |
mmagician
left a comment
There was a problem hiding this comment.
Great work, the architecture is spot on ✅ There are still many areas of improvement but nothing fundamentally changing the flow
There was a problem hiding this comment.
Technically, we don't need this component at all, right? If so, we should delete it (could be done in a follow-up).
There was a problem hiding this comment.
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.
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.
|
@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 |
| 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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
we can do this in a follow-up PR
Co-authored-by: Marti <marcin.gorny.94@protonmail.com>
Closes #2700. Adds a lock/unlock path for Miden-native tokens to the AggLayer bridge, mirroring
PolygonZkEVMBridgeV2.claimAsset()'s handling oforiginNetwork == 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_BRIDGEnote withis_native = true. Registration is still admin-gated (register_faucetis caller-restricted); what changed is the ownership requirement.Approach
Native faucet branch, end-to-end:
is_native, calllock_asset→native_account::add_assetto park the asset in the bridge's own vault. NoBURNnote is emitted.is_native, callunlock_and_send→native_account::remove_asset+p2id::newto emit aP2IDnote to the recipient. NoMINTnote, no faucet ntx.PROOF_DATA_KEYis used as the serial number so the note commitment is deterministic per claim.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 newfaucet_metadata_map.A single map with four faucet-ID-keyed sub-keys holds everything:
[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_mapis extended from[1, 0, 0, 0]to[1, is_native, 0, 0], which is backward-compatible since existing entries haveis_native = 0implicitly.CONFIG_AGG_BRIDGEcarries 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_scaleprocs are dead code. Removed: theAggLayerFaucetcomponent now only re-exportsmint_and_send+burnon top ofOwnable2Step+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:
faucet_registry_mapintofaucet_metadata_mapby parkingis_nativein sub-key 1's 4th limb. Needs a new presence check (origin-address-word non-zero) before landing.nameon-chain (AggLayer: store token name in faucet storage #2585).