feat: autoclaim L1 -> L2#1644
Conversation
# Conflicts: # multidownloader/evm_multidownloader_reorg.go # multidownloader/evm_multidownloader_reorg_test.go
…tic logs - config/config_test.go: move testify/urfave imports to end of non-stdlib group to satisfy gci two-group ordering (fixes CI lint failure) - config/default.go: set AutoClaim.L1ToL2Watchdog Enabled = true to match the assertion in TestLoadDefaultConfig (fixes unit test failure) - autoclaim/proof/preparer.go: add debug logs in selectL1InfoTreeIndex to surface whether the proof is blocked by l1InfoTree lag or gerSyncer lag - autoclaim/claimer/claimer.go: log info when proof not ready so E2E logs clearly show the pending state on each poll cycle Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rackedBlocks wipe reorgDetector.Start calls loadTrackedHeaders which replaces the in-memory trackedBlocks map entirely from DB. If l2gersync.New (which calls Subscribe internally) ran first, the subscription entry it added would be wiped by the subsequent loadTrackedHeaders call, causing every AddBlockToTrack to return "subscriber not subscribed" forever. The per-claimer l2gersync was stuck in a retry loop at the first GER injection block (block 24), never processing the GER that covered the bridge deposit, so GetFirstGERAfterL1InfoTreeIndex always returned ErrNotFound and the claim proof was never prepared. Fix: call reorgDetector.Start synchronously before l2gersync.New so that loadTrackedHeaders completes before Subscribe adds the entry. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The 3 SQL injection hotspots in autoclaim/storage/storage.go were marked SAFE on SonarCloud (all use parameterized ? placeholders, not user input). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add tests across 11 files covering uncovered nil-arg error paths, option closures, getter/accessor methods, disabled-state branches, and constructor validation to push SonarCloud new_coverage above the 80% quality gate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…overage Adds 7 targeted tests across sender and api packages to cover previously unreachable lines: default time.Now closure bodies, IsClaimed error propagation, pollResult Result() failure path, zero WaitPeriod fallback to s.pollPeriod, zero-MaxRetries target fallback to request.MaxRetries, and claimTxHash with nil Tx entry. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Claude finished @joanestebanr's task in 2m 17s —— View job Code Review Complete ✓I've completed a comprehensive review of PR #1644: feat: autoclaim L1 -> L2 🚀 What's NewThis PR adds an opt-in 🐛 Bug Fixes
📋 Config UpdatesAdds a new optional [AutoClaim]
Enabled = false
StoragePath = "{{PathRWData}}/autoclaim.sqlite"
Claimers = []
[AutoClaim.API]
Enabled = false
Host = "0.0.0.0"
Port = 5579
[AutoClaim.L1ToL2Watchdog]
Enabled = true
StartBlock = 0
PollInterval = "3s"
RetryAfterErrorPeriod = "1s"
MaxRetryAttemptsAfterError = -1
EtrogL1UpgradeBlock = 0Each claimer requires its own
|
| Enabled = true | ||
| StoragePath = "/var/lib/aggkit/autoclaim.sqlite" | ||
|
|
||
| [AutoClaim.API] |
There was a problem hiding this comment.
"Perhaps it would make more sense to use the same REST endpoint (host + port) as Bridge, allowing both services to coexist while sharing the same host and port.
Take a look to config:
[REST]
Host = "0.0.0.0"
Port = 5577
ReadTimeout = "5m"
WriteTimeout = "5m"
MaxRequestsPerIPAndSecond = 10
|
|
||
| // NewBridgeExitFromSyncWithEtrog converts a bridge sync record using Etrog-upgrade awareness. | ||
| func NewBridgeExitFromSyncWithEtrog(bridge bridgesync.Bridge, etrogL1UpgradeBlock uint64) BridgeExit { | ||
| preEtrog := isPreEtrogBridge(bridge, etrogL1UpgradeBlock) |
There was a problem hiding this comment.
This transformation is not done in bridgeservice? It sounds to me like this code already exists somewhere
|
|
||
| ```toml | ||
| [AutoClaim] | ||
| Enabled = true |
There was a problem hiding this comment.
Maybe it's redundant... if you don't want AutoClaim remove from component list... it would make more sense to call it DryRun or something like that, that run normally but skip the send claim tx
| NewTargetClaimReader func(common.Address, aggkittypes.BaseEthereumClienter) (autoclaimtypes.TargetClaimReader, error) | ||
| // NewGERSyncer builds the per-claimer destination-L2 GER syncer and returns a start function that | ||
| // runs its reorg detector and sync loop (the start function blocks and is meant to run in a goroutine). | ||
| NewGERSyncer func( |
There was a problem hiding this comment.
The new L2GerSyncer can't set individually BlockFinality or InitialBlockNum. They inherit the configuration from the instance in config file
joanestebanr
left a comment
There was a problem hiding this comment.
Another point is that the API for bridgeservice and for autoclaim is very similar. But there are a different that can be a bit misleading: the first page_number: bridgeservice use 1 and autoclaim use 0



🔄 Changes Summary
autoclaimAggkit component for L1 -> L2 bridge claims. It discovers eligible exits froml1bridgesync, persists request/cursor/attempt state, prepares claim proofs withl1infotreesyncplus a per-claimerl2gersync, submitsclaimAsset/claimMessagetransactions throughEthTxManager, and tracks requests through confirmation or failure.allow-all,api-approve,no-message, andbasic-filter./autoclaim/v1for request listing, inspection, manual approval, and manual rejection, with generated Swagger docs.autoclaimcomponent, config loading/validation/defaults, docs, mocks, and the OP e2e environment./l1-info-tree-index: if a verified batch LER is missing from the local L2 exit tree, the lookup now avoids mixing L2 bridge-sync block numbers with L1 verified-batch block numbers and falls back to a bounded linear scan.[AutoClaim]config section and remains disabled by default.autoclaimcomponent selector and optional/autoclaim/v1API. Existing bridge service API paths are unchanged.📋 Config Updates
autoclaimcomponent and setting[AutoClaim].Enabled = true.[[AutoClaim.Claimers]]entry configures one EVM destination network with its own RPC URL, bridge address, policy, gas settings, retry settings, andEthTxManagerstorage/signing config.AutoClaim.StoragePath; each claimer also has an independentEthTxManager.StoragePathand isolated per-claimerl2gersync/ L2 reorg detector databases under the Auto Claim storage directory.✅ Testing
autoclaim/api,claimer,claimtx,config,policy,proof,runtime,sender,simulator,storage,types, andwatchdog.TestAutoClaimL1ToL2AllowAll,TestAutoClaimL1ToL2APIApprove, andTestAutoClaimL1ToL2BasicFilter.lint,test-unit, Go e2e, Docker image build, CodeQL, SonarCloud, and the configured multi/single-chain e2e workflows passing.Run govulncheckis currently failing.🐞 Issues
🔗 Related PRs
📝 Notes
origin_network:destination_network:deposit_count;origin_networkis the bridged token's origin network, not necessarily the chain where the bridge was initiated.l2gersync, which supports both legacy and sovereign L2 GER manager behavior.api-approvepolicy requires the Auto Claim API so operators can approve or reject requests inmanual-approval-requiredstatus.TestRemoveGER_NoProblematicClaims,TestRemoveGER_CategoryA,TestRemoveGER_CategoryB1,TestRemoveGER_CategoryB2, andTestGenerateInvalidGER. This is being worked on feat: migrate e2e tests from bats to go #1643GetFirstVerifiedBatchesAfterBlockwithGetLastRoot().BlockNum, which is an L2 bridge-sync block number even though the L1 info tree query expects an L1 block number. The new path only falls back ondb.ErrNotFound, keeps the original L1 verified-batch bounds, and skips missing LERs with a linear scan when binary search cannot safely continue.