github agglayer/aggkit v0.11.0-rc1

latest releases: v0.12.0-rc1, v0.11.1, v0.10.2...
pre-release3 months ago

πŸš€ What's New

  • Auto Claim: automatic L1 β†’ L2 bridge claiming (#1662): New opt-in autoclaim component. It discovers eligible exits from l1bridgesync, persists request/cursor/attempt state, prepares claim proofs with l1infotreesync plus a per-claimer l2gersync, submits claimAsset / claimMessage through EthTxManager, and tracks each request to confirmation or failure.

    • Per-destination claimers with named policies: allow-all, api-approve, no-message, basic-filter.
    • API split: public read endpoints (GET /autoclaim/v1/bridges, GET /autoclaim/v1/bridges/:id) are served on the same public REST server as the bridge service (now [PublicREST]) under their own /autoclaim/v1 prefix; admin write endpoints (POST /autoclaim/v1/bridges/:id/approve|reject) live on a separate [AdminREST] server, gated by [AutoClaim.API].Enabled.
    • DryRun flag runs the full pipeline (discovery β†’ policy β†’ proof preparation) without submitting the transaction; requests end in the terminal dry-run status.
    • Per-claimer BlockFinality / InitialBlockNum overrides for the destination-L2 GER syncer (empty/zero falls back to the shared [L2GERSync] values).
    • Admin API hardening: max-length validation on decider, decider_id, reason and a 1 MiB body cap via io.LimitReader.
    • ⚠️ Ships a breaking config rename β€” see Breaking Changes.
  • New tool: exit_certificate (+ exit_certificate_claimer) (#1582): CLI that generates, signs and submits an agglayer Certificate for an aggchain exiting the Agglayer ecosystem. It scans L2 state from genesis to a target block, discovers every value-holding address (ETH + wrapped tokens), computes smart-contract-locked balances, detects unclaimed L1β†’L2 deposits, verifies balances against the agglayer, computes the NewLocalExitRoot (optionally cross-verified against an Anvil shadow-fork of the real bridge contract) and produces a signed certificate whose BridgeExit entries transfer every balance to the destination network.

    • Runs as a resumable step pipeline (CHECK β†’ 0 β†’ A β†’ B β†’ C β†’ D β†’ E β†’ F β†’ G β†’ H β†’ I β†’ SIGN); every step persists its output so single steps, comma lists and ranges (a-c, g-, 0-wait) can be re-run. SUBMIT / WAIT are excluded from the default pipeline so the operator reviews the signed certificate first.
    • exit_certificate_claimer is a companion HTTP service used after the certificate settles on L1: given a destination address it returns the available bridge exits plus every parameter needed to call AgglayerBridge.claimAsset on L1 (merkle proofs, global index, exit roots, leaf fields), with helper scripts and a docker-compose setup.
    • Scope is contained to ./tools/ β€” the only changes outside are new Makefile targets, Dockerfile copies, and the new Kurtosis e2e workflow.
  • New tool: force_ger_update (#1730): Standalone long-running CLI that guarantees the L1 Global Exit Root is updated at least every configurable X. It watches L1 for UpdateL1InfoTree and, when none happens organically within the window, sends a bridgeMessage with forceUpdateGlobalExitRoot = true.

    • Motivation (OP-FEP / DA provability): every GER update appends an L1 info tree leaf embedding an L1 block hash, and the aggchain proof uses that block hash to attest to what happened on L1 β€” including data availability. Anything posted on L1 after the last L1 info root update is covered by no block hash in any L1 info root and therefore cannot be proven; certificate progress stalls, unbounded, until an organic GER update lands. This tool bounds that wait to X.
    • Deliberately lightweight: no syncer, reorg detector or event DB β€” boot scan via chunked FilterLogs, then WS WatchUpdateL1InfoTree (if L1WSURL is set) or FilterLogs polling; sends via the standard zkevm-ethtx-manager; keys via go_signer (local keystore or GCP/AWS KMS).
    • Shipped in the Docker image at /usr/local/bin/force_ger_update.
  • Improved Backward/Forward LET tooling (#1616): Staging-only helper commands for controlled LET validation (craft-cert, send-cert --no-db, cert-status), a new export-cert-exits that generates override files from authoritative cert-ID maps via the AggLayer admin admin_getCertificate, better diagnosis/recovery output and safe-stops for missing cert exits and bridge-service readiness. The in-repo runbook is replaced by a pointer to the public recovery runbook.

  • New bridgeservicefinder package (#1692): Resolves and serves the bridge service URL for every network attached to a rollup manager (New, Start, GetURL(networkID)). Resolution follows a strict priority β€” (1) static config map, (2) on-chain aggchainMetadata["BRIDGE_SERVICE_URL"], (3) trustedSequencerURL() with the port replaced by the bridge REST port 5577 β€” and a polling loop watches SetTrustedSequencerURL / AggchainMetadataSet to refresh the cache live, with health gating. Not yet wired into cmd/run.go or the app config; it is intended for consumption by other components.

  • Per-certificate L2 block range cap (#1673): New optional MaxL2BlockRange caps each certificate's span across PP, FEP, optimistic, builder and validator flows (0 = unlimited, the default).

  • Configurable batch block header retrieval (#1606): RetrieveBlockHeaders moves onto the CustomEthereumClienter interface and a new BatchBlockHeaderRetrieval (default true) lets operators disable batch RPC requests for nodes that do not support them. BlockHeadersResult, ErrNotFound and IsErrNotFound move from etherman to types (aliases kept for backward compatibility).

  • Docs: log availability from genesis (#1610, #1664): docs/usage_ethereum_full_node.md now documents that aggkit syncs contract state with eth_getLogs and therefore needs logs (receipts) available from genesis, not just a recent window. reth prunes receipts by default in full-node mode, so run it with --full --prune.receipts.before=0 (or the equivalent [prune.segments] snippet). Historical state pruning remains acceptable.

⚠️ Breaking Changes

  • πŸ› οΈ Config β€” [REST] is split into [PublicREST] and [AdminREST] (#1662): The single top-level [REST] section (the bridge service HTTP server) is replaced by [PublicREST] (bridge service + Auto Claim public read routes) and [AdminREST] (Auto Claim admin routes only). Existing configs must rename [REST] to [PublicREST]; there is no automatic migration. A leftover [REST] section is currently ignored and logs a deprecation warning at startup (warn-only for now because the e2e infra still generates configs with [REST]).
  • πŸ› οΈ Config: The new [AutoClaim] section is opt-in β€” Auto Claim only runs if autoclaim is in the component list. No migration needed.
  • πŸ”Œ API/CLI: Additive only. New public routes GET /autoclaim/v1/bridges, GET /autoclaim/v1/bridges/:id on [PublicREST]; new admin routes POST /autoclaim/v1/bridges/:id/approve|reject on the new [AdminREST] server. Existing /bridge/v1 paths are unchanged. New tool binaries: exit_certificate, exit_certificate_claimer, force_ger_update.
  • πŸ—‘οΈ Deprecated Features: [REST] (see above). No features removed.

πŸ“‹ Configuration Updates

Required rename:

# Before                       # After
# [REST]                       [PublicREST]
[PublicREST]
Host = "0.0.0.0"
Port = 5577
ReadTimeout = "5m"
WriteTimeout = "5m"
MaxRequestsPerIPAndSecond = 10

# New β€” only used by Auto Claim's admin write routes
[AdminREST]
Host = "0.0.0.0"
Port = 5579
ReadTimeout = "5m"
WriteTimeout = "5m"
MaxRequestsPerIPAndSecond = 10

New optional fields:

[AggSender]
MaxL2BlockRange = 0   # 0 = unlimited (default)

[Validator]
MaxL2BlockRange = "{{AggSender.MaxL2BlockRange}}"

# In [L1NetworkConfig.RPC] and [Common.L2RPC]
BatchBlockHeaderRetrieval = true  # default; disable for nodes without batch RPC support

New opt-in [AutoClaim] section (add "autoclaim" to the component list to enable):

[AutoClaim]
DryRun = false
StoragePath = "{{PathRWData}}/autoclaim.sqlite"

[AutoClaim.API]
Enabled = false

[AutoClaim.L1ToL2BridgeDetector]
Enabled = true
StartBlock = 0
PollInterval = "3s"
RetryAfterErrorPeriod = "1s"
MaxRetryAttemptsAfterError = -1
EtrogL1UpgradeBlock = 0

[AutoClaim.L2ToLxBridgeDetector]
Enabled = false

# One entry per destination network. Add as many [[AutoClaim.Claimers]] blocks as needed.
[[AutoClaim.Claimers]]
Enabled = true
ID = "my-l2"
NetworkType = "evm"
NetworkID = 1
URLRPC = "http://l2-rpc:8545"
BridgeAddr = "0x0000000000000000000000000000000000000000"
PolicyName = "allow-all"   # allow-all | api-approve | no-message | basic-filter
GasOffset = 0
WaitPeriod = "5s"
RetryAfter = "30s"
MaxRetries = 3
# Optional: override the shared [L2GERSync] values for this claimer's destination-L2 GER syncer.
# BlockFinality = "LatestBlock"
# InitialBlockNum = 0

[AutoClaim.Claimers.Policy]
AllowMessageClaims = false   # only relevant for "basic-filter"
AllowedOrigins     = []      # empty = all origins allowed
AllowedTokens      = []      # empty = all tokens allowed
ManualFallback     = false   # fall back to "api-approve" when the filter rejects
MaxGas             = 0       # 0 = unlimited

[AutoClaim.Claimers.EthTxManager]
StoragePath = "{{PathRWData}}/autoclaim-claimer-my-l2-ethtx.sqlite"
# PrivateKeys, ForcedGas, MaxGasPrice, RetryInterval, WaitTxToBeMined, etc.
# See [EthTxManager] in the main config for the full list of fields.

Standalone tool configs (separate files β€” they do not touch the aggkit config):

  • force_ger_update: [ForceGERUpdate] root section (+ nested […EthTxManager] / […EthTxManager.Etherman]). Mandatory and validated: L1URL, GlobalExitRootManagerAddr, BridgeAddr, DestinationNetwork (β‰  0), MaxTimeWithoutGERUpdate (> 0), CheckInterval (> 0), FilterLogsChunkSize (> 0), EventPollInterval (> 0, poll mode only). See tools/force_ger_update/example-config.toml.
  • exit_certificate: JSON or TOML (format selected by file extension). See the tool README.

🐞 Bug Fixes

  • #1709 β€” FEP certificates were retried forever after an L1 info root reorg. On an InError certificate the aggchain prover flow resent the exact same certificate, reusing the finalized L1 info tree root/leaf count and the cached aggchain proof; if the root had been reorged, that certificate (and the imported bridge exit proofs derived from it) was permanently invalid. The InError retry path now validates the certificate's L1 info tree data against the current canonical tree first: still valid β†’ resend unchanged; root mismatch or leaf reorged out β†’ drop the stale proof and rebuild from scratch with fresh data; transient lookup errors β†’ propagated so the retry happens later instead of needlessly discarding the cached proof.
  • #1673 β€” AggSender.MaxL2BlockNumber was not honored in the aggchain flow: the prover flow config was dropped when constructing AggchainProverBuilderFlow, so the limit never reached block-range adjustment for FEP and optimistic certificates.
  • #1645 (cherry-pick of #1639) β€” l1infotreesync stuck in a sync failure loop after upgrading from the legacy syncer (e.g. 0.8.x) to the multidownloader. The processor reported a last-processed checkpoint block the legacy syncer had persisted but the multidownloader's fresh storage never contained, so CheckValidBlock failed with not found in storage or blocks_reorged with no self-recovery (manual DB deletion was the only workaround). For blocks at or below the finalized block, CheckValidBlock now validates against L1 by comparing the canonical header hash: match β†’ valid, resume from lastBlock+1; differ β†’ error (severe inconsistency). Blocks above finalized keep the previous behaviour.
  • #1729 β€” exit_certificate state dumps were silently truncated. The cdk-erigon RPC behind a load balancer intermittently returned an HTTP 200 debug_accountRange page with an empty next cursor before the trie ended (observed on zkEVM mainnet: 735,744 of 899,166 accounts), under-collecting native-ETH holders and under-reporting balances by ~152.7 ETH. The erigon dialect now re-seeks strictly past the highest key seen and only stops when a fresh re-seek genuinely returns no new account. Verified against mainnet: the full 899,166 accounts, matching an independent full-trie walk summing to exactly the 200,000,000 ETH genesis reserve.
  • #1605 β€” Debug-trace bridge call matching now only considers real CALL frames in bridgesync and claimsync, so calldata is never extracted from DELEGATECALL, STATICCALL, CALLCODE or type-less frames. Claim calldata extraction also rejects ambiguous distinct matching bridge calls while still accepting duplicate identical matches.
  • #1600 (cherry-pick of #1589) β€” ParseMaxRangeFromError did not recognise the query exceeds max block range <N> format emitted by some RPC providers, so the ClaimSyncer's RPC fallback returned immediately instead of retrying with chunked requests.
  • #1623 (cherry-pick of #1621) β€” .git/ was excluded from the Docker build context, so git describe / git rev-parse produced empty Version, GitRev and GitBranch in the built binary.
  • #1674 β€” Release candidates did not publish a -dev (shell-enabled) image. The dev variant embedded -dev inside the semver pattern, and docker/metadata-action discards a custom semver pattern for pre-releases; moving -dev to the suffix= attribute makes rc tags yield X.Y.Z-rcN-dev. The same PR publishes a -no_shell alias for the current production image ahead of the shell-by-default migration (#1671), so consumers needing the hardened image can switch to *-no_shell today.

🏷️ Image tags

Variant Shell Final release (v0.11.0) Release candidate (v0.11.0-rc1)
production ❌ 0.11.0, 0.11, 0.11.0-no_shell, 0.11-no_shell 0.11.0-rc1, 0.11.0-rc1-no_shell
development βœ… 0.11.0-dev, 0.11-dev 0.11.0-rc1-dev

For pre-releases docker/metadata-action only extends {{version}}, so {{major}}.{{minor}} tags are intentionally not produced.

βœ… Testing & Validation

  • Auto Claim (#1662): wired into the OP e2e environment alongside unit coverage for the component, policies and admin API.
  • exit_certificate (#1582): new Kurtosis-based e2e suite under test/e2e-exit_certificate/ with its own CI workflow.
  • force_ger_update (#1730): Tier-1 unit + simulated-backend integration tests (go test -race ./tools/force_ger_update/..., both watch and poll modes) and a Tier-2 real-network e2e, TestForceGERUpdateE2E, now running in CI on its own isolated job (test-go-e2e-force-ger-update) instead of being permanently skipped. It proves causality β€” the new GER update's tx From equals the tool sender and the input starts with the 0x240ff378 selector.
  • bridgeservicefinder (#1692): extensive simulated-backend unit tests covering each URL source in isolation, all priority combinations, config immunity to on-chain updates, live updates via both event types and health-gating edge cases, plus a smoke test asserting the new mock contracts are selector/event-compatible with the real bindings.
  • FEP reorg fix (#1709): unit tests for reorged L1 info root, leaf reorged out, and validation-error propagation.
  • Legacy checkpoint fix (#1645): TestEVMMultidownloader_CheckValidBlock_LegacyCheckpointAfterUpgrade against real empty storage, covering all three paths.
  • Backward/Forward LET (#1616): end-to-end staging validation completed; final no-override diagnosis returned NoDivergence.
  • Local e2e ergonomics (#1591): run-local-e2e.sh now uses ${TMPDIR:-/tmp} and deterministic temp-dir names derived from the commit hash / repo URL, reusing an already-cloned repo instead of re-cloning on every run.

πŸ”§ Maintenance & Dependencies

  • Grouped Dependabot updates (#1733, #1734): all Dependabot bumps now land as a single grouped PR. Notable in this release: go-ethereum 1.17.2 β†’ 1.17.4, grpc 1.80.0 β†’ 1.81.0, go-sqlite3 1.14.42 β†’ 1.14.48, zap 1.27.1 β†’ 1.28.0, go-toml/v2 2.3.0 β†’ 2.4.3, prometheus/client_golang 1.23.2 β†’ 1.24.0, invopop/jsonschema 0.13.0 β†’ 0.14.0, plus the agglayer protobuf/grpc modules.
    • ⚠️ Note for maintainers: buf.build/gen/go/agglayer/* modules are now ignored by Dependabot. Their pseudo-versions encode the protobuf-go generator version in the semver prefix while the actual schema is identified by the embedded commit timestamp, so a v1.36.11 module can carry an older schema than a v1.36.10 one. MVS picking the highest semver rolled interop back to a 2025-05-19 snapshot predating the Multisig / ECDSAMultisig / AggchainProofWithMultisig types and broke the build; these first-party modules are now bumped deliberately, together.
  • SonarQube/SonarCloud removed (#1719): part of the org-wide deprecation, consolidating on CodeQL + GitHub Advanced Security. CI-only.
  • Claude PR review is mention-only (#1695): the automatic review trigger was removed.

πŸ“¦ Full Changelog

New Contributors

Full Changelog: v0.10.0-rc1...v0.11.0-rc1

Don't miss a new aggkit release

NewReleases is sending notifications on new releases.