Skip to content

feat: backward and forward LET testing + runbook + tool #1502

Draft
arnaubennassar wants to merge 17 commits intodevelopfrom
feat/back-and-for-let
Draft

feat: backward and forward LET testing + runbook + tool #1502
arnaubennassar wants to merge 17 commits intodevelopfrom
feat/back-and-for-let

Conversation

@arnaubennassar
Copy link
Collaborator

@arnaubennassar arnaubennassar commented Mar 2, 2026

🔄 Changes Summary

  • BackwardLET/ForwardLET bug fixes: Fixed two bugs in bridgesync/processor.go that caused the L2 bridge syncer to halt during BackwardLET+ForwardLET recovery: off-by-one in
    handleForwardLETEvent (PreviousDepositCount is already the next leaf index, no +1 needed) and incorrect > vs >= in archiveAndDeleteBridgesAbove (bridges at exactly DC=N must also be
    archived after a BackwardLET to DC=N).
  • BackwardLET to DC=0: Special-cased BackwardToIndex for DC=0 to use exitTree.Reorg(0) (full clear) since BackwardToIndex(-1) is invalid.
  • GetBridgeByDepositCount query fix: Removed AND origin_network = 0 filter — deposit_count is a unique monotonic counter per bridge event and filtering by origin_network=0 incorrectly
    excluded L2-native token bridges.
  • Empty LER sanity check: Added special-case handling in sanityCheckLatestLER and handleForwardLETEvent for the empty LER (empty Merkle tree root maps to 0x00...0, not to the standard
    empty hash).
  • aggsender fromBlock derivation fix: flow_base.go now re-derives fromBlock from on-chain/bridgesync data for settled certificates using the same logic as the local validator, preventing
    stale ToBlock=0 values (e.g. from the debug endpoint) from breaking subsequent certs.
  • Debug SendCertificate RPC endpoint: Added opt-in aggsender_debugSendCertificate RPC method for manual recovery; when enabled, the normal cert-sending loop is disabled. Gated behind
    EnableDebugSendCertificate config flag (default false; never enable in production).
  • New aggsender_getCertificateBridgeExits RPC: Returns stored bridge exits for a given certificate height, used by the recovery tool to reconstruct certificates without agglayer access.
  • BackwardForwardLET recovery tool (tools/backward_forward_let/): New CLI tool that diagnoses BackwardLET/ForwardLET divergences and drives automated recovery by replaying bridge exits
    from the aggsender DB or agglayer admin API.
  • E2E tests (test/e2e/backwardforwardlet_test.go): 5 new E2E tests covering BackwardLET/ForwardLET scenarios (no divergence, Case 1–4, and aggsender API fallback).
  • MintableERC20 test contract: Added for use in BFL E2E tests that need L2-native token bridges.

⚠️ Breaking Changes

  • 🛠️ Config: Two new optional fields added to [AggSender] in TOML — no breaking change if omitted (defaults to false/zero address).

📋 Config Updates

  [AggSender]
  # Optional — default false. NEVER enable in production.
  EnableDebugSendCertificate = false
  # Address whose signature is required to use the debug endpoint.
  DebugSendCertificateAuthAddress = "0x0000000000000000000000000000000000000000"

✅ Testing

  • 🤖 Automatic:
    • TestBackwardForwardLET_NoDivergence — baseline: normal operation, no LET events
    • TestBackwardForwardLET_Case1 — single BackwardLET then ForwardLET, no divergence
    • TestBackwardForwardLET_Case2 — BackwardLET to DC=0 (full tree wipe) then ForwardLET
    • TestBackwardForwardLET_Case3 — multiple BackwardLET/ForwardLET cycles
    • TestBackwardForwardLET_Case4 — divergence requiring the recovery tool
    • TestBackwardForwardLET_AggsenderAPIFallback — recovery with empty aggsender DB, falling back to agglayer admin API

🐞 Issues

🔗 Related PRs

📝 Notes

  • The debug SendCertificate endpoint is intended for manual recovery only (BackwardLET/ForwardLET scenarios where the aggsender is stuck). When EnableDebugSendCertificate = true, the
    normal sending loop is fully disabled — this is intentional to prevent conflicts between the operator's manual certificates and the automatic sender.
  • docs/backward_forward_let_runbook.md and tools/backward_forward_let/RECOVERY_PROCEDURE.md contain full operator runbooks.
  • The deposit_count uniqueness assumption (used to drop origin_network = 0 from GetBridgeByDepositCount) holds because the L1 escape hatch contract assigns a strictly monotonic global
    counter regardless of token type.

@arnaubennassar arnaubennassar marked this pull request as draft March 2, 2026 10:05
@arnaubennassar arnaubennassar self-assigned this Mar 2, 2026
@arnaubennassar arnaubennassar changed the title Feat/back and for let feat: backward and forward LET testing + runbook + tool Mar 2, 2026
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 2, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
2 Security Hotspots
76.5% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@agglayer agglayer deleted a comment from claude bot Mar 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant