feat: new tool - exit_certificate#1582
Conversation
|
Claude finished @krlosMata's task in 4m 37s —— View job PR Review: exit_certificate Tool ✅🚀 What's NewA new standalone CLI tool ( 🐛 Bug FixesNone. 📋 Config UpdatesNew config file: Required fields:
Optional fields:
|
6d18df4 to
85a1fa9
Compare
There was a problem hiding this comment.
Pull request overview
Adds a new standalone exit-certificate CLI tool under tools/exit_certificate/ to generate Agglayer exit certificates for aggchain migration by scanning L2/L1 state and producing an agglayer/types.Certificate with BridgeExit entries.
Changes:
- Implements a 6-step pipeline (0 → A → B → C → D → E) for LBT generation, address discovery, balance scanning, SC-locked computation, certificate building, and unclaimed L1→L2 deposit detection.
- Adds JSON-RPC batching/concurrency utilities plus step-wise resumable output persisted to an output directory.
- Adds unit/integration tests and documentation/config examples for running the tool.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/exit_certificate/worker.go | Generic worker-pool helper used to parallelize step workloads. |
| tools/exit_certificate/types.go | Shared data structures for step outputs and deposit/event models. |
| tools/exit_certificate/step_0.go | Step 0 implementation: generate LBT by scanning bridge logs + supplies. |
| tools/exit_certificate/step_a.go | Step A implementation: scan blocks/txs and trace touched addresses. |
| tools/exit_certificate/step_b.go | Step B implementation: EOA/contract classification and balance scanning. |
| tools/exit_certificate/step_c.go | Step C implementation: compute SC-locked values from LBT vs EOA totals. |
| tools/exit_certificate/step_d.go | Step D implementation: build the exit certificate BridgeExits. |
| tools/exit_certificate/step_e.go | Step E implementation: scan L1 BridgeEvents and add unclaimed deposits. |
| tools/exit_certificate/rpc.go | JSON-RPC client utilities: batch/single RPC, retries, concurrency batching. |
| tools/exit_certificate/hex.go | Hex/decimal parsing helpers and ABI safety conversions. |
| tools/exit_certificate/config.go | Config loading/validation, defaults, and LBT file parsing helpers. |
| tools/exit_certificate/run.go | CLI execution wiring: full pipeline + step-by-step resumability and I/O. |
| tools/exit_certificate/cmd/main.go | CLI binary entrypoint using urfave/cli. |
| tools/exit_certificate/README.md | Tool documentation: config, steps, usage, outputs, testing. |
| tools/exit_certificate/.gitignore | Ignore local parameters/output/binary artifacts for this tool. |
| tools/exit_certificate/parameters.json.example | Example standalone JSON config for running the tool. |
| tools/exit_certificate/step_a_test.go | Unit tests for hex block parsing helper(s) used in Step A. |
| tools/exit_certificate/step_b_test.go | Unit tests for hex-to-bigint helper used in Step B. |
| tools/exit_certificate/step_c_test.go | Unit tests for SC-locked computation behavior and edge cases. |
| tools/exit_certificate/step_d_test.go | Unit tests for certificate construction from EOA + SC-locked inputs. |
| tools/exit_certificate/step_e_test.go | Unit tests for BridgeEvent decoding and claimed-set filtering logic. |
| tools/exit_certificate/rpc_test.go | Unit tests for batch/single RPC, retry behavior, and error handling. |
| tools/exit_certificate/run_test.go | Unit tests for block parsing and JSON save/load helpers. |
| tools/exit_certificate/config_test.go | Unit tests for config parsing, defaults, and LBT parsing helpers. |
| tools/exit_certificate/integration_test.go | Integration-style tests for production-like config/data shapes (skippable). |
| var bridgeEventTopic = common.HexToHash("0x501781209a1f8899323b96b4ef08b168df93e0a90c673d1e4cce39f97571d4d7") | ||
|
|
||
| // claimEventTopic is keccak256("ClaimEvent(uint256,uint32,address,address,uint256)"). | ||
| var claimEventTopic = common.HexToHash("0x25308c93ceeed162da955b3f7ce3e3f93606579e40fb92029faa9efe27545983") |
There was a problem hiding this comment.
It ignores old zkevm claimEvent
claimEventSignaturePreEtrog = crypto.Keccak256Hash([]byte("ClaimEvent(uint32,uint32,address,address,uint256)"))
It's the expected behaviour?
There was a problem hiding this comment.
Good catch !!
I'm thinking that is worth it to check directly the nullififer (via de isClaimed) SC call rather than ClaimEvent
4936ffa to
63a43be
Compare
|
- Add overflow checks for big.Int to uint32/uint64 conversions (safeUint32, safeUint8) - Add max metadata size validation (1MB) in decodeBridgeEvent to prevent DoS - Cap batch size to RPCBatchSize in fetchTotalSupplies - Return error from parseBlockNumber on invalid input instead of silent zero - Extract globalIndex magic numbers to named constants - Add progress logging to Step D - Document native token handling in step_c indexByAddress - Fix all golangci-lint issues (errcheck, gci, gosec, lll, mnd, prealloc, unparam) Made-with: Cursor
- Scan L2 bridge for ClaimEvent logs so Step E correctly identifies already-claimed deposits instead of treating all as unclaimed (joanestebanr) - Fail on trace/scan errors instead of warn+continue: traceTransactions, fetchL1BridgeEvents now propagate errors (partial scans are unsafe) - Fix encodeBalanceOf: use zero-padding (LeftPadBytes) instead of space-padding (%064s) which produced invalid hex calldata - Use strconv.ParseUint instead of fmt.Sscanf to reject trailing non-numeric input like "123abc" - Set MaxIdleConnsPerHost=100 instead of 0 (0 defaults to 2 in net/http) - Preserve OriginNetwork/OriginTokenAddress from LBT for native token entries (supports chains with custom gas tokens) - Add decodeClaimEvent tests Made-with: Cursor
- Extract magic number 32 to named constant abiWordSize in step_b.go - Pre-allocate claims slice in fetchClaimEventsInRange Made-with: Cursor
- Replace ClaimEvent log scanning with isClaimed(depositCount, 0) eth_call on L2 bridge contract (authoritative claimed bitmap) - Extract helper functions across all steps to bring every function under diffguard thresholds (complexity ≤ 10, size ≤ 50 lines) Made-with: Cursor
Add exit_certificate binary to the Makefile build-tools target so it builds alongside the other tools. Add maskRPCURL helper that strips the path from RPC URLs before logging, preventing API key exposure in error messages. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ndling and logging - Add L2StartBlock option to config so block scanning starts from a configurable block instead of always from block 0 - Add label parameter to concurrentBatchRPC to identify each call site in progress logs (e.g. "L2 RPC/blockHeaders", "L2 RPC/balanceOf") - Improve batchRPC: log individual RPC-level errors via log.Warn and return the first error instead of silently dropping failed responses; add response-count validation - Add detailed app.Description to the CLI listing all pipeline steps (0, A, B, C, D, E) and how to run individual steps - Add .PHONY declarations for build-tools targets in Makefile Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ac0936d to
2122813
Compare
Revert the step A split (A1 + A2) back to a single RunStepA to avoid OOM caused by json.MarshalIndent serialising all tx hashes while they were still live in memory. Add a new SIGN step that signs the exit certificate with a local keystore and exposes signerKeyPath / signerKeyPassword in config and as CLI flags. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add ContinueOnTraceError config option so Step A skips transactions whose debug_traceTransaction call fails instead of aborting. Failed tx hashes are collected in StepAResult.FailedTraces and always saved to step-a-failed-traces.json alongside the other Step A outputs. Also fix README and cmd usage strings to reflect the reverted A1/A2 split and document the new sign step and signer config fields. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the second eth_getBlockByNumber round-trip (with full tx objects). The headers-only call (false) already returns transaction hashes in the transactions array, so extractTxHashes and parseTxHashesFromResults are no longer needed. Update README to reflect the simplified two-phase flow. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Step G: computes NewLocalExitRoot by replaying all bridge exits against an Anvil shadow-fork of the L2 chain. Uses debug_traceCall to discover LBT storage slots and hardhat_setStorageAt to unlock them before each bridgeAsset replay; falls back to EmptyLER when there are no bridge exits. Step H: fetches PreviousLocalExitRoot from the agglayer via interop_getNetworkInfo (requires agglayerRpcUrl in options). Step I: assembles the final certificate by applying NewLocalExitRoot (from G) and PreviousLocalExitRoot (from H) into exit-certificate-final.json. Step SUBMIT: sends the signed certificate to the agglayer over gRPC (requires agglayerGrpcUrl in options). Not part of the default pipeline; run with --step submit. Guards against submission when a pending certificate already exists for the network. The default pipeline is now 0 → A → B → C → D → E → F → G → H → I → SIGN. Config gains agglayerRpcUrl and agglayerGrpcUrl options. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ript Replace signerKeyPath/signerKeyPassword in Config with signertypes.SignerConfig, matching the same type aggsender uses for AggsenderPrivateKey. The JSON config now uses a flat signerConfig object (Method, Path, Password at top level). Update configuration_based_on_kurtosis.sh to download the sequencer keystore from the aggkit-sequencer-keystore artifact and extract the password from config.toml, producing a fully configured signerConfig block. Also add agglayerRpcUrl and agglayerGrpcUrl (needed for steps H and SUBMIT). Remove obsolete --signer-key-path/--signer-key-password CLI flags. Align README.md and CLAUDE.md with the current pipeline (steps H, I, SUBMIT, corrected step order, updated config fields and output file names). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion When a chain uses a custom gas token (non-zero origin address, e.g. a token from network 0), resolveTokenAddresses was falling through to the wrapped-token lookup path and failing because getTokenWrappedAddress returns zero — the token is native, not a wrapped ERC-20. Fix: fetch gasTokenInfo from the real L2 RPC in RunStepG (same pattern as step 0) and pass it to resolveTokenAddresses. Bridge exits whose TokenInfo matches the gas token are now treated as isNative=true, so bridgeAsset is called with token=address(0) as the contract expects. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>



🔄 Changes Summary
exit-certificateCLI tool undertools/exit_certificate/that generates exit certificates for aggchain migration. It scans L2 state from genesis to a target block, discovers all addresses with value (ETH + wrapped tokens), computes smart-contract-locked balances, detects unclaimed L1→L2 bridge deposits, and produces a standard agglayerCertificatewithBridgeExitentries that bridge all value back to L1.debug_traceTransaction, EOA balance scanning, SC-locked value computation, certificate building, and unclaimed bridge detection.--stepflag. Intermediate results are persisted to an output directory, enabling step-by-step execution and resumability.📋 Config Updates
tools/exit_certificate/parameters.json(standalone JSON, not part of aggkit's main config). Example:{ "l2RpcUrl": "https://your-l2-rpc.example.com", "l1RpcUrl": "https://your-l1-rpc.example.com", "l2BridgeAddress": "0x2a3DD3EB832aF982ec71669E178424b10Dca2EDe", "l1BridgeAddress": "0x2a3DD3EB832aF982ec71669E178424b10Dca2EDe", "l2NetworkId": 1, "targetBlock": "latest", "exitAddress": "0x0000000000000000000000000000000000000001", "destinationNetwork": 0, "options": { "blockRange": 10000, "concurrencyLimit": 200, "rpcBatchSize": 200, "rpcDelayMs": 10, "outputDir": "./output", "l1StartBlock": 0 } }Close #1584