Skip to content

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

Open
partylikeits1983 wants to merge 14 commits intoagglayerfrom
ajl-agg-bridge-lock-unlock
Open

feat: add lock / unlock functionality to agg_bridge for native miden assets#2771
partylikeits1983 wants to merge 14 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
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.
@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 on lines +117 to +118
#! 1. KEY [0, 0, fid_s, fid_p] -> [addr0, addr1, addr2, addr3] (origin address part 1)
#! 2. KEY [1, 0, fid_s, fid_p] -> [addr4, origin_network, scale, 0] (origin address part 2)
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.

nit: I'm not sure about fid abbreviation, it could be not immediately clear. I would vote for faucet_id_suffux and faucet_id_prefix

Comment thread crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm Outdated
Comment on lines +35 to +40
const REG_TOKEN_HASH_LOC = 0
const REG_FID_S_LOC = 4
const REG_FID_P_LOC = 5
const REG_SCALE_LOC = 6
const REG_ORIGIN_NETWORK_LOC = 7
const REG_IS_NATIVE_LOC = 8
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.

optional nit: I would use more detailed names, though it is indeed not obvious how to express these pointers clearly but briefly

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 on lines +105 to +107
# Drop the remaining pad to start fresh
dropw dropw dropw
# => []
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.

I'm not sure that this will work, since we are in the note script

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I tried removing them and the integration tests fail with when returning from a call

Comment thread crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm Outdated
Comment on lines +143 to +145
# Drop all 16 pad elements and rebuild the stack
dropw dropw dropw dropw
# => []
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.

Same as above

Comment thread crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm Outdated
call.bridge_config::store_faucet_metadata_hash
# => [pad(16)]

dropw dropw dropw dropw
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.

I suppose this works only because we were explicitly padding to the 16 elements before call, but once everything else is updated, we should be able to remove this line

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 and others added 3 commits April 22, 2026 12:05
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>
@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.

The inter-call dropw sequences were removed, so the input pad(12) now
persists across both call boundaries. Update every # => [...] comment
to reflect the true sdepth (verified with sdepth debug.stack drop):
pad(16) through the setup, pad(32) after call 1, pad(48) after call 2,
pad(16) after the final repeat.8 cleanup.
- bridge_config.masm: reword the register_faucet local-offset comment and
  move an implementation detail out of get_faucet_metadata_hash's docstring
  into an inline comment.
- bridge_in_output.masm: document that unlock_and_send's replay safety comes
  from the nullifier check in bridge_in::claim, not serial-number uniqueness.
- bridge_in.rs: add bridge_in_unlock_native_duplicate_rejected. Seeds the
  bridge vault with 2x the claim amount so the nullifier is the only thing
  stopping a second unlock, then asserts the replay fails with
  ERR_CLAIM_ALREADY_SPENT.
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.

3 participants