Skip to content

feat(agglayer): add GER removal mechanism#2837

Open
partylikeits1983 wants to merge 9 commits into
nextfrom
ajl-remove-ger
Open

feat(agglayer): add GER removal mechanism#2837
partylikeits1983 wants to merge 9 commits into
nextfrom
ajl-remove-ger

Conversation

@partylikeits1983

@partylikeits1983 partylikeits1983 commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Closes #2702.

Adds a GER removal mechanism mirroring Solidity's
GlobalExitRootManagerL2SovereignChain.removeGlobalExitRoots:

  • New ger_remover role (separate from the GER manager) stored in the bridge account.
  • New remove_ger MASM proc in bridge_config.masm that asserts the sender is the GER remover, asserts the GER is currently registered, clears ger_map[ger] to [0,0,0,0], and folds the removed GER into a running keccak256 hash chain (new_chain = keccak256(prev_chain || ger)) — same shape as the existing update_cgi_chain_hash pattern in bridge_in.masm.
  • New REMOVE_GER note script + RemoveGerNote Rust helper.
  • New storage slots: ger_remover_account_id, removed_ger_hash_chain_lo, removed_ger_hash_chain_hi.
  • Three new integration tests: happy path (removes + chain advances to keccak256(0…0 || ger)), unknown-GER reverts with ERR_GER_NOT_FOUND, non-remover sender reverts with ERR_SENDER_NOT_GER_REMOVER.
  • Spec updated: replaced the GER-removal TODO with the new flow.

AggLayerBridge::new, create_bridge_account, and create_existing_bridge_account now take a third ger_remover_id argument; all existing call sites (tests + bench-transaction) updated.

@partylikeits1983 partylikeits1983 changed the title feat(agglayer): add GER removal mechanism (#2702) feat(agglayer): add GER removal mechanism Apr 27, 2026
@partylikeits1983 partylikeits1983 self-assigned this Apr 27, 2026
@partylikeits1983 partylikeits1983 force-pushed the ajl-remove-ger branch 2 times, most recently from 8ebfee1 to 82b6703 Compare April 27, 2026 16:27
@partylikeits1983 partylikeits1983 marked this pull request as ready for review April 29, 2026 21:53
@partylikeits1983 partylikeits1983 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 29, 2026
@partylikeits1983 partylikeits1983 marked this pull request as draft May 11, 2026 22:05
@partylikeits1983

Copy link
Copy Markdown
Contributor Author

Putting into draft mode until v0.15.0

@partylikeits1983 partylikeits1983 added this to the v0.16.0 milestone Jun 1, 2026
@partylikeits1983 partylikeits1983 changed the base branch from agglayer to next June 10, 2026 03:28
@partylikeits1983 partylikeits1983 marked this pull request as ready for review June 10, 2026 03:28
Adds a remove_ger MASM procedure gated by a new ger_remover role
(separate from the existing GER manager) that revokes a previously
registered Global Exit Root and folds it into a running keccak256 hash
chain. Mirrors the Solidity sovereign-chain
removeGlobalExitRoots / removedGERHashChain pair. Adds a REMOVE_GER
note script, a RemoveGerNote Rust helper, integration tests, and
updates the spec.
… in SPEC

Updates the sections that were missed when the GER removal mechanism
was added: the administration section now lists the GER remover as a
third role, the bridge component procedure list includes remove_ger,
the storage table covers the ger_remover_account_id and
removed_ger_hash_chain_lo/hi slots, and a new section 4.5 specifies
the REMOVE_GER note (BURN/P2ID/MINT renumbered to 4.6-4.8).
- remove_ger now clears the map entry with a single set_map_item and
  asserts the returned OLD_VALUE equals GER_KNOWN_FLAG, matching the
  update_ger idiom and dropping one SMT operation per removal.
- Extract the duplicated felt-to-LE-bytes conversion shared by
  cgi_chain_hash and removed_ger_hash_chain into a chain_hash_bytes
  helper, and return a RemovedGerHashChain newtype (Keccak256Output)
  instead of a raw byte array, mirroring CgiChainHash.
- Deduplicate the per-test wallet/bridge setup in the remove_ger
  integration tests into a setup_bridge helper.

@mmagician mmagician left a comment

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.

Approved modulo:

  • a few general questions
  • SPEC needs more info

the refactors (code de-dup) are optional and not even sure if possible cleanly, just a direction to explore

#! - the GER is not currently registered in the bridge's GER map.
#!
#! Invocation: call
pub proc remove_ger

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.

very clean 👍🏼

Comment on lines +670 to +676
#! Loads the old removed-GER hash chain onto the stack, below the existing GER preimage.
#!
#! Inputs: [GER_LOWER[4], GER_UPPER[4]]
#! Outputs: [OLD_CHAIN[8], GER_LOWER, GER_UPPER]
#!
#! Invocation: exec
proc load_removed_ger_hash_chain_data

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 this procedure doesn't do anything with the inputs, and we usually just mark it as [] (i.e. it only appends elements to the output)

#! Outputs: []
#!
#! Invocation: exec
proc update_removed_ger_hash_chain

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.

Question: what is the initial value of the OLD_CHAIN when we haven't removed anything yet?
And does it match solidity's reference?

Comment on lines +69 to +70
call.bridge_config::remove_ger
# => [pad(16)]

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.

It looks like this note script is very similar to the GER injection script, with the only difference being in the procedure called on the bridge account. I wonder if we could re-use any code?

Comment on lines +189 to +190
ger_manager_id: AccountId,
ger_remover_id: AccountId,

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 would re-work the naming a bit.
"manager" sounds like it can already remove items, which is not the case here.
So maybe ger_injector or ger_updater? Though they only sound half good :)

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.

similar comment as for the MASM note script, it would be great if we could re-use some code between the update and remove note, as these files are almost identical now

Comment thread crates/miden-agglayer/SPEC.md Outdated
Comment on lines +128 to +129
A separate GER Remover role can revoke a previously-registered GER by sending a
`REMOVE_GER` note. The bridge consumes such a note and:

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 would add a note somewhere in the spec for the conditions under which a GER removal can/is expected to happen, and its impact on the Miden chain. This is non-trivial, it likely means that something has gone wrong upstream in AggLayer IIUC, but would be good to research the exact conditions & consequences in more detail so we know why we have the removal in the first place.

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.

Also, worth noting that the manager can re-register a removed GER, so a compromised manager can undo the REMOVE_GER emergency patch and re-open the attack window.

This is mitigated only if the manager is rotated out, but this is currently unimplemented until #2706 lands.

Comment on lines +317 to +321
/// Tests that calling REMOVE_GER twice on the same GER reverts the second call with
/// ERR_GER_NOT_FOUND. Locks in the invariant that a removed entry stays at [0,0,0,0]
/// and cannot be re-removed.
#[tokio::test]
async fn remove_ger_double_remove_reverts() -> anyhow::Result<()> {

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 this test subsumes one of the previous tests which just verifies ERR_GER_NOT_FOUND

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.

wow that's pretty comprehensive

Comment on lines +43 to +62
let mut builder = MockChain::builder();

let bridge_admin = builder.add_existing_wallet(Auth::BasicAuth {
auth_scheme: AuthScheme::Falcon512Poseidon2,
})?;
let ger_manager = builder.add_existing_wallet(Auth::BasicAuth {
auth_scheme: AuthScheme::Falcon512Poseidon2,
})?;
let ger_remover = builder.add_existing_wallet(Auth::BasicAuth {
auth_scheme: AuthScheme::Falcon512Poseidon2,
})?;

let bridge_seed = builder.rng_mut().draw_word();
let bridge_account = create_existing_bridge_account(
bridge_seed,
bridge_admin.id(),
ger_manager.id(),
ger_remover.id(),
);
builder.add_account(bridge_account.clone())?;

@mmagician mmagician Jun 11, 2026

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.

AFAICS most (all?) of these tests share the same setup

Comment thread crates/miden-agglayer/src/bridge.rs Outdated
Comment on lines +435 to +437
pub fn removed_ger_hash_chain(
bridge_account: &Account,
) -> Result<[u8; 32], AgglayerBridgeError> {

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.

nit: rather than using a bare [u8; 32], let's have a stronger type, I think Keccak256Output should suffice, or maybe a dedicated wrapper if it makes more sense for some reason?

…alue

- load_removed_ger_hash_chain_data only appends OLD_CHAIN and passes the GER
  preimage through untouched, so its doc declares Inputs: [] / Outputs: [OLD_CHAIN[8]].
- Document that the chain seeds from the empty word [0, 0, 0, 0], so the first
  removal yields keccak256(0..0 || ger), matching the zero-initialized
  removedGERHashChain bytes32 in Solidity's GlobalExitRootManagerL2SovereignChain.
…veat

Adds the conditions under which a GER removal is expected (an exceptional,
emergency-only response to an invalid/erroneous upstream root) and its impact on
the Miden chain (unprocessed CLAIMs against the removed GER revert; already-processed
claims are unaffected). Makes explicit that a compromised or faulty GER manager can
re-register a removed GER and undo the emergency patch, bounded only once role
rotation lands (#2706).
remove_ger_double_remove_reverts already exercises the ERR_GER_NOT_FOUND path
(and the stronger invariant that a removed entry stays at [0,0,0,0] and cannot be
re-removed), subsuming the standalone never-registered case.
…d REMOVE_GER

UpdateGerNote::create and RemoveGerNote::create built structurally identical notes
(8 felts of GER storage, a network-account target on the bridge, public metadata, no
assets), differing only in the note script. Extract the shared body into a crate-internal
create_ger_note helper that each note type calls with its own script.
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.

AggLayer: Add GER removal mechanism

2 participants