From 28bf6a5cf89c42bead4ca61b2438a16e8956c303 Mon Sep 17 00:00:00 2001 From: Arnau Bennassar Date: Fri, 14 Aug 2026 10:47:04 +0000 Subject: [PATCH 1/4] feat(e2e): add anvil-2chains env, generalize L2B/log-service/chain-id assumptions Implements T3's design (aggkit-env-design.md): a new test/e2e/envs/anvil-2chains/ env built from the kurtosis-cdk anvil devnet snapshot (commit fc160450, K8's GHCR-published digest-pinned images for anvil-001/l2-anvil-001/l2-anvil-002/ agglayer; aggkit-001/aggkit-002/aggkit-proxy-001 on aggkit:local). Two anvil L2 sovereign chains settle PessimisticProof certificates (TriggerCertMode=ASAP, explicit in both configs) against one anvil L1, each aggkit running aggsender+aggoracle+bridge+autoclaim, fronted by a shared aggkit-proxy. Config tree copied from the actual published-run images' baked config (not a separate local re-run capture, whose block numbers didn't match the digest- pinned anvil chain state and caused "no contract code at given address" against the RollupManager at a stale genesis block), then renamed per extract-state.sh's documented kurtosis->aggkit path mapping. agglayer carries a real TCP-connect healthcheck against its gRPC port (K5c's fix); aggkit-00X/aggkit-proxy-001 gate on service_healthy against it and carry no healthcheck of their own (distroless image, confirmed no shell). This avoids the aggsender claim-syncer deadlock a `service_started` dependency reproducibly hits. Four Go edits generalize hardcoded op-pp-2chains assumptions so this new env (and any future multi-chain env) works without a new env-name branch: - loader.go: add EnvAnvil2Chains; load L2B based on summary.json key presence instead of env name; add Env.ComposeServices(ctx) (docker compose config --services) for log collection. - checks.go: extend the L2A chain-ID check's env-name condition (kept name-keyed on purpose -- it exists to catch a stale/wrong summary.json). - testmain_test.go: dumpContainerLogs now iterates ComposeServices(ctx) instead of a hardcoded, already under-covering service list. Also fixes a real race in bridge_utils.go's BridgeL1ToL2: this env runs AutoClaim's L1ToL2BridgeDetector on the same network TestMain's post-test bridge check manually claims into, so the manual ClaimAsset call can lose the race and revert with AlreadyClaimed. Check IsClaimed first (same check autoclaim_test.go already uses) and treat an already-claimed deposit as success instead of a failure. anvil-2chains's aggkit-001 also sets user: "${UID}:${GID}" (matching op-pp's own /tmp-bind-mount precedent) so files written to the host-mounted aggkit-001-data dir stay host-owned and removable by the next run. Verified: AGGKIT_E2E_ENV=anvil-2chains make test-e2e TEST_RUN='TestZZZNoSuchTest' passes green from a clean docker state (43s wall clock), including both post-test L1->L2 and L2->L1 bridge flows; op-pp and op-pp-2chains pass unchanged with the same pattern (61.6s, 138.7s respectively); make lint and gofmt are clean. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015Q5xUjjWCiNQdm7cmeWYrs --- test/e2e/bridge_utils.go | 53 +- test/e2e/envs/anvil-2chains/README.md | 129 +++ .../config/001/aggkit-config.toml | 751 ++++++++++++++++++ .../config/001/aggoracle.keystore | 20 + .../config/001/sequencer.keystore | 20 + .../config/001/sovereignadmin.keystore | 20 + .../config/002/aggkit-config.toml | 751 ++++++++++++++++++ .../config/002/aggoracle.keystore | 20 + .../config/002/sequencer.keystore | 20 + .../config/002/sovereignadmin.keystore | 20 + .../config/aggkit-proxy/aggkit-proxy.toml | 105 +++ .../config/agglayer/aggregator.keystore | 20 + .../anvil-2chains/config/agglayer/config.toml | 130 +++ .../e2e/envs/anvil-2chains/docker-compose.yml | 199 +++++ test/e2e/envs/anvil-2chains/summary.json | 269 +++++++ test/e2e/envs/checks.go | 6 +- test/e2e/envs/loader.go | 31 +- test/e2e/testmain_test.go | 28 +- 18 files changed, 2559 insertions(+), 33 deletions(-) create mode 100644 test/e2e/envs/anvil-2chains/README.md create mode 100644 test/e2e/envs/anvil-2chains/config/001/aggkit-config.toml create mode 100644 test/e2e/envs/anvil-2chains/config/001/aggoracle.keystore create mode 100644 test/e2e/envs/anvil-2chains/config/001/sequencer.keystore create mode 100644 test/e2e/envs/anvil-2chains/config/001/sovereignadmin.keystore create mode 100644 test/e2e/envs/anvil-2chains/config/002/aggkit-config.toml create mode 100644 test/e2e/envs/anvil-2chains/config/002/aggoracle.keystore create mode 100644 test/e2e/envs/anvil-2chains/config/002/sequencer.keystore create mode 100644 test/e2e/envs/anvil-2chains/config/002/sovereignadmin.keystore create mode 100644 test/e2e/envs/anvil-2chains/config/aggkit-proxy/aggkit-proxy.toml create mode 100644 test/e2e/envs/anvil-2chains/config/agglayer/aggregator.keystore create mode 100644 test/e2e/envs/anvil-2chains/config/agglayer/config.toml create mode 100644 test/e2e/envs/anvil-2chains/docker-compose.yml create mode 100644 test/e2e/envs/anvil-2chains/summary.json diff --git a/test/e2e/bridge_utils.go b/test/e2e/bridge_utils.go index 845168fef..abc5f3b5d 100644 --- a/test/e2e/bridge_utils.go +++ b/test/e2e/bridge_utils.go @@ -257,24 +257,45 @@ func BridgeL1ToL2(ctx context.Context, env *envs.Env, l1Opts, l2Opts *bind.Trans } time.Sleep(time.Second) } - log.Debugf("sending claim transaction on L2") - claimTx, err := env.L2.Contracts.L2Bridge.ClaimAsset( - l2Opts, smtProofLocalExitRoot, smtProofRollupExitRoot, - bridge.GlobalIndex, mainnetExitRoot, rollupExitRoot, - bridge.OriginNetwork, originTokenAddress, bridge.DestinationNetwork, - destinationAddress, bridgeAmount, metadata, - ) - if err != nil { - return fmt.Errorf("failed to send claim transaction: %w", err) - } - log.Debugf("L2 claim tx submitted, waiting for mining: tx=%s", claimTx.Hash().Hex()) - claimReceipt, err := bind.WaitMined(ctx, env.Clients.L2, claimTx) + // Some envs (e.g. anvil-2chains) run AutoClaim's L1ToL2BridgeDetector on this destination + // network, which may claim the deposit before this helper gets to it -- a real race, not a + // bug, since AutoClaim polls independently of this test. Check IsClaimed first (same check + // autoclaim_test.go already uses) so a legitimately-already-claimed deposit is treated as + // success instead of failing on the bridge contract's AlreadyClaimed revert. + alreadyClaimed, err := env.L2.Contracts.L2Bridge.IsClaimed(callOpts, depositCount, bridge.OriginNetwork) if err != nil { - return fmt.Errorf("failed to wait for claim tx: %w", err) + return fmt.Errorf("failed to check IsClaimed: %w", err) } - log.Debugf("L2 claim tx mined: tx=%s block=%d", claimTx.Hash().Hex(), claimReceipt.BlockNumber.Uint64()) - if claimReceipt.Status != ethtypes.ReceiptStatusSuccessful { - return errors.New("claim transaction failed") + if alreadyClaimed { + log.Debugf("deposit already claimed (likely by AutoClaim): deposit_count=%d", depositCount) + } else { + log.Debugf("sending claim transaction on L2") + claimTx, err := env.L2.Contracts.L2Bridge.ClaimAsset( + l2Opts, smtProofLocalExitRoot, smtProofRollupExitRoot, + bridge.GlobalIndex, mainnetExitRoot, rollupExitRoot, + bridge.OriginNetwork, originTokenAddress, bridge.DestinationNetwork, + destinationAddress, bridgeAmount, metadata, + ) + if err != nil { + // AutoClaim may have won the race between our IsClaimed check above and this send + // (its own poll loop runs concurrently and independently). Re-check before failing. + if reClaimed, reErr := env.L2.Contracts.L2Bridge.IsClaimed( + callOpts, depositCount, bridge.OriginNetwork); reErr == nil && reClaimed { + log.Debugf("claim transaction lost the race to AutoClaim: deposit_count=%d", depositCount) + } else { + return fmt.Errorf("failed to send claim transaction: %w", err) + } + } else { + log.Debugf("L2 claim tx submitted, waiting for mining: tx=%s", claimTx.Hash().Hex()) + claimReceipt, err := bind.WaitMined(ctx, env.Clients.L2, claimTx) + if err != nil { + return fmt.Errorf("failed to wait for claim tx: %w", err) + } + log.Debugf("L2 claim tx mined: tx=%s block=%d", claimTx.Hash().Hex(), claimReceipt.BlockNumber.Uint64()) + if claimReceipt.Status != ethtypes.ReceiptStatusSuccessful { + return errors.New("claim transaction failed") + } + } } finalL2Balance, err := env.Clients.L2.BalanceAt(ctx, destinationAddress, nil) if err != nil { diff --git a/test/e2e/envs/anvil-2chains/README.md b/test/e2e/envs/anvil-2chains/README.md new file mode 100644 index 000000000..f2d10b148 --- /dev/null +++ b/test/e2e/envs/anvil-2chains/README.md @@ -0,0 +1,129 @@ +# anvil-2chains + +`AGGKIT_E2E_ENV=anvil-2chains` + +Two independent anvil-backed L2 sovereign chains (L2-001 chain 20201, L2-002 chain 20202) +settling PessimisticProof certificates against a single anvil L1 (chain 271828) through one +agglayer, each with its own aggkit instance (merged aggsender + aggoracle + bridge + autoclaim +-- no separate `-bridge` sidecar), fronted by a shared aggkit-proxy. Sourced from a +kurtosis-cdk anvil devnet snapshot rather than a live `kurtosis run` per test invocation, +matching the `op-pp`/`op-pp-2chains` pattern in this directory. + +## Provenance + +- **kurtosis-cdk commit:** `fc160450b55e64332436f11c091c61130c64030f` + (`0xPolygon/kurtosis-cdk`, branch `feat/aggkit-bridge-ui-backend`, PR #929's head). +- **Params files** (two sequential `kurtosis run` invocations into the same enclave): + - [`params-aggkit-anvil-l2l2-run1.yml`](https://github.com/0xPolygon/kurtosis-cdk/blob/fc160450b55e64332436f11c091c61130c64030f/params-aggkit-anvil-l2l2-run1.yml) + -- deploys L1 (anvil-001) + agglayer + rollup 1 (network_id 1, `aggkit-001`). + - [`params-aggkit-anvil-l2l2-run2.yml`](https://github.com/0xPolygon/kurtosis-cdk/blob/fc160450b55e64332436f11c091c61130c64030f/params-aggkit-anvil-l2l2-run2.yml) + -- adds rollup 2 (network_id 2, `aggkit-002`) into the same enclave, plus the + `aggkit-proxy-001` / dev-ui stack that this e2e env doesn't use. +- **Snapshot build:** `snapshot/snapshot.sh --flavor anvil-aggkit --tag ` (see + `.github/workflows/snapshot-devui.yml`), which seeds fixtures, captures live state, builds + self-contained images, and emits `docker-compose.yml` / `docker-compose.mounts.yml` / + `summary.json` / `config/` under `snapshots/-/`. +- **Publish run:** [GitHub Actions run 31787941750](https://github.com/0xPolygon/kurtosis-cdk/actions/runs/31787941750) + (`workflow_dispatch`, `publish=true`, ref `feat/aggkit-bridge-ui-backend`, resolved base tag + `fc160450b55e`). Independent full `test` run at the same HEAD (all 18 jobs green, zero + skipped): [run 31787908220](https://github.com/0xPolygon/kurtosis-cdk/actions/runs/31787908220). +- **Config tree source:** this directory's `config/` was copied from the same commit's local + re-run of the snapshot pipeline (`snapshots/k8-20260814-095314/config/`, + `docker-compose.mounts.yml` variant -- bare upstream `agglayer`/`aggkit` images with + bind-mounted config, not the fully-baked default variant), then renamed per the mapping + documented in `snapshot/scripts/extract-state.sh` (kurtosis-cdk names each directory after + its own service, e.g. `config/aggkit-001/config.toml`; this env instead keys per-L2 + directories by bare network prefix and calls the aggkit config file + `aggkit-config.toml`, matching `op-pp-2chains`'s own layout): + + | kurtosis-cdk (emitted) | this env | + |---|---| + | `config/agglayer/config.toml` | `config/agglayer/config.toml` | + | `config/agglayer/aggregator.keystore` | `config/agglayer/aggregator.keystore` | + | `config/aggkit-001/config.toml` | `config/001/aggkit-config.toml` | + | `config/aggkit-001/{sequencer,aggoracle,sovereignadmin}.keystore` | `config/001/{sequencer,aggoracle,sovereignadmin}.keystore` | + | `config/aggkit-002/config.toml` | `config/002/aggkit-config.toml` | + | `config/aggkit-002/{sequencer,aggoracle,sovereignadmin}.keystore` | `config/002/{sequencer,aggoracle,sovereignadmin}.keystore` | + | `config/aggkit-proxy-001/config.toml` | `config/aggkit-proxy/aggkit-proxy.toml` | + + Only hostnames/internal service names needed to line up (they already did -- this env keeps + the same compose service names the bundle uses: `anvil-001`, `l2-anvil-001`, `l2-anvil-002`, + `agglayer`, `aggkit-001`, `aggkit-002`, `aggkit-proxy-001`) and only host ports changed (see + below); no contract addresses or private keys needed to change, because the bundle's L1/L2 + mnemonics are byte-identical to `op-pp-2chains`'s own (`giant issue aisle ... athlete` / `test + test ... junk`), so every deterministically-derived contract address matches address-for-address. + +## Images (`name@digest`) + +Independently re-verified as anonymously pullable from `ghcr.io/0xpolygon` (see the plan's +`K8-evidence/HANDOFF.md` and `06-anonymous-pull-proof.log` / `08-registry-digest-comparison.txt` +for the verification detail): + +| Service | Image | Tag | +|---|---|---| +| `anvil-001` | `ghcr.io/0xpolygon/kurtosis-cdk-snapshot-anvil-001@sha256:006932fc49ce501c8d6f8c3f4ac3b5873ec14b59b101b4f4e9db02b169e6c0c9` | `v1.5.1-1786700247` | +| `l2-anvil-001` | `ghcr.io/0xpolygon/kurtosis-cdk-snapshot-l2-anvil-001@sha256:e9bbeb7f9a76a4ea725f194059c6b23d49c65e89a8a00241d6df3b687c8ccbb8` | `v1.5.1-1786700247` | +| `l2-anvil-002` | `ghcr.io/0xpolygon/kurtosis-cdk-snapshot-l2-anvil-002@sha256:3555d50518f6f72d5811edd759293ba205ac192c04192695afc046c2cb595ef0` | `v1.5.1-1786700247` | +| `agglayer` | `ghcr.io/0xpolygon/kurtosis-cdk-snapshot-agglayer@sha256:5a47d3778657ba618ff7dfc99dfd55a3863097d5fff4f960a0740d4d0ae80073` | `0.6.0-rc.8-1786700247` | +| `aggkit-001` / `aggkit-002` / `aggkit-proxy-001` | **not** the snapshot's baked aggkit image (`kurtosis-cdk-snapshot-aggkit-*`) -- this env runs `aggkit:local`, this repo's own build, so the binary under test is always the checkout in this worktree, not a pinned upstream aggkit release. | + +`aggkit:local` is built by `make build-docker` (`docker build -t aggkit:local ... -f ./Dockerfile +.`, see the top-level Makefile) and is expected to already exist before running this env's +tests, exactly like `op-pp`/`op-pp-2chains`. + +## Chain / network IDs + +| | chain_id | network_id | +|---|---|---| +| L1 (`anvil-001`) | 271828 | 0 | +| L2-001 (`l2-anvil-001`) | 20201 | 1 | +| L2-002 (`l2-anvil-002`) | 20202 | 2 | + +## Ports (host, collision-checked against `op-pp` and `op-pp-2chains`) + +| Service | Container port(s) | Host port(s) | +|---|---|---| +| `anvil-001` | 8545 | 13545 | +| `agglayer` | 4443/4444/4446/9092 | 13443/13444/13446/13092 | +| `l2-anvil-001` | 8545 | 14545 | +| `aggkit-001` | 5576/5577 | 14576/14577 | +| `l2-anvil-002` | 8545 | 15545 | +| `aggkit-002` | 5576/5577 | 15576/15577 | +| `aggkit-proxy-001` | 8080 | 15601 | + +## Known deviations from the design note + +`config/agglayer/config.toml`'s `[full-node-rpcs]` / `[proof-signers]` here has entries for +**both** network 1 and network 2 (unlike `op-pp-2chains`'s own config, which only has a +network-1 entry). This is intentionally carried forward as emitted by the kurtosis-cdk bundle +rather than trimmed to match `op-pp-2chains`'s pattern: the bundle's own two-network config is +the one that was actually exercised end-to-end by kurtosis-cdk's test run (18 jobs green, +including PessimisticProof settlement on both rollups), so it is a stronger working precedent +here than `op-pp-2chains`'s incomplete (network-1-only) config for a topology that doesn't +happen to need network 2's entry for its own tests to pass. + +## Regenerating this env + +1. Check out kurtosis-cdk at `fc160450b55e64332436f11c091c61130c64030f` (or a descendant that + hasn't changed the anvil-aggkit flavor's shape). +2. `kurtosis run --enclave=cdk --args-file=params-aggkit-anvil-l2l2-run1.yml .` +3. `kurtosis run --enclave=cdk --args-file=params-aggkit-anvil-l2l2-run2.yml .` +4. `snapshot/snapshot.sh cdk --flavor anvil-aggkit --tag ` -- produces + `snapshots/cdk-/{docker-compose.yml,docker-compose.mounts.yml,summary.json,config/}`. +5. Copy `docker-compose.mounts.yml` as the starting shape for this directory's + `docker-compose.yml` (swap in digest-pinned image refs for `anvil-001`/`l2-anvil-001`/ + `l2-anvil-002`/`agglayer`, and `aggkit:local` for `aggkit-001`/`aggkit-002`/ + `aggkit-proxy-001`; keep the TCP-connect healthcheck on `agglayer` -- see the file header + comment in `docker-compose.yml` for why). +6. Copy `config/` into this directory's `config/`, renaming per the mapping table above. +7. Rewrite `summary.json` into aggkit's own schema (see `test/e2e/envs/loader.go`'s + `summaryJSON`/`summaryL2Network` structs for the exhaustive list of keys actually read) using + this bundle's own `summary.json` (`chain_ids`, `network_ids`, `accounts.funded` filtered by + `funded_on`, `networks.l1.contracts`, `networks.l2.*.contracts`) plus the host ports chosen + in step 5. +8. Confirm `TriggerCertMode = "ASAP"` is still explicit in both `config/001/aggkit-config.toml` + and `config/002/aggkit-config.toml` (`Auto` silently resolves to `EpochBased` for a + PessimisticProof aggsender). +9. To publish new digests for step 5's image refs, re-run + `.github/workflows/snapshot-devui.yml` with `publish=true` and copy the resulting + `name@digest` triples from the run's summary/logs. diff --git a/test/e2e/envs/anvil-2chains/config/001/aggkit-config.toml b/test/e2e/envs/anvil-2chains/config/001/aggkit-config.toml new file mode 100644 index 000000000..d276e4ece --- /dev/null +++ b/test/e2e/envs/anvil-2chains/config/001/aggkit-config.toml @@ -0,0 +1,751 @@ +# ============================================================================== +# _ ____ ____ _ _____ _____ +# / \ / ___|/ ___| |/ /_ _|_ _| +# / _ \| | _| | _| ' / | | | | +# / ___ \ |_| | |_| | . \ | | | | +# /_/ \_\____|\____|_|\_\___| |_| +# +# This is a reference config file used by the Kurtosis CDK Testing +# setup. The values here should work, but are necessarily meant for +# production environments. DYOR +# The below configs are the default mandatory parameters to be used. +# ============================================================================== + +PathRWData = "/tmp" +L1URL = "http://anvil-001:8545" +L2URL = "http://l2-anvil-001:8545" +OpNodeURL = "http://l2-anvil-001:8545" + +# Check if agglayer grpc or readrpc should be used for AggLayerURL +AggLayerURL = "http://agglayer:4443" + +AggchainProofURL= "aggkit-prover-001:4446" +SequencerPrivateKeyPath = "/etc/aggkit/sequencer.keystore" +SequencerPrivateKeyPassword = "pSnv6Dh5s9ahuzGzH9RoCDrKAMddaX3m" +RPCURL = "http://l2-anvil-001:8545" + +# These values can be overridden directly from genesis.json +rollupCreationBlockNumber = "60" +rollupManagerCreationBlockNumber = "60" +genesisBlockNumber = "60" +# ------------------------------------------------------------------------------ + +# ============================================================================== +# _ _ ____ ___ _ _ _____ ___ ____ +# | | / |/ ___/ _ \| \ | | ___|_ _/ ___| +# | | | | | | | | | \| | |_ | | | _ +# | |___| | |__| |_| | |\ | _| | | |_| | +# |_____|_|\____\___/|_| \_|_| |___\____| +# +# ------------------------------------------------------------------------------ +[L1Config] +# ------------------------------------------------------------------------------ +# URL is the L1 network url +# ------------------------------------------------------------------------------ +URL = "http://anvil-001:8545" + +# ------------------------------------------------------------------------------ +# L1 chain id +# ------------------------------------------------------------------------------ +chainId = "271828" + +# ------------------------------------------------------------------------------ +# Address of the zkevm global exit root contract on L1 +# ------------------------------------------------------------------------------ +polygonZkEVMGlobalExitRootAddress = "0x1f7ad7caA53e35b4f0D138dC5CBF91aC108a2674" + +# ------------------------------------------------------------------------------ +# Address of the rollup manager contract on L1 +# ------------------------------------------------------------------------------ +polygonRollupManagerAddress = "0x6c6c009cC348976dB4A908c92B24433d4F6edA43" + +# ------------------------------------------------------------------------------ +# Address of the pol token address on L1 +# ------------------------------------------------------------------------------ +polTokenAddress = "0xEdE9cf798E0fE25D35469493f43E88FeA4a5da0E" + +# ------------------------------------------------------------------------------ +# Address of the sovereign rollup contract on L2 +# ------------------------------------------------------------------------------ +polygonZkEVMAddress = "0x414e9E227e4b589aF92200508aF5399576530E4e" + +BridgeAddr = "0xC8cbEBf950B9Df44d987c8619f092beA980fF038" + +# ============================================================================== +# _ ____ ____ ___ _ _ _____ ___ ____ +# | | |___ \ / ___/ _ \| \ | | ___|_ _/ ___| +# | | __) | | | | | | \| | |_ | | | _ +# | |___ / __/| |__| |_| | |\ | _| | | |_| | +# |_____|_____|\____\___/|_| \_|_| |___\____| +# +# ------------------------------------------------------------------------------ +[L2Config] +# ------------------------------------------------------------------------------ +# Address of the sovereign global exit root proxy contract on L2 +# ------------------------------------------------------------------------------ +GlobalExitRootAddr = "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa" +BridgeAddr = "0xC8cbEBf950B9Df44d987c8619f092beA980fF038" + +# ============================================================================== +# _ ___ ____ +# | | / _ \ / ___| +# | | | | | | | _ +# | |__| |_| | |_| | +# |_____\___/ \____| +# +# ------------------------------------------------------------------------------ +[Log] +# ------------------------------------------------------------------------------ +# Environment generally dictates the format of the logs and the +# sampling rate. We often default to production even for development +# because of the JSON encoding. +# +# https://github.com/uber-go/zap/blob/a55bdc32f526699c3b4cc51a2cc97e944d02fbbf/config.go#L120 +# https://github.com/uber-go/zap/blob/a55bdc32f526699c3b4cc51a2cc97e944d02fbbf/config.go#L161 +# ------------------------------------------------------------------------------ +Environment = "development" + +# ------------------------------------------------------------------------------ +# Level determines the log level that will be written to the +# log. Generally we'll switch to debug if we want to troubleshoot +# something specifically otherwise we leave it at info +# ------------------------------------------------------------------------------ +Level = "info" + +# ------------------------------------------------------------------------------ +# Outputs define the output paths for writing logs. The default is to +# write to stderr, but other output paths should be supported +# +# https://github.com/uber-go/zap/blob/a55bdc32f526699c3b4cc51a2cc97e944d02fbbf/writer.go#L32-L50 +# ------------------------------------------------------------------------------ +Outputs = ["stderr"] + +# ============================================================================== +# ____ ____ ____ +# | _ \| _ \ / ___| +# | |_) | |_) | | +# | _ <| __/| |___ +# |_| \_\_| \____| +# +# ------------------------------------------------------------------------------ +[RPC] +# ------------------------------------------------------------------------------ +# Port will configure the port that the JSON RPC server will listen on +# ------------------------------------------------------------------------------ +Port = "5576" + +# ============================================================================== +# ____ _____ ____ _____ +# | _ \| ____/ ___|_ _| +# | |_) | _| \___ \ | | +# | _ <| |___ ___) || | +# |_| \_\_____|____/ |_| +# +# ------------------------------------------------------------------------------ +[PublicREST] +# ------------------------------------------------------------------------------ +# Port will configure the port that the REST API HTTP server will +# listen on +# ------------------------------------------------------------------------------ +Port = "5577" + +# ============================================================================== +# _ ____ ____ ____ _____ _ _ ____ _____ ____ +# / \ / ___|/ ___/ ___|| ____| \ | | _ \| ____| _ \ +# / _ \| | _| | _\___ \| _| | \| | | | | _| | |_) | +# / ___ \ |_| | |_| |___) | |___| |\ | |_| | |___| _ < +# /_/ \_\____|\____|____/|_____|_| \_|____/|_____|_| \_\ +# +# ------------------------------------------------------------------------------ +[AggSender] +# ------------------------------------------------------------------------------ +# StoragePath is the path of the sqlite db for the AggSender to store the data +# ------------------------------------------------------------------------------ +# StoragePath = "/tmp" + +# ------------------------------------------------------------------------------ +# AggsenderPrivateKey is the private key which is used to sign certificates +# ------------------------------------------------------------------------------ +AggSenderPrivateKey = {Path = "/etc/aggkit/sequencer.keystore", Password = "pSnv6Dh5s9ahuzGzH9RoCDrKAMddaX3m"} + +# ------------------------------------------------------------------------------ +# Defines if a check on aggsender proposer startup will be performed +# to see if the proposer is in the multisig committee +# ------------------------------------------------------------------------------ +# RequireCommitteeMembershipCheck = false +Mode = "PessimisticProof" +CheckStatusCertificateInterval = "1s" + +# ------------------------------------------------------------------------------ +# TriggerCertMode is the mode used to trigger certificate sending +# Valid values are: "EpochBased", "NewBridge", "ASAP", "Auto" +# EpochBased: this is the legacy mode that waits until reach a percentage of a epoch (you can configure here: AggSender.TriggerEpochBased) +# ASAP: this mode try to generate a new certificate after a successful settled certificate +# NewBridge: Each time that a new bridge is done in L2 it generate a certificate (if it's possible) (experimental) +# ------------------------------------------------------------------------------ +TriggerCertMode = "ASAP" + +# # Encouraged to use the default values - the parameters are left commented out for reference +# +# [AggSender.TriggerASAP] +# # Delay between the moment the aggsender becomes idle and when it sends a new certificate trigger +# DelayBetweenCertificates = "1s" +# # Minimum time that must elapse between certificate generation triggers, regardless of the trigger source +# MinimumNewCertificateInterval = "5s" +# # When enabled, the ASAP trigger will automatically generate certificate triggers when new bridge events are detected on L2 +# OnNewL2Bridge = false +# + +# ------------------------------------------------------------------------------ +# MaxCertSize is the maximum size of the certificate +# i.e (the emitted certificate cannot be bigger that this size) +# 0 is infinite +# ------------------------------------------------------------------------------ +# MaxCertSize = 0 +[AggSender.ValidatorClient] +URL = "aggkit-validator-001:5578" + +[AggSender.AggkitProverClient] +UseTLS = false + +# ------------------------------------------------------------------------------ +# URLRPCL2 is the URL of the L2 RPC node +# ------------------------------------------------------------------------------ +# URLRPCL2 = "http://l2-anvil-001:8545" + +# ------------------------------------------------------------------------------ +# EpochNotificationPercentage indicates the percentage of the epoch +# the AggSender should send the certificate +# 0 -> Begin +# 50 -> Middle +# ------------------------------------------------------------------------------ +# EpochNotificationPercentage = 50 + +# ------------------------------------------------------------------------------ +# MaxRetriesStoreCertificate is the maximum number of retries to store a certificate +# 0 is infinite +# ------------------------------------------------------------------------------ +# MaxRetriesStoreCertificate = 3 + +# ------------------------------------------------------------------------------ +# DelayBetweenRetries is the delay between retries +# Duration expressed in units: [ns, us, ms, s, m, h, d]" +# ------------------------------------------------------------------------------ +# DelayBetweenRetries = 5s + +# ------------------------------------------------------------------------------ +# BridgeMetadataAsHash is a flag to import the bridge metadata as hash +# ------------------------------------------------------------------------------ +# BridgeMetadataAsHash = false + +# ------------------------------------------------------------------------------ +# DryRun is a flag to enable the dry run mode +# in this mode the AggSender will not send the certificates to Agglayer +# ------------------------------------------------------------------------------ +# DryRun = false + +# ------------------------------------------------------------------------------ +# EnableRPC is a flag to enable the RPC for aggsender +# ------------------------------------------------------------------------------ +# EnableRPC = false + +[AggSender.AgglayerClient] + +[[AggSender.AgglayerClient.APIRateLimits]] +MethodName = "SendCertificate" + +[AggSender.AgglayerClient.APIRateLimits.RateLimit] +# Disable limit +NumRequests = 0 + +[AggSender.AgglayerClient.GRPC] +URL = "http://agglayer:4443" +MinConnectTimeout = "5s" +RequestTimeout = "300s" +UseTLS = false + +[AggSender.AgglayerClient.GRPC.Retry] +InitialBackoff = "1s" +MaxBackoff = "10s" +BackoffMultiplier = 2.0 +MaxAttempts = 20 + +[AggSender.StorageRetainCertificatesPolicy] +# ------------------------------------------------------------------------------ +# RetainCertificatesCount controls how many certificates to retain on storage. +# If set to zero, all certificates will be stored. +# ------------------------------------------------------------------------------ +# RetainCertificatesCount = 0 + +# ------------------------------------------------------------------------------ +# KeepCertificatesHistory is a flag to keep the certificates history on storage +# ------------------------------------------------------------------------------ +# KeepCertificatesHistory = true + +# ------------------------------------------------------------------------------ +# RetryCertAfterInError when a cert pass to 'InError'state +# the AggSender will try to resend it immediately +# ------------------------------------------------------------------------------ +# RetryCertAfterInError = false + +# ------------------------------------------------------------------------------ +# RequireNoFEPBlockGap is true if the AggSender should not accept a gap between +# lastBlock from lastCertificate and first block of FEP +# ------------------------------------------------------------------------------ +# RequireNoFEPBlockGap = false + +# ------------------------------------------------------------------------------ +# RequireOneBridgeInPPCertificate is a flag to force the AggSender to have at least one bridge exit +# for the Pessimistic Proof certificates +# ------------------------------------------------------------------------------ +# RequireOneBridgeInPPCertificate = false + +# ------------------------------------------------------------------------------ +# MaxL2BlockNumber is the last L2 block number that is going to be included in a certificate +# 0 means disabled +# ------------------------------------------------------------------------------ +# MaxL2BlockNumber = 0 + +# ------------------------------------------------------------------------------ +# StopOnFinishedSendingAllCertificates is a flag to stop the AggSender when it finishes sending all certificates +# up to MaxL2BlockNumber +# ------------------------------------------------------------------------------ +# StopOnFinishedSendingAllCertificates = false + +# ============================================================================== +# _ ____ ____ ___ ____ _ ____ _ _____ +# / \ / ___|/ ___|/ _ \| _ \ / \ / ___| | | ____| +# / _ \| | _| | _| | | | |_) | / _ \| | | | | _| +# / ___ \ |_| | |_| | |_| | _ < / ___ \ |___| |___| |___ +# /_/ \_\____|\____|\___/|_| \_\/_/ \_\____|_____|_____| +# +# ------------------------------------------------------------------------------ +[AggOracle] +# ------------------------------------------------------------------------------ +# TargetChainType currently only supports "EVM" +# ------------------------------------------------------------------------------ +# TargetChainType = "EVM" + +# ------------------------------------------------------------------------------ +# URLRPCL1 is the URL of the L1 RPC node +# ------------------------------------------------------------------------------ +# URLRPCL1 = "http://anvil-001:8545" + +# ------------------------------------------------------------------------------ +# Duration expressed in units: [ns, us, ms, s, m, h, d]" +# ------------------------------------------------------------------------------ +WaitPeriodNextGER = "10s" + +# ------------------------------------------------------------------------------ +# +# ------------------------------------------------------------------------------ +EnableAggOracleCommittee = false + +[AggOracle.EVMSender] +# ------------------------------------------------------------------------------ +# Address of the sovereign global exit root proxy contract on L2 +# ------------------------------------------------------------------------------ +GlobalExitRootL2 = "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa" + +# ------------------------------------------------------------------------------ +# URLRPCL2 is the URL of the L2 RPC node +# ------------------------------------------------------------------------------ +# URLRPCL2 = "http://l2-anvil-001:8545" + +# ------------------------------------------------------------------------------ +# GasOffset is the gas to add on the estimated gas when sending the claim txs +# ------------------------------------------------------------------------------ +# GasOffset = 0 + +# ------------------------------------------------------------------------------ +# Duration expressed in units: [ns, us, ms, s, m, h, d]" +# ------------------------------------------------------------------------------ +WaitPeriodMonitorTx = "10s" + +# ------------------------------------------------------------------------------ +# +# ------------------------------------------------------------------------------ + +[AggOracle.EVMSender.EthTxManager] +# ------------------------------------------------------------------------------ +# PrivateKeys defines all the key store files that are going +# to be read in order to provide the private keys to sign the L1 txs +# ------------------------------------------------------------------------------ +PrivateKeys = [{Path = "/etc/aggkit/aggoracle.keystore", Password = "pSnv6Dh5s9ahuzGzH9RoCDrKAMddaX3m"}] + +# ------------------------------------------------------------------------------ +# FrequencyToMonitorTxs frequency of the resending failed txs +# Duration expressed in units: [ns, us, ms, s, m, h, d]" +# ------------------------------------------------------------------------------ +# FrequencyToMonitorTxs = "1s" + +# ------------------------------------------------------------------------------ +# WaitTxToBeMined time to wait after transaction was sent to the ethereum +# ------------------------------------------------------------------------------ +# WaitTxToBeMined = "2s" + +# ------------------------------------------------------------------------------ +# GetReceiptMaxTime is the max time to wait to get the receipt of the mined transaction +# ------------------------------------------------------------------------------ +# GetReceiptMaxTime = "250ms" + +# ------------------------------------------------------------------------------ +# GetReceiptWaitInterval is the time to sleep before trying to get the receipt of the mined transaction +# ------------------------------------------------------------------------------ +# GetReceiptWaitInterval = "1s" + +# ------------------------------------------------------------------------------ +# ForcedGas is the amount of gas to be forced in case of gas estimation error +# ------------------------------------------------------------------------------ +# ForcedGas = 0 + +# ------------------------------------------------------------------------------ +# GasPriceMarginFactor is used to multiply the suggested gas price provided by the network +# in order to allow a different gas price to be set for all the transactions and making it +# easier to have the txs prioritized in the pool, default value is 1. +# +# example: +# suggested gas price: 100 +# GasPriceMarginFactor: 1 +# gas price = 100 +# +# suggested gas price: 100 +# GasPriceMarginFactor: 1.1 +# gas price = 110 +# ------------------------------------------------------------------------------ +# GasPriceMarginFactor = 1 + +# ------------------------------------------------------------------------------ +# MaxGasPriceLimit helps avoiding transactions to be sent over an specified +# gas price amount, default value is 0, which means no limit. +# If the gas price provided by the network and adjusted by the GasPriceMarginFactor +# is greater than this configuration, transaction will have its gas price set to +# the value configured in this config as the limit. +# +# example: +# suggested gas price: 100 +# gas price margin factor: 20% +# max gas price limit: 150 +# tx gas price = 120 +# +# suggested gas price: 100 +# gas price margin factor: 20% +# max gas price limit: 110 +# tx gas price = 110 +# ------------------------------------------------------------------------------ +# MaxGasPriceLimit = 0 + +# ------------------------------------------------------------------------------ +# StoragePath is the path of the internal storage +# ------------------------------------------------------------------------------ +# StoragePath = "/tmp/ethtxmanager-aggoracle.sqlite" + +# ------------------------------------------------------------------------------ +# ReadPendingL1Txs is a flag to enable the reading of pending L1 txs +# It can only be enabled if DBPath is empty +# ------------------------------------------------------------------------------ +# ReadPendingL1Txs = false + +# ------------------------------------------------------------------------------ +# SafeStatusL1NumberOfBlocks overwrites the number of blocks to consider a tx as safe +# overwriting the default value provided by the network +# 0 means that the default value will be used +# ------------------------------------------------------------------------------ +# SafeStatusL1NumberOfBlocks = 5 + +# ------------------------------------------------------------------------------ +# FinalizedStatusL1NumberOfBlocks overwrites the number of blocks to +# consider a tx as finalized overwriting the default value provided by the network +# 0 means that the default value will be used +# ------------------------------------------------------------------------------ +# FinalizedStatusL1NumberOfBlocks = 10 + +[AggOracle.EVMSender.EthTxManager.Etherman] +# ------------------------------------------------------------------------------ +# Needs to be set to be the sovereign L2 chain id +# ------------------------------------------------------------------------------ +L1ChainID = "20201" + +# ============================================================================== +# ____ ____ ___ ____ ____ _____ _ ____ ______ ___ _ ____ +# | __ )| _ \|_ _| _ \ / ___| ____| | |___ \/ ___\ \ / / \ | |/ ___| +# | _ \| |_) || || | | | | _| _| | | __) \___ \\ V /| \| | | +# | |_) | _ < | || |_| | |_| | |___| |___ / __/ ___) || | | |\ | |___ +# |____/|_| \_\___|____/ \____|_____|_____|_____|____/ |_| |_| \_|\____| +# ------------------------------------------------------------------------------ +[BridgeL2Sync] +# ------------------------------------------------------------------------------ +# BridgeAddr is the address of the sovereign bridge contract on L2 +# ------------------------------------------------------------------------------ +BridgeAddr = "0xC8cbEBf950B9Df44d987c8619f092beA980fF038" + +# ------------------------------------------------------------------------------ +# BlockFinality selects which L2 block type to use when querying the bridge +# contract for synchronization +# Accepted values: +# - LatestBlock +# - SafeBlock +# - PendingBlock +# - FinalizedBlock +# ------------------------------------------------------------------------------ +BlockFinality = "LatestBlock" + +[ReorgDetectorL2] +# ------------------------------------------------------------------------------ +# FinalizedBlock defines which L2 block tag is used as the source of truth when +# checking for reorgs on L2 +# Accepted values: +# - LatestBlock +# - SafeBlock +# - PendingBlock +# - FinalizedBlock +# +# cdk-erigon sovereign chains never advance the "finalized" (nor "safe") block +# tag past genesis, so the reorg detector never drops finalized blocks from its +# tracked set. That set then grows without bound and the detector re-queries a +# header for every tracked block on each check tick; under constrained CI CPU +# this starves the L2 bridge syncer's downloader and the sync silently stalls, +# so L2->L1 bridge claims never become ready. op-stack chains advance +# "finalized" through op-node, and anvil advances it through +# --slots-in-an-epoch; both keep the default tag. Track the latest block for +# cdk-erigon so the tracked set stays bounded (matches BridgeL2Sync). +# ------------------------------------------------------------------------------ +FinalizedBlock = "FinalizedBlock" + +# ------------------------------------------------------------------------------ +# DBPath path of the sqlite db +# ------------------------------------------------------------------------------ +# DBPath = "/tmp/bridgel2sync.sqlite" + +# ------------------------------------------------------------------------------ +# First block that will be queried when starting the synchronization from scratch. +# It should be a number equal or bellow the creation of the bridge contract +# ------------------------------------------------------------------------------ +# InitialBlockNum = 0 + +# ------------------------------------------------------------------------------ +# The amount of blocks that will be queried to the client on each request +# ------------------------------------------------------------------------------ +# SyncBlockChunkSize = 100 + +# ------------------------------------------------------------------------------ +# The time that will be waited when an unexpected error happens before retry +# ------------------------------------------------------------------------------ +# RetryAfterErrorPeriod = "1s" + +# ------------------------------------------------------------------------------ +# The maximum number of consecutive attempts that will happen before panic. +# Any number smaller than zero will be considered as unlimited retries +# ------------------------------------------------------------------------------ +# MaxRetryAttemptsAfterError = -1 + +# ------------------------------------------------------------------------------ +# Time that will be waited when the synchronizer has reached the latest block +# ------------------------------------------------------------------------------ +# WaitForNewBlocksPeriod = "3s" + +# ============================================================================== +# _ _ ___ _ _ _____ ___ _____ ____ _____ _____ ______ ___ _ ____ +# | | / |_ _| \ | | ___/ _ \_ _| _ \| ____| ____/ ___\ \ / / \ | |/ ___| +# | | | || || \| | |_ | | | || | | |_) | _| | _| \___ \\ V /| \| | | +# | |___| || || |\ | _|| |_| || | | _ <| |___| |___ ___) || | | |\ | |___ +# |_____|_|___|_| \_|_| \___/ |_| |_| \_\_____|_____|____/ |_| |_| \_|\____| +# +# ------------------------------------------------------------------------------ +[L1InfoTreeSync] +# ------------------------------------------------------------------------------ +# The initial block number from which to start syncing. +# Default: 0 +# ------------------------------------------------------------------------------ +InitialBlock = "60" + +# ============================================================================== +# _ ____ ____ _____ ____ ______ ___ _ ____ +# | | |___ \ / ___| ____| _ \/ ___\ \ / / \ | |/ ___| +# | | __) | | _| _| | |_) \___ \\ V /| \| | | +# | |___ / __/| |_| | |___| _ < ___) || | | |\ | |___ +# |_____|_____|\____|_____|_| \_\____/ |_| |_| \_|\____| +# ============================================================================== +[L2GERSync] +# ------------------------------------------------------------------------------ +# BlockFinality indicates which finality follows AggLayer accepted values are: +# LatestBlock, SafeBlock, PendingBlock, FinalizedBlock, EarliestBlock +# Default value is "LatestBlock" +# ------------------------------------------------------------------------------ +BlockFinality = "LatestBlock" + +# ============================================================================== +# _ _ __ +# __ _ __ _ __ _ ___| |__ __ _(_)_ __ _ __ _ __ ___ ___ / _| __ _ ___ _ __ +# / _` |/ _` |/ _` |/ __| '_ \ / _` | | '_ \| '_ \| '__/ _ \ / _ \| |_ / _` |/ _ \ '_ \ +#| (_| | (_| | (_| | (__| | | | (_| | | | | | |_) | | | (_) | (_) | _| (_| | __/ | | | +# \__,_|\__, |\__, |\___|_| |_|\__,_|_|_| |_| .__/|_| \___/ \___/|_| \__, |\___|_| |_| +# |___/ |___/ |_| |___/ +# ------------------------------------------------------------------------------ +[AggchainProofGen] +# ------------------------------------------------------------------------------ +# SovereignRollupAddr is the address of the sovereign rollup contract on L1 +# ------------------------------------------------------------------------------ +SovereignRollupAddr = "0x414e9E227e4b589aF92200508aF5399576530E4e" + +# ------------------------------------------------------------------------------ +# GlobalExitRootL2Addr is the address of the GlobalExitRootManager contract on l2 sovereign chain +# this address is needed for the AggchainProof mode of the AggSender +# ------------------------------------------------------------------------------ +GlobalExitRootL2 = "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa" + +[AggchainProofGen.AggkitProverClient] +# ------------------------------------------------------------------------------ +# UseTLS is a flag to enable the AggkitProver TLS handshake in the AggSender-AggkitProver gRPC connection +# ------------------------------------------------------------------------------ +# UseTLS = false + +# ============================================================================== +# ____ __ _ _ _ +# | _ \ _ __ ___ / _(_) (_)_ __ __ _ +# | |_) | '__/ _ \| |_| | | | '_ \ / _` | +# | __/| | | (_) | _| | | | | | | (_| | +# |_| |_| \___/|_| |_|_|_|_| |_|\__, | +# |___/ +# ------------------------------------------------------------------------------ +[Profiling] +# ------------------------------------------------------------------------------ +# ProfilingHost is the address to bind the profiling server +# Default: "localhost" +# ------------------------------------------------------------------------------ +ProfilingHost = "0.0.0.0" + +# ------------------------------------------------------------------------------ +# ProfilingPort is the port to bind the profiling server +# Default: 6060 +# ------------------------------------------------------------------------------ +ProfilingPort = 6060 + +# ------------------------------------------------------------------------------ +# ProfilingEnabled is the flag to enable/disable the profiling server +# Default: false +# ------------------------------------------------------------------------------ +ProfilingEnabled = true + + +# https://github.com/agglayer/aggkit/pull/744 +[Validator] +EnableRPC = true +# Signer = { Method = "mock" } +Signer = { Method = "local", Path = "/etc/aggkit/sequencer.keystore", Password = "pSnv6Dh5s9ahuzGzH9RoCDrKAMddaX3m" } +# PessimisticProof or AggchainProof +Mode = "PessimisticProof" + +[Validator.ServerConfig] +Host = "0.0.0.0" +Port = 5578 +MaxDecodingMessageSize = 1073741824 # 1GB + +# Is it necessary to specify all of these values again? +[Validator.LerQuerierConfig] +RollupManagerAddr = "0x6c6c009cC348976dB4A908c92B24433d4F6edA43" +RollupCreationBlockL1 = "60" + +[Validator.AgglayerClient] +Cached = true + +[Validator.AgglayerClient.ConfigurationCache] +TTL = "15m" +Capacity = 100 + +[Validator.AgglayerClient.GRPC] +URL = "http://agglayer:4443" +UseTLS = false + +# ============================================================================== +# _ _____ ___ ____ _ _ ___ __ __ +# / \ _ _ |_ _/ _ \ / ___| | / \ |_ _| \/ | +# / _ \ | | | | || | | || | | | / _ \ | || |\/| | +# / ___ \|_| | | || |_| || |___| |___ / ___ \ | || | | | +# /_/ \_\__,_| |_| \___/ \____|_____/_/ \_\___|_| |_| +# +# ------------------------------------------------------------------------------ +# Auto Claim is entirely absent from this template's defaults (the aggkit +# binary's own config/default.go supplies working, inert defaults -- +# Claimers = [], L2ToLxBridgeDetector.Enabled = false -- when this section is +# omitted). This block only renders when aggkit_autoclaim_enabled is true, +# i.e. when this instance's own l2_network_id is listed in +# aggkit_autoclaim_destinations (see input_parser.star). It configures a +# SINGLE claimer targeting this instance's own destination network -- never +# network 0 (L1); deposits destined to L1 must stay manual-claim-only for the +# bridge UI. Both bridge detectors are enabled (L1ToL2 for L1->L2, L2ToLx for +# L2->L2 and L2->L1 discovery), but only an L2-destination claimer is ever +# configured here, so L2->L1 exits are discovered (claim-proof works) without +# ever being auto-claimed. +# Requires "autoclaim" to also be listed in aggkit_components (see +# _log_autoclaim_warning in aggkit.star). +# ------------------------------------------------------------------------------ +[AutoClaim] +DryRun = false +StoragePath = "/tmp/autoclaim.sqlite" + +[AutoClaim.API] +Enabled = false + +[AutoClaim.L1ToL2BridgeDetector] +Enabled = true +StartBlock = 0 +PollInterval = "3s" +EtrogL1UpgradeBlock = 0 + +[AutoClaim.L2ToLxBridgeDetector] +Enabled = true +StartL1Block = 0 +PollInterval = "3s" + +[AutoClaim.BridgeServiceFinder] +RollupManagerAddr = "0x6c6c009cC348976dB4A908c92B24433d4F6edA43" +PollInterval = "3s" + +[AutoClaim.BridgeServiceFinder.BridgeURLs] +1 = "http://aggkit-001:5577" +2 = "http://aggkit-002:5577" + +[[AutoClaim.Claimers]] +Enabled = true +ID = "autoclaim-001" +NetworkType = "EVM" +NetworkID = 1 +URLRPC = "http://l2-anvil-001:8545" +BridgeAddr = "0xC8cbEBf950B9Df44d987c8619f092beA980fF038" +PolicyName = "allow-all" +GasOffset = 100000 +WaitPeriod = "1s" +RetryAfter = "1s" +MaxRetries = 180 + +[AutoClaim.Claimers.Policy] +AllowMessageClaims = false +AllowedOrigins = [] +AllowedTokens = [] +ManualFallback = false +MaxGas = 500000 + +[AutoClaim.Claimers.EthTxManager] +FrequencyToMonitorTxs = "1s" +WaitTxToBeMined = "2s" +WaitReceiptMaxTime = "250ms" +WaitReceiptCheckInterval = "1s" +PrivateKeys = [ + {Method = "local", Path = "/etc/aggkit/aggoracle.keystore", Password = "pSnv6Dh5s9ahuzGzH9RoCDrKAMddaX3m"}, +] +ForcedGas = 0 +GasPriceMarginFactor = 1 +MaxGasPriceLimit = 0 +StoragePath = "/tmp/ethtxmanager-autoclaim.sqlite" +ReadPendingL1Txs = false +SafeStatusL1NumberOfBlocks = 0 +FinalizedStatusL1NumberOfBlocks = 0 +EstimateGasMaxRetries = 1 + +[AutoClaim.Claimers.EthTxManager.Etherman] +URL = "http://l2-anvil-001:8545" +MultiGasProvider = false +L1ChainID = 20201 +HTTPHeaders = {} + diff --git a/test/e2e/envs/anvil-2chains/config/001/aggoracle.keystore b/test/e2e/envs/anvil-2chains/config/001/aggoracle.keystore new file mode 100644 index 000000000..9e0b74de2 --- /dev/null +++ b/test/e2e/envs/anvil-2chains/config/001/aggoracle.keystore @@ -0,0 +1,20 @@ +{ + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "5ec2fbc61368fbab89b6e864753e0a8b" + }, + "ciphertext": "8a09b2e806d443ddb7575e15628133da3dd463a6f66e6398024226b7a4063b63", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 8192, + "p": 1, + "r": 8, + "salt": "9639bdb4596bae41fdd20986234448ff6f0640cbb52a91fee96e8f5bf552b8fe" + }, + "mac": "46b3edb02407e54719aea1726c27af272c40953e1a4ef1e1f0676cc2285d30a5" + }, + "id": "d3091e5f-d122-44cb-b47a-308fb39790a4", + "version": 3 +} diff --git a/test/e2e/envs/anvil-2chains/config/001/sequencer.keystore b/test/e2e/envs/anvil-2chains/config/001/sequencer.keystore new file mode 100644 index 000000000..e09c8c122 --- /dev/null +++ b/test/e2e/envs/anvil-2chains/config/001/sequencer.keystore @@ -0,0 +1,20 @@ +{ + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "5bf47bbafe1b518910544614a43eec8c" + }, + "ciphertext": "0a8abf1ce90945350f6a14356b79b15d69eec9e77d4357e500147ce1c0718e82", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 8192, + "p": 1, + "r": 8, + "salt": "94913f125b1d4f76cc1d010b0567e30cd5966e561abba9d8944be063b4345b50" + }, + "mac": "f3c73279efd74598e00cc2fe5ee8a52321920d564bf92da815d4053e263a02bc" + }, + "id": "4242e2de-a97c-4f0e-84c1-d119a62595a2", + "version": 3 +} diff --git a/test/e2e/envs/anvil-2chains/config/001/sovereignadmin.keystore b/test/e2e/envs/anvil-2chains/config/001/sovereignadmin.keystore new file mode 100644 index 000000000..88da90699 --- /dev/null +++ b/test/e2e/envs/anvil-2chains/config/001/sovereignadmin.keystore @@ -0,0 +1,20 @@ +{ + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "c75071241b98d74fb8a5aa8aa386d8e5" + }, + "ciphertext": "ea7a83722729a2f2c3c557773c74ab7bd30e59bb6faff3c67334fbb01486fd40", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 8192, + "p": 1, + "r": 8, + "salt": "53596bb73362ca70fa6f9b4e5ff57ca2ea7a6f1448dafb2c763ebd8668ecdf83" + }, + "mac": "f5a216e073d175068d3bcc1901cf9e573bd05540ffef1d03072ecbee339f0967" + }, + "id": "1dfb1229-60c2-4b5f-99a0-c5ddbe7341b2", + "version": 3 +} diff --git a/test/e2e/envs/anvil-2chains/config/002/aggkit-config.toml b/test/e2e/envs/anvil-2chains/config/002/aggkit-config.toml new file mode 100644 index 000000000..dc4d389a2 --- /dev/null +++ b/test/e2e/envs/anvil-2chains/config/002/aggkit-config.toml @@ -0,0 +1,751 @@ +# ============================================================================== +# _ ____ ____ _ _____ _____ +# / \ / ___|/ ___| |/ /_ _|_ _| +# / _ \| | _| | _| ' / | | | | +# / ___ \ |_| | |_| | . \ | | | | +# /_/ \_\____|\____|_|\_\___| |_| +# +# This is a reference config file used by the Kurtosis CDK Testing +# setup. The values here should work, but are necessarily meant for +# production environments. DYOR +# The below configs are the default mandatory parameters to be used. +# ============================================================================== + +PathRWData = "/tmp" +L1URL = "http://anvil-001:8545" +L2URL = "http://l2-anvil-002:8545" +OpNodeURL = "http://l2-anvil-002:8545" + +# Check if agglayer grpc or readrpc should be used for AggLayerURL +AggLayerURL = "http://agglayer:4443" + +AggchainProofURL= "aggkit-prover-002:4446" +SequencerPrivateKeyPath = "/etc/aggkit/sequencer.keystore" +SequencerPrivateKeyPassword = "pSnv6Dh5s9ahuzGzH9RoCDrKAMddaX3m" +RPCURL = "http://l2-anvil-002:8545" + +# These values can be overridden directly from genesis.json +rollupCreationBlockNumber = "60" +rollupManagerCreationBlockNumber = "60" +genesisBlockNumber = "60" +# ------------------------------------------------------------------------------ + +# ============================================================================== +# _ _ ____ ___ _ _ _____ ___ ____ +# | | / |/ ___/ _ \| \ | | ___|_ _/ ___| +# | | | | | | | | | \| | |_ | | | _ +# | |___| | |__| |_| | |\ | _| | | |_| | +# |_____|_|\____\___/|_| \_|_| |___\____| +# +# ------------------------------------------------------------------------------ +[L1Config] +# ------------------------------------------------------------------------------ +# URL is the L1 network url +# ------------------------------------------------------------------------------ +URL = "http://anvil-001:8545" + +# ------------------------------------------------------------------------------ +# L1 chain id +# ------------------------------------------------------------------------------ +chainId = "271828" + +# ------------------------------------------------------------------------------ +# Address of the zkevm global exit root contract on L1 +# ------------------------------------------------------------------------------ +polygonZkEVMGlobalExitRootAddress = "0x1f7ad7caA53e35b4f0D138dC5CBF91aC108a2674" + +# ------------------------------------------------------------------------------ +# Address of the rollup manager contract on L1 +# ------------------------------------------------------------------------------ +polygonRollupManagerAddress = "0x6c6c009cC348976dB4A908c92B24433d4F6edA43" + +# ------------------------------------------------------------------------------ +# Address of the pol token address on L1 +# ------------------------------------------------------------------------------ +polTokenAddress = "0xEdE9cf798E0fE25D35469493f43E88FeA4a5da0E" + +# ------------------------------------------------------------------------------ +# Address of the sovereign rollup contract on L2 +# ------------------------------------------------------------------------------ +polygonZkEVMAddress = "0x5D1A491A416feEbf8C123A558ec28A239960bd0E" + +BridgeAddr = "0xC8cbEBf950B9Df44d987c8619f092beA980fF038" + +# ============================================================================== +# _ ____ ____ ___ _ _ _____ ___ ____ +# | | |___ \ / ___/ _ \| \ | | ___|_ _/ ___| +# | | __) | | | | | | \| | |_ | | | _ +# | |___ / __/| |__| |_| | |\ | _| | | |_| | +# |_____|_____|\____\___/|_| \_|_| |___\____| +# +# ------------------------------------------------------------------------------ +[L2Config] +# ------------------------------------------------------------------------------ +# Address of the sovereign global exit root proxy contract on L2 +# ------------------------------------------------------------------------------ +GlobalExitRootAddr = "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa" +BridgeAddr = "0xC8cbEBf950B9Df44d987c8619f092beA980fF038" + +# ============================================================================== +# _ ___ ____ +# | | / _ \ / ___| +# | | | | | | | _ +# | |__| |_| | |_| | +# |_____\___/ \____| +# +# ------------------------------------------------------------------------------ +[Log] +# ------------------------------------------------------------------------------ +# Environment generally dictates the format of the logs and the +# sampling rate. We often default to production even for development +# because of the JSON encoding. +# +# https://github.com/uber-go/zap/blob/a55bdc32f526699c3b4cc51a2cc97e944d02fbbf/config.go#L120 +# https://github.com/uber-go/zap/blob/a55bdc32f526699c3b4cc51a2cc97e944d02fbbf/config.go#L161 +# ------------------------------------------------------------------------------ +Environment = "development" + +# ------------------------------------------------------------------------------ +# Level determines the log level that will be written to the +# log. Generally we'll switch to debug if we want to troubleshoot +# something specifically otherwise we leave it at info +# ------------------------------------------------------------------------------ +Level = "info" + +# ------------------------------------------------------------------------------ +# Outputs define the output paths for writing logs. The default is to +# write to stderr, but other output paths should be supported +# +# https://github.com/uber-go/zap/blob/a55bdc32f526699c3b4cc51a2cc97e944d02fbbf/writer.go#L32-L50 +# ------------------------------------------------------------------------------ +Outputs = ["stderr"] + +# ============================================================================== +# ____ ____ ____ +# | _ \| _ \ / ___| +# | |_) | |_) | | +# | _ <| __/| |___ +# |_| \_\_| \____| +# +# ------------------------------------------------------------------------------ +[RPC] +# ------------------------------------------------------------------------------ +# Port will configure the port that the JSON RPC server will listen on +# ------------------------------------------------------------------------------ +Port = "5576" + +# ============================================================================== +# ____ _____ ____ _____ +# | _ \| ____/ ___|_ _| +# | |_) | _| \___ \ | | +# | _ <| |___ ___) || | +# |_| \_\_____|____/ |_| +# +# ------------------------------------------------------------------------------ +[PublicREST] +# ------------------------------------------------------------------------------ +# Port will configure the port that the REST API HTTP server will +# listen on +# ------------------------------------------------------------------------------ +Port = "5577" + +# ============================================================================== +# _ ____ ____ ____ _____ _ _ ____ _____ ____ +# / \ / ___|/ ___/ ___|| ____| \ | | _ \| ____| _ \ +# / _ \| | _| | _\___ \| _| | \| | | | | _| | |_) | +# / ___ \ |_| | |_| |___) | |___| |\ | |_| | |___| _ < +# /_/ \_\____|\____|____/|_____|_| \_|____/|_____|_| \_\ +# +# ------------------------------------------------------------------------------ +[AggSender] +# ------------------------------------------------------------------------------ +# StoragePath is the path of the sqlite db for the AggSender to store the data +# ------------------------------------------------------------------------------ +# StoragePath = "/tmp" + +# ------------------------------------------------------------------------------ +# AggsenderPrivateKey is the private key which is used to sign certificates +# ------------------------------------------------------------------------------ +AggSenderPrivateKey = {Path = "/etc/aggkit/sequencer.keystore", Password = "pSnv6Dh5s9ahuzGzH9RoCDrKAMddaX3m"} + +# ------------------------------------------------------------------------------ +# Defines if a check on aggsender proposer startup will be performed +# to see if the proposer is in the multisig committee +# ------------------------------------------------------------------------------ +# RequireCommitteeMembershipCheck = false +Mode = "PessimisticProof" +CheckStatusCertificateInterval = "1s" + +# ------------------------------------------------------------------------------ +# TriggerCertMode is the mode used to trigger certificate sending +# Valid values are: "EpochBased", "NewBridge", "ASAP", "Auto" +# EpochBased: this is the legacy mode that waits until reach a percentage of a epoch (you can configure here: AggSender.TriggerEpochBased) +# ASAP: this mode try to generate a new certificate after a successful settled certificate +# NewBridge: Each time that a new bridge is done in L2 it generate a certificate (if it's possible) (experimental) +# ------------------------------------------------------------------------------ +TriggerCertMode = "ASAP" + +# # Encouraged to use the default values - the parameters are left commented out for reference +# +# [AggSender.TriggerASAP] +# # Delay between the moment the aggsender becomes idle and when it sends a new certificate trigger +# DelayBetweenCertificates = "1s" +# # Minimum time that must elapse between certificate generation triggers, regardless of the trigger source +# MinimumNewCertificateInterval = "5s" +# # When enabled, the ASAP trigger will automatically generate certificate triggers when new bridge events are detected on L2 +# OnNewL2Bridge = false +# + +# ------------------------------------------------------------------------------ +# MaxCertSize is the maximum size of the certificate +# i.e (the emitted certificate cannot be bigger that this size) +# 0 is infinite +# ------------------------------------------------------------------------------ +# MaxCertSize = 0 +[AggSender.ValidatorClient] +URL = "aggkit-validator-002:5578" + +[AggSender.AggkitProverClient] +UseTLS = false + +# ------------------------------------------------------------------------------ +# URLRPCL2 is the URL of the L2 RPC node +# ------------------------------------------------------------------------------ +# URLRPCL2 = "http://l2-anvil-002:8545" + +# ------------------------------------------------------------------------------ +# EpochNotificationPercentage indicates the percentage of the epoch +# the AggSender should send the certificate +# 0 -> Begin +# 50 -> Middle +# ------------------------------------------------------------------------------ +# EpochNotificationPercentage = 50 + +# ------------------------------------------------------------------------------ +# MaxRetriesStoreCertificate is the maximum number of retries to store a certificate +# 0 is infinite +# ------------------------------------------------------------------------------ +# MaxRetriesStoreCertificate = 3 + +# ------------------------------------------------------------------------------ +# DelayBetweenRetries is the delay between retries +# Duration expressed in units: [ns, us, ms, s, m, h, d]" +# ------------------------------------------------------------------------------ +# DelayBetweenRetries = 5s + +# ------------------------------------------------------------------------------ +# BridgeMetadataAsHash is a flag to import the bridge metadata as hash +# ------------------------------------------------------------------------------ +# BridgeMetadataAsHash = false + +# ------------------------------------------------------------------------------ +# DryRun is a flag to enable the dry run mode +# in this mode the AggSender will not send the certificates to Agglayer +# ------------------------------------------------------------------------------ +# DryRun = false + +# ------------------------------------------------------------------------------ +# EnableRPC is a flag to enable the RPC for aggsender +# ------------------------------------------------------------------------------ +# EnableRPC = false + +[AggSender.AgglayerClient] + +[[AggSender.AgglayerClient.APIRateLimits]] +MethodName = "SendCertificate" + +[AggSender.AgglayerClient.APIRateLimits.RateLimit] +# Disable limit +NumRequests = 0 + +[AggSender.AgglayerClient.GRPC] +URL = "http://agglayer:4443" +MinConnectTimeout = "5s" +RequestTimeout = "300s" +UseTLS = false + +[AggSender.AgglayerClient.GRPC.Retry] +InitialBackoff = "1s" +MaxBackoff = "10s" +BackoffMultiplier = 2.0 +MaxAttempts = 20 + +[AggSender.StorageRetainCertificatesPolicy] +# ------------------------------------------------------------------------------ +# RetainCertificatesCount controls how many certificates to retain on storage. +# If set to zero, all certificates will be stored. +# ------------------------------------------------------------------------------ +# RetainCertificatesCount = 0 + +# ------------------------------------------------------------------------------ +# KeepCertificatesHistory is a flag to keep the certificates history on storage +# ------------------------------------------------------------------------------ +# KeepCertificatesHistory = true + +# ------------------------------------------------------------------------------ +# RetryCertAfterInError when a cert pass to 'InError'state +# the AggSender will try to resend it immediately +# ------------------------------------------------------------------------------ +# RetryCertAfterInError = false + +# ------------------------------------------------------------------------------ +# RequireNoFEPBlockGap is true if the AggSender should not accept a gap between +# lastBlock from lastCertificate and first block of FEP +# ------------------------------------------------------------------------------ +# RequireNoFEPBlockGap = false + +# ------------------------------------------------------------------------------ +# RequireOneBridgeInPPCertificate is a flag to force the AggSender to have at least one bridge exit +# for the Pessimistic Proof certificates +# ------------------------------------------------------------------------------ +# RequireOneBridgeInPPCertificate = false + +# ------------------------------------------------------------------------------ +# MaxL2BlockNumber is the last L2 block number that is going to be included in a certificate +# 0 means disabled +# ------------------------------------------------------------------------------ +# MaxL2BlockNumber = 0 + +# ------------------------------------------------------------------------------ +# StopOnFinishedSendingAllCertificates is a flag to stop the AggSender when it finishes sending all certificates +# up to MaxL2BlockNumber +# ------------------------------------------------------------------------------ +# StopOnFinishedSendingAllCertificates = false + +# ============================================================================== +# _ ____ ____ ___ ____ _ ____ _ _____ +# / \ / ___|/ ___|/ _ \| _ \ / \ / ___| | | ____| +# / _ \| | _| | _| | | | |_) | / _ \| | | | | _| +# / ___ \ |_| | |_| | |_| | _ < / ___ \ |___| |___| |___ +# /_/ \_\____|\____|\___/|_| \_\/_/ \_\____|_____|_____| +# +# ------------------------------------------------------------------------------ +[AggOracle] +# ------------------------------------------------------------------------------ +# TargetChainType currently only supports "EVM" +# ------------------------------------------------------------------------------ +# TargetChainType = "EVM" + +# ------------------------------------------------------------------------------ +# URLRPCL1 is the URL of the L1 RPC node +# ------------------------------------------------------------------------------ +# URLRPCL1 = "http://anvil-001:8545" + +# ------------------------------------------------------------------------------ +# Duration expressed in units: [ns, us, ms, s, m, h, d]" +# ------------------------------------------------------------------------------ +WaitPeriodNextGER = "10s" + +# ------------------------------------------------------------------------------ +# +# ------------------------------------------------------------------------------ +EnableAggOracleCommittee = false + +[AggOracle.EVMSender] +# ------------------------------------------------------------------------------ +# Address of the sovereign global exit root proxy contract on L2 +# ------------------------------------------------------------------------------ +GlobalExitRootL2 = "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa" + +# ------------------------------------------------------------------------------ +# URLRPCL2 is the URL of the L2 RPC node +# ------------------------------------------------------------------------------ +# URLRPCL2 = "http://l2-anvil-002:8545" + +# ------------------------------------------------------------------------------ +# GasOffset is the gas to add on the estimated gas when sending the claim txs +# ------------------------------------------------------------------------------ +# GasOffset = 0 + +# ------------------------------------------------------------------------------ +# Duration expressed in units: [ns, us, ms, s, m, h, d]" +# ------------------------------------------------------------------------------ +WaitPeriodMonitorTx = "10s" + +# ------------------------------------------------------------------------------ +# +# ------------------------------------------------------------------------------ + +[AggOracle.EVMSender.EthTxManager] +# ------------------------------------------------------------------------------ +# PrivateKeys defines all the key store files that are going +# to be read in order to provide the private keys to sign the L1 txs +# ------------------------------------------------------------------------------ +PrivateKeys = [{Path = "/etc/aggkit/aggoracle.keystore", Password = "pSnv6Dh5s9ahuzGzH9RoCDrKAMddaX3m"}] + +# ------------------------------------------------------------------------------ +# FrequencyToMonitorTxs frequency of the resending failed txs +# Duration expressed in units: [ns, us, ms, s, m, h, d]" +# ------------------------------------------------------------------------------ +# FrequencyToMonitorTxs = "1s" + +# ------------------------------------------------------------------------------ +# WaitTxToBeMined time to wait after transaction was sent to the ethereum +# ------------------------------------------------------------------------------ +# WaitTxToBeMined = "2s" + +# ------------------------------------------------------------------------------ +# GetReceiptMaxTime is the max time to wait to get the receipt of the mined transaction +# ------------------------------------------------------------------------------ +# GetReceiptMaxTime = "250ms" + +# ------------------------------------------------------------------------------ +# GetReceiptWaitInterval is the time to sleep before trying to get the receipt of the mined transaction +# ------------------------------------------------------------------------------ +# GetReceiptWaitInterval = "1s" + +# ------------------------------------------------------------------------------ +# ForcedGas is the amount of gas to be forced in case of gas estimation error +# ------------------------------------------------------------------------------ +# ForcedGas = 0 + +# ------------------------------------------------------------------------------ +# GasPriceMarginFactor is used to multiply the suggested gas price provided by the network +# in order to allow a different gas price to be set for all the transactions and making it +# easier to have the txs prioritized in the pool, default value is 1. +# +# example: +# suggested gas price: 100 +# GasPriceMarginFactor: 1 +# gas price = 100 +# +# suggested gas price: 100 +# GasPriceMarginFactor: 1.1 +# gas price = 110 +# ------------------------------------------------------------------------------ +# GasPriceMarginFactor = 1 + +# ------------------------------------------------------------------------------ +# MaxGasPriceLimit helps avoiding transactions to be sent over an specified +# gas price amount, default value is 0, which means no limit. +# If the gas price provided by the network and adjusted by the GasPriceMarginFactor +# is greater than this configuration, transaction will have its gas price set to +# the value configured in this config as the limit. +# +# example: +# suggested gas price: 100 +# gas price margin factor: 20% +# max gas price limit: 150 +# tx gas price = 120 +# +# suggested gas price: 100 +# gas price margin factor: 20% +# max gas price limit: 110 +# tx gas price = 110 +# ------------------------------------------------------------------------------ +# MaxGasPriceLimit = 0 + +# ------------------------------------------------------------------------------ +# StoragePath is the path of the internal storage +# ------------------------------------------------------------------------------ +# StoragePath = "/tmp/ethtxmanager-aggoracle.sqlite" + +# ------------------------------------------------------------------------------ +# ReadPendingL1Txs is a flag to enable the reading of pending L1 txs +# It can only be enabled if DBPath is empty +# ------------------------------------------------------------------------------ +# ReadPendingL1Txs = false + +# ------------------------------------------------------------------------------ +# SafeStatusL1NumberOfBlocks overwrites the number of blocks to consider a tx as safe +# overwriting the default value provided by the network +# 0 means that the default value will be used +# ------------------------------------------------------------------------------ +# SafeStatusL1NumberOfBlocks = 5 + +# ------------------------------------------------------------------------------ +# FinalizedStatusL1NumberOfBlocks overwrites the number of blocks to +# consider a tx as finalized overwriting the default value provided by the network +# 0 means that the default value will be used +# ------------------------------------------------------------------------------ +# FinalizedStatusL1NumberOfBlocks = 10 + +[AggOracle.EVMSender.EthTxManager.Etherman] +# ------------------------------------------------------------------------------ +# Needs to be set to be the sovereign L2 chain id +# ------------------------------------------------------------------------------ +L1ChainID = "20202" + +# ============================================================================== +# ____ ____ ___ ____ ____ _____ _ ____ ______ ___ _ ____ +# | __ )| _ \|_ _| _ \ / ___| ____| | |___ \/ ___\ \ / / \ | |/ ___| +# | _ \| |_) || || | | | | _| _| | | __) \___ \\ V /| \| | | +# | |_) | _ < | || |_| | |_| | |___| |___ / __/ ___) || | | |\ | |___ +# |____/|_| \_\___|____/ \____|_____|_____|_____|____/ |_| |_| \_|\____| +# ------------------------------------------------------------------------------ +[BridgeL2Sync] +# ------------------------------------------------------------------------------ +# BridgeAddr is the address of the sovereign bridge contract on L2 +# ------------------------------------------------------------------------------ +BridgeAddr = "0xC8cbEBf950B9Df44d987c8619f092beA980fF038" + +# ------------------------------------------------------------------------------ +# BlockFinality selects which L2 block type to use when querying the bridge +# contract for synchronization +# Accepted values: +# - LatestBlock +# - SafeBlock +# - PendingBlock +# - FinalizedBlock +# ------------------------------------------------------------------------------ +BlockFinality = "LatestBlock" + +[ReorgDetectorL2] +# ------------------------------------------------------------------------------ +# FinalizedBlock defines which L2 block tag is used as the source of truth when +# checking for reorgs on L2 +# Accepted values: +# - LatestBlock +# - SafeBlock +# - PendingBlock +# - FinalizedBlock +# +# cdk-erigon sovereign chains never advance the "finalized" (nor "safe") block +# tag past genesis, so the reorg detector never drops finalized blocks from its +# tracked set. That set then grows without bound and the detector re-queries a +# header for every tracked block on each check tick; under constrained CI CPU +# this starves the L2 bridge syncer's downloader and the sync silently stalls, +# so L2->L1 bridge claims never become ready. op-stack chains advance +# "finalized" through op-node, and anvil advances it through +# --slots-in-an-epoch; both keep the default tag. Track the latest block for +# cdk-erigon so the tracked set stays bounded (matches BridgeL2Sync). +# ------------------------------------------------------------------------------ +FinalizedBlock = "FinalizedBlock" + +# ------------------------------------------------------------------------------ +# DBPath path of the sqlite db +# ------------------------------------------------------------------------------ +# DBPath = "/tmp/bridgel2sync.sqlite" + +# ------------------------------------------------------------------------------ +# First block that will be queried when starting the synchronization from scratch. +# It should be a number equal or bellow the creation of the bridge contract +# ------------------------------------------------------------------------------ +# InitialBlockNum = 0 + +# ------------------------------------------------------------------------------ +# The amount of blocks that will be queried to the client on each request +# ------------------------------------------------------------------------------ +# SyncBlockChunkSize = 100 + +# ------------------------------------------------------------------------------ +# The time that will be waited when an unexpected error happens before retry +# ------------------------------------------------------------------------------ +# RetryAfterErrorPeriod = "1s" + +# ------------------------------------------------------------------------------ +# The maximum number of consecutive attempts that will happen before panic. +# Any number smaller than zero will be considered as unlimited retries +# ------------------------------------------------------------------------------ +# MaxRetryAttemptsAfterError = -1 + +# ------------------------------------------------------------------------------ +# Time that will be waited when the synchronizer has reached the latest block +# ------------------------------------------------------------------------------ +# WaitForNewBlocksPeriod = "3s" + +# ============================================================================== +# _ _ ___ _ _ _____ ___ _____ ____ _____ _____ ______ ___ _ ____ +# | | / |_ _| \ | | ___/ _ \_ _| _ \| ____| ____/ ___\ \ / / \ | |/ ___| +# | | | || || \| | |_ | | | || | | |_) | _| | _| \___ \\ V /| \| | | +# | |___| || || |\ | _|| |_| || | | _ <| |___| |___ ___) || | | |\ | |___ +# |_____|_|___|_| \_|_| \___/ |_| |_| \_\_____|_____|____/ |_| |_| \_|\____| +# +# ------------------------------------------------------------------------------ +[L1InfoTreeSync] +# ------------------------------------------------------------------------------ +# The initial block number from which to start syncing. +# Default: 0 +# ------------------------------------------------------------------------------ +InitialBlock = "60" + +# ============================================================================== +# _ ____ ____ _____ ____ ______ ___ _ ____ +# | | |___ \ / ___| ____| _ \/ ___\ \ / / \ | |/ ___| +# | | __) | | _| _| | |_) \___ \\ V /| \| | | +# | |___ / __/| |_| | |___| _ < ___) || | | |\ | |___ +# |_____|_____|\____|_____|_| \_\____/ |_| |_| \_|\____| +# ============================================================================== +[L2GERSync] +# ------------------------------------------------------------------------------ +# BlockFinality indicates which finality follows AggLayer accepted values are: +# LatestBlock, SafeBlock, PendingBlock, FinalizedBlock, EarliestBlock +# Default value is "LatestBlock" +# ------------------------------------------------------------------------------ +BlockFinality = "LatestBlock" + +# ============================================================================== +# _ _ __ +# __ _ __ _ __ _ ___| |__ __ _(_)_ __ _ __ _ __ ___ ___ / _| __ _ ___ _ __ +# / _` |/ _` |/ _` |/ __| '_ \ / _` | | '_ \| '_ \| '__/ _ \ / _ \| |_ / _` |/ _ \ '_ \ +#| (_| | (_| | (_| | (__| | | | (_| | | | | | |_) | | | (_) | (_) | _| (_| | __/ | | | +# \__,_|\__, |\__, |\___|_| |_|\__,_|_|_| |_| .__/|_| \___/ \___/|_| \__, |\___|_| |_| +# |___/ |___/ |_| |___/ +# ------------------------------------------------------------------------------ +[AggchainProofGen] +# ------------------------------------------------------------------------------ +# SovereignRollupAddr is the address of the sovereign rollup contract on L1 +# ------------------------------------------------------------------------------ +SovereignRollupAddr = "0x5D1A491A416feEbf8C123A558ec28A239960bd0E" + +# ------------------------------------------------------------------------------ +# GlobalExitRootL2Addr is the address of the GlobalExitRootManager contract on l2 sovereign chain +# this address is needed for the AggchainProof mode of the AggSender +# ------------------------------------------------------------------------------ +GlobalExitRootL2 = "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa" + +[AggchainProofGen.AggkitProverClient] +# ------------------------------------------------------------------------------ +# UseTLS is a flag to enable the AggkitProver TLS handshake in the AggSender-AggkitProver gRPC connection +# ------------------------------------------------------------------------------ +# UseTLS = false + +# ============================================================================== +# ____ __ _ _ _ +# | _ \ _ __ ___ / _(_) (_)_ __ __ _ +# | |_) | '__/ _ \| |_| | | | '_ \ / _` | +# | __/| | | (_) | _| | | | | | | (_| | +# |_| |_| \___/|_| |_|_|_|_| |_|\__, | +# |___/ +# ------------------------------------------------------------------------------ +[Profiling] +# ------------------------------------------------------------------------------ +# ProfilingHost is the address to bind the profiling server +# Default: "localhost" +# ------------------------------------------------------------------------------ +ProfilingHost = "0.0.0.0" + +# ------------------------------------------------------------------------------ +# ProfilingPort is the port to bind the profiling server +# Default: 6060 +# ------------------------------------------------------------------------------ +ProfilingPort = 6060 + +# ------------------------------------------------------------------------------ +# ProfilingEnabled is the flag to enable/disable the profiling server +# Default: false +# ------------------------------------------------------------------------------ +ProfilingEnabled = true + + +# https://github.com/agglayer/aggkit/pull/744 +[Validator] +EnableRPC = true +# Signer = { Method = "mock" } +Signer = { Method = "local", Path = "/etc/aggkit/sequencer.keystore", Password = "pSnv6Dh5s9ahuzGzH9RoCDrKAMddaX3m" } +# PessimisticProof or AggchainProof +Mode = "PessimisticProof" + +[Validator.ServerConfig] +Host = "0.0.0.0" +Port = 5578 +MaxDecodingMessageSize = 1073741824 # 1GB + +# Is it necessary to specify all of these values again? +[Validator.LerQuerierConfig] +RollupManagerAddr = "0x6c6c009cC348976dB4A908c92B24433d4F6edA43" +RollupCreationBlockL1 = "60" + +[Validator.AgglayerClient] +Cached = true + +[Validator.AgglayerClient.ConfigurationCache] +TTL = "15m" +Capacity = 100 + +[Validator.AgglayerClient.GRPC] +URL = "http://agglayer:4443" +UseTLS = false + +# ============================================================================== +# _ _____ ___ ____ _ _ ___ __ __ +# / \ _ _ |_ _/ _ \ / ___| | / \ |_ _| \/ | +# / _ \ | | | | || | | || | | | / _ \ | || |\/| | +# / ___ \|_| | | || |_| || |___| |___ / ___ \ | || | | | +# /_/ \_\__,_| |_| \___/ \____|_____/_/ \_\___|_| |_| +# +# ------------------------------------------------------------------------------ +# Auto Claim is entirely absent from this template's defaults (the aggkit +# binary's own config/default.go supplies working, inert defaults -- +# Claimers = [], L2ToLxBridgeDetector.Enabled = false -- when this section is +# omitted). This block only renders when aggkit_autoclaim_enabled is true, +# i.e. when this instance's own l2_network_id is listed in +# aggkit_autoclaim_destinations (see input_parser.star). It configures a +# SINGLE claimer targeting this instance's own destination network -- never +# network 0 (L1); deposits destined to L1 must stay manual-claim-only for the +# bridge UI. Both bridge detectors are enabled (L1ToL2 for L1->L2, L2ToLx for +# L2->L2 and L2->L1 discovery), but only an L2-destination claimer is ever +# configured here, so L2->L1 exits are discovered (claim-proof works) without +# ever being auto-claimed. +# Requires "autoclaim" to also be listed in aggkit_components (see +# _log_autoclaim_warning in aggkit.star). +# ------------------------------------------------------------------------------ +[AutoClaim] +DryRun = false +StoragePath = "/tmp/autoclaim.sqlite" + +[AutoClaim.API] +Enabled = false + +[AutoClaim.L1ToL2BridgeDetector] +Enabled = true +StartBlock = 0 +PollInterval = "3s" +EtrogL1UpgradeBlock = 0 + +[AutoClaim.L2ToLxBridgeDetector] +Enabled = true +StartL1Block = 0 +PollInterval = "3s" + +[AutoClaim.BridgeServiceFinder] +RollupManagerAddr = "0x6c6c009cC348976dB4A908c92B24433d4F6edA43" +PollInterval = "3s" + +[AutoClaim.BridgeServiceFinder.BridgeURLs] +1 = "http://aggkit-001:5577" +2 = "http://aggkit-002:5577" + +[[AutoClaim.Claimers]] +Enabled = true +ID = "autoclaim-002" +NetworkType = "EVM" +NetworkID = 2 +URLRPC = "http://l2-anvil-002:8545" +BridgeAddr = "0xC8cbEBf950B9Df44d987c8619f092beA980fF038" +PolicyName = "allow-all" +GasOffset = 100000 +WaitPeriod = "1s" +RetryAfter = "1s" +MaxRetries = 180 + +[AutoClaim.Claimers.Policy] +AllowMessageClaims = false +AllowedOrigins = [] +AllowedTokens = [] +ManualFallback = false +MaxGas = 500000 + +[AutoClaim.Claimers.EthTxManager] +FrequencyToMonitorTxs = "1s" +WaitTxToBeMined = "2s" +WaitReceiptMaxTime = "250ms" +WaitReceiptCheckInterval = "1s" +PrivateKeys = [ + {Method = "local", Path = "/etc/aggkit/aggoracle.keystore", Password = "pSnv6Dh5s9ahuzGzH9RoCDrKAMddaX3m"}, +] +ForcedGas = 0 +GasPriceMarginFactor = 1 +MaxGasPriceLimit = 0 +StoragePath = "/tmp/ethtxmanager-autoclaim.sqlite" +ReadPendingL1Txs = false +SafeStatusL1NumberOfBlocks = 0 +FinalizedStatusL1NumberOfBlocks = 0 +EstimateGasMaxRetries = 1 + +[AutoClaim.Claimers.EthTxManager.Etherman] +URL = "http://l2-anvil-002:8545" +MultiGasProvider = false +L1ChainID = 20202 +HTTPHeaders = {} + diff --git a/test/e2e/envs/anvil-2chains/config/002/aggoracle.keystore b/test/e2e/envs/anvil-2chains/config/002/aggoracle.keystore new file mode 100644 index 000000000..557813d07 --- /dev/null +++ b/test/e2e/envs/anvil-2chains/config/002/aggoracle.keystore @@ -0,0 +1,20 @@ +{ + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "249b2d77f2f8b3d7b8ef9b9b9c9c1630" + }, + "ciphertext": "11b1cc93393e73474663253775b2ddc88d48622d69850f7bb3f3290542e24a9c", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 8192, + "p": 1, + "r": 8, + "salt": "ce7cfd692a21f4b095282ff564aff6d971d537837b8601700eabce3f5716e04e" + }, + "mac": "33afcba5bb6343aa9f8e081540fc8797c491d75f8d09ba8b0b3f420ec5b9a50d" + }, + "id": "c7afce59-d87e-4d78-8975-2e14b76b9638", + "version": 3 +} diff --git a/test/e2e/envs/anvil-2chains/config/002/sequencer.keystore b/test/e2e/envs/anvil-2chains/config/002/sequencer.keystore new file mode 100644 index 000000000..1b7808d4a --- /dev/null +++ b/test/e2e/envs/anvil-2chains/config/002/sequencer.keystore @@ -0,0 +1,20 @@ +{ + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "38dad6dd09ad368977d1ad9f22a16e92" + }, + "ciphertext": "4f14eaed64efeec70863a09bca758270cdcdfcf5ed7ebd65d504e891c5ba7451", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 8192, + "p": 1, + "r": 8, + "salt": "f3f71b8b380a19cef8b0ddc57bb5318041db90150722ac2931e2d9702c9a8155" + }, + "mac": "43f945d2d9f9527568a114f07ccec39cb223b176f38e6e4378f1e779ca5ccb12" + }, + "id": "60e29f01-3a3a-4abf-8afd-857ca51c6792", + "version": 3 +} diff --git a/test/e2e/envs/anvil-2chains/config/002/sovereignadmin.keystore b/test/e2e/envs/anvil-2chains/config/002/sovereignadmin.keystore new file mode 100644 index 000000000..5ac6b4ee9 --- /dev/null +++ b/test/e2e/envs/anvil-2chains/config/002/sovereignadmin.keystore @@ -0,0 +1,20 @@ +{ + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "7c33c59b56cc88c5a8703afeff6b720b" + }, + "ciphertext": "9a48a6c3810bee9c8c085340a81f366bc76c82bad9f9ee74f71fec5987463c84", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 8192, + "p": 1, + "r": 8, + "salt": "14c1b9666123c384f92fb992ca253d4b9ddfe4840e96e1fd1fd1e7ea6d7dfbba" + }, + "mac": "1762a85728a35c17857e6e21d564f4397c02fb7cdedcb4e5300f544c7a7637a9" + }, + "id": "a677ead1-5159-4697-b8a0-6dc940f4b98f", + "version": 3 +} diff --git a/test/e2e/envs/anvil-2chains/config/aggkit-proxy/aggkit-proxy.toml b/test/e2e/envs/anvil-2chains/config/aggkit-proxy/aggkit-proxy.toml new file mode 100644 index 000000000..fad151569 --- /dev/null +++ b/test/e2e/envs/anvil-2chains/config/aggkit-proxy/aggkit-proxy.toml @@ -0,0 +1,105 @@ +# aggkit-proxy config (proxy + tracker components; --components=proxy,tracker +# -- see aggkit_proxy.star). +# +# Values chosen to mirror aggkit's own +# proxy/scripts/configuration_based_on_kurtosis.sh recipe for a local devnet +# (LatestBlock instead of FinalizedBlock -- FinalizedBlock lags too much on a +# local devnet; PollInterval 10s instead of the 30s upstream default; +# HealthCheckPath "/" since the aggkit bridge REST service serves its health +# check at the root path, not "/health"). [Tracker] mirrors the binary's own +# defaults (proxy/config/default.go) except RetentionPeriod, raised so a slow +# L2->L1 demo certificate stays inspectable through /tracker/v1. BridgeAddrs is +# intentionally left unset -- the tracker's own on-chain discovery covers this +# package's needs there (an absent entry still matches logs on the event +# signature alone). L1GlobalExitRootAddress has NO such fallback (confirmed in +# aggkit's bridgetracker/sources/ger.go): left unset it defaults to the zero +# address, which permanently stalls StepWaitingGERUpdate for every L1->L2 +# bridge, so it must be set explicitly below. As of aggkit v0.11.0-rc5 +# (bridgetracker/config.go's Config.Validate, PR agglayer/aggkit#1784) the +# proxy now fails fast at startup with a clear error instead of silently +# stalling if this resolves to the zero address -- this package's templating +# already supplies a real address, so the new check is expected to pass +# without any config change here. + +[Log] +Environment = "development" +Level = "info" +Outputs = ["stderr"] + +[L1RPC] +URL = "http://anvil-001:8545" +Mode = "basic" +RetryMode = "backoff" +MaxRetries = 5 +InitialBackoff = "2s" +MaxBackoff = "10s" +BackoffMultiplier = 2.0 + +[BridgeServiceFinder] +RollupManagerAddr = "0x6c6c009cC348976dB4A908c92B24433d4F6edA43" +BlockFinality = "LatestBlock" +PollInterval = "10s" +BlockChunkSize = 10000 +HealthCheckPath = "/" +HealthCheckTimeout = "5s" +RequireAllHealthyOnStart = false + +[BridgeServiceFinder.BridgeURLs] +0 = "http://aggkit-001:5577" +1 = "http://aggkit-001:5577" +2 = "http://aggkit-002:5577" + +[BridgeServiceFinder.RPCURLs] +0 = "http://anvil-001:8545" +1 = "http://l2-anvil-001:8545" +2 = "http://l2-anvil-002:8545" + +[REST] +Host = "0.0.0.0" +Port = 8080 +ReadTimeout = "5m" +WriteTimeout = "5m" +# As of aggkit v0.11.0-rc5, MaxRequestsPerIPAndSecond is unenforced in +# RESTConfig-backed sections (no middleware reads it) -- upstream's own +# default changed 10 -> 0 and docs/common_config.md's new RESTConfig section +# documents it as "unused; apply rate limiting at the infra layer" (see +# agglayer/aggkit#1783). Matching the upstream default +# here rather than pinning a value that implies in-process enforcement it +# doesn't have. Rate limiting for this service, if ever needed, belongs at +# haproxy or another fronting layer. +MaxRequestsPerIPAndSecond = 0 + +[Tracker] +# RetentionPeriod raised from the binary's 10m default so a slow L2->L1 demo +# certificate (agglayer settlement can take a while) stays queryable through +# /tracker/v1 instead of falling out of the registry mid-demo. +RetentionPeriod = "30m" +IdleTimeout = "30m" +RegisterResolveTimeout = "3s" +L1BlockFinality = "LatestBlock" +L2BlockFinality = "LatestBlock" +MaxTrackedBridges = 100000 +L1GlobalExitRootAddress = "0x1f7ad7caA53e35b4f0D138dC5CBF91aC108a2674" + +[Tracker.AgglayerClient] +Cached = true +[Tracker.AgglayerClient.ConfigurationCache] +TTL = "1s" +Capacity = 100 +SendCertificate = "forbidden" +GetCertificateHeader = "cached" +GetEpochConfiguration = "cached" +GetLatestPendingCertificateHeader = "cached" +GetNetworkInfo = "cached" + +[Tracker.AgglayerClient.GRPC] +URL = "http://agglayer:4443" +UseTLS = false +MinConnectTimeout = "5s" +RequestTimeout = "300s" + +[Tracker.AgglayerClient.GRPC.Retry] +InitialBackoff = "1s" +MaxBackoff = "10s" +BackoffMultiplier = 2.0 +MaxAttempts = 20 diff --git a/test/e2e/envs/anvil-2chains/config/agglayer/aggregator.keystore b/test/e2e/envs/anvil-2chains/config/agglayer/aggregator.keystore new file mode 100644 index 000000000..ae115f3ad --- /dev/null +++ b/test/e2e/envs/anvil-2chains/config/agglayer/aggregator.keystore @@ -0,0 +1,20 @@ +{ + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "fcd94bfef1b7e17213e1f9d7de2c74d6" + }, + "ciphertext": "9760449127d568224487accab22842553f57dea74d442751db1225e19293e598", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 8192, + "p": 1, + "r": 8, + "salt": "a0695165cb05219ed8c3d3987c1a1a683a0d71920ad4b4b4fabe21b55c487f4d" + }, + "mac": "a5439eabc23a0df3972ba55dcfe9db9b79d80f686e8fd7ae3ac95dff7aefd4d7" + }, + "id": "9eafcbe9-478c-4ffe-abab-9f9c4fa6b316", + "version": 3 +} diff --git a/test/e2e/envs/anvil-2chains/config/agglayer/config.toml b/test/e2e/envs/anvil-2chains/config/agglayer/config.toml new file mode 100644 index 000000000..36c9cab0f --- /dev/null +++ b/test/e2e/envs/anvil-2chains/config/agglayer/config.toml @@ -0,0 +1,130 @@ +debug-mode = true + + +# Only supported by fork 12+ +mock-verifier = true + + +[full-node-rpcs] +# OP Stack RPC (also used by the anvil L2, whose op_el_rpc_url is aliased to it) +1 = "http://l2-anvil-001:8545" +2 = "http://l2-anvil-002:8545" + +[proof-signers] +1 = "0x5b06837A43bdC3dD9F114558DAf4B26ed49842Ed" +2 = "0x5b06837A43bdC3dD9F114558DAf4B26ed49842Ed" +[prover.mock-prover] +proving-timeout = "5m" +proving-request-timeout = "300s" + +[rpc] +grpc-port = 4443 +readrpc-port = 4444 +admin-port = 4446 +host = "0.0.0.0" +request-timeout = 180 +# size is define in bytes e.g. 100 * 1024 * 1024 +# same for `max_response_body_size` +# default value is equal to 10MB +max-request-body-size = 104857600 + +[grpc] +# size is define in bytes e.g. 100 * 1024 * 1024 +# same for `max-encoding-message-size` +# default value is equal to 4MB +max-decoding-message-size = 104857600 + +# [outbound.rpc.settle] used to live here. It has had no effect on settlement +# since agglayer PR #1393 introduced the agglayer-settlement-service -- +# OutboundConfig is explicitly documented upstream as deprecated, and at +# v0.6.0-rc.7+ the binary itself warns on startup if this section is present +# (OutboundConfig::ignored_config_warning()). The real settlement-tx knobs +# live under [settlement.pessimistic-proof-tx-config] below. +[settlement.pessimistic-proof-tx-config] +# Number of L1 block confirmations required before a settlement tx's receipt +# is considered resolved. Upstream default is 12 (default_confirmations(), +# crates/agglayer-config/src/settlement_service.rs); this devnet had +# effectively been stuck at that default the whole time the dead +# [outbound.rpc.settle] confirmations=1 above was silently ignored. Lower +# values settle certificates faster at the cost of reorg safety -- fine on a +# throwaway anvil L1, not a production recommendation. +confirmations = 1 +# Finality level required for a settlement tx to be considered settled. +# Upstream enum is LatestBlock/SafeBlock/FinalizedBlock (SafeBlock is the +# upstream default); the latest->safe->finalized lag on an anvil L1 is +# l1_anvil_block_time * l1_anvil_slots_in_epoch seconds per step. The +# agglayer_settlement_policy input arg uses the lowercase +# latest/safe/finalized tokens; they're translated to the upstream wire +# enum names here since agglayer's SettlementPolicy has no +# #[serde(rename_all = "kebab-case")] (confirmed against +# crates/agglayer-config/tests/fixtures/settlement/*.toml at v0.6.0-rc.8, +# which all use the PascalCase variant names verbatim). +settlement-policy = "SafeBlock" +# retry-on-transient-failure / retry-on-not-included-on-l1 and +# gas-limit-multiplier-factor are intentionally left unset here (upstream +# defaults apply): the old [outbound.rpc.settle] max-retries/retry-interval +# had no 1:1 mapping onto the new schema's two separate retry-policy tables, +# and settlement-timeout = 1200 has no equivalent anywhere in the new schema +# at all (grepped SettlementTransactionConfig/SettlementServiceConfig at +# v0.6.0-rc.8 -- no timeout field exists upstream; that intent has no home). + +[log] +# level = "info" +level = "debug" # we want debug visibility for now +outputs = ["stderr"] +format = "pretty" + +[auth.local] +private-keys = [ + # First entry = pp-settlement signer (certificate/PP settlement). + { path = "/etc/agglayer/aggregator.keystore", password = "pSnv6Dh5s9ahuzGzH9RoCDrKAMddaX3m" }, +] + +[l1] +chain-id = 271828 +node-url = "http://anvil-001:8545" +ws-node-url = "ws://anvil-001:8545" +rollup-manager-contract = "0x6c6c009cC348976dB4A908c92B24433d4F6edA43" +polygon-zkevm-global-exit-root-v2-contract = "0x1f7ad7caA53e35b4f0D138dC5CBF91aC108a2674" +rpc-timeout = 45 + +[l2] +rpc-timeout = 45 + +[telemetry] +prometheus-addr = "0.0.0.0:9092" + +# https://github.com/orgs/agglayer/discussions/213 + +[rate-limiting] +send-tx = "unlimited" +# [rate-limiting.send-tx] +# max-per-interval = 1 +# time-interval = "15m" + +[rate-limiting.network] + +# Bookkeeping-only from v0.6.0-rc.2 onward: per-epoch certificate rate +# limiting was deleted in commit 41d7a17e (PR #1615). epoch-duration still +# parses and drives epoch bookkeeping/storage indexing, but it moves neither +# settlement nor submission timing under trigger_cert_mode: ASAP (this +# package's default) -- do not treat it as a latency knob. +[epoch.block-clock] +epoch-duration = 15 +genesis-block = 60 + +[shutdown] +runtime-timeout = 5 + +[certificate-orchestrator] +input-backpressure-buffer-size = 1000 + +[certificate-orchestrator.prover.sp1-local] + +[storage] +db-path = "/etc/agglayer/storage" + +[storage.backup] +path = "/etc/agglayer/backups" +state-max-backup-count = 100 +pending-max-backup-count = 100 diff --git a/test/e2e/envs/anvil-2chains/docker-compose.yml b/test/e2e/envs/anvil-2chains/docker-compose.yml new file mode 100644 index 000000000..b41bb6701 --- /dev/null +++ b/test/e2e/envs/anvil-2chains/docker-compose.yml @@ -0,0 +1,199 @@ +# anvil-2chains E2E environment +# +# Source: a kurtosis-cdk anvil devnet snapshot (flavor "anvil-aggkit", kurtosis-cdk commit +# fc160450b55e64332436f11c091c61130c64030f) -- see README.md in this directory for full +# provenance (image tags/digests, params files, regenerate procedure). +# +# Topology: one anvil L1 (anvil-001) + two independent anvil L2 sovereign chains +# (l2-anvil-001 / l2-anvil-002), each with its own aggkit instance (merged +# aggsender+aggoracle+bridge+autoclaim, no separate "-bridge" sidecar), settling through a +# single agglayer in PessimisticProof mode, fronted by a shared aggkit-proxy. +# +# Images: anvil-001/l2-anvil-001/l2-anvil-002/agglayer are the digest-pinned kurtosis-cdk +# snapshot images (state + captured devnet config baked in, but agglayer's config is +# overridden below via bind mount so it can be tuned locally). aggkit-001/aggkit-002/ +# aggkit-proxy-001 run this repo's own locally built image (`make build-docker`), NOT the +# snapshot's baked aggkit image, so the binary under test is always the one checked out here. +# +# Healthcheck note (do not simplify away): agglayer's healthcheck below is a genuine +# TCP-connect probe against its own gRPC port (bash's /dev/tcp builtin -- no curl/wget/nc +# needed, confirmed present in the bare agglayer image). aggkit-00X and aggkit-proxy-001 gate +# on `condition: service_healthy` against agglayer using that probe, and themselves carry NO +# healthcheck (their image is genuinely distroless -- no shell at all). A merely +# "process started" agglayer dependency (`condition: service_started`) loses a real race +# against aggkit's own claim-syncer autostart and permanently wedges aggsender at +# "starting_claim_syncer_stage" ("cannot set next required block to 0, it must be >= the +# first block in DB") -- reproduced 3/3 in this plan's own K5/K5c evidence. Do not revert to +# aggkit's own op-pp-2chains precedent here (`test -f /proc/1/cmdline`): that check passes +# within milliseconds, before agglayer's gRPC listener actually binds, and does not close the +# race. + +services: + anvil-001: + image: ghcr.io/0xpolygon/kurtosis-cdk-snapshot-anvil-001@sha256:006932fc49ce501c8d6f8c3f4ac3b5873ec14b59b101b4f4e9db02b169e6c0c9 + hostname: anvil-001 + ports: + - "13545:8545" # L1 JSON-RPC + restart: unless-stopped + healthcheck: + test: ["CMD", "/bin/sh", "/snapshot/healthcheck.sh"] + interval: 3s + timeout: 10s + retries: 40 + start_period: 5s + + l2-anvil-001: + image: ghcr.io/0xpolygon/kurtosis-cdk-snapshot-l2-anvil-001@sha256:e9bbeb7f9a76a4ea725f194059c6b23d49c65e89a8a00241d6df3b687c8ccbb8 + hostname: l2-anvil-001 + ports: + - "14545:8545" # L2-001 (chain 20201) JSON-RPC + restart: unless-stopped + healthcheck: + test: ["CMD", "/bin/sh", "/snapshot/healthcheck.sh"] + interval: 3s + timeout: 10s + retries: 40 + start_period: 5s + + l2-anvil-002: + image: ghcr.io/0xpolygon/kurtosis-cdk-snapshot-l2-anvil-002@sha256:3555d50518f6f72d5811edd759293ba205ac192c04192695afc046c2cb595ef0 + hostname: l2-anvil-002 + ports: + - "15545:8545" # L2-002 (chain 20202) JSON-RPC + restart: unless-stopped + healthcheck: + test: ["CMD", "/bin/sh", "/snapshot/healthcheck.sh"] + interval: 3s + timeout: 10s + retries: 40 + start_period: 5s + + agglayer: + image: ghcr.io/0xpolygon/kurtosis-cdk-snapshot-agglayer@sha256:5a47d3778657ba618ff7dfc99dfd55a3863097d5fff4f960a0740d4d0ae80073 + hostname: agglayer + entrypoint: ["/usr/local/bin/agglayer"] + command: ["run", "--cfg", "/etc/agglayer/config.toml"] + environment: + - RUST_BACKTRACE=1 + volumes: + - ./config/agglayer/config.toml:/etc/agglayer/config.toml:ro + - ./config/agglayer/aggregator.keystore:/etc/agglayer/aggregator.keystore:ro + ports: + - "13443:4443" # gRPC + - "13444:4444" # read RPC + - "13446:4446" # admin API + - "13092:9092" # prometheus + depends_on: + anvil-001: + condition: service_healthy + l2-anvil-001: + condition: service_healthy + l2-anvil-002: + condition: service_healthy + restart: unless-stopped + healthcheck: + # Genuine TCP-connect probe against agglayer's own gRPC port -- see the file header + # comment for why this matters (aggkit-00X's aggsender races agglayer's async + # gRPC-listener bind against its own local claim-syncer autostart on startup). + test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/127.0.0.1/4443"] + interval: 2s + timeout: 3s + retries: 30 + start_period: 10s + + aggkit-001: + image: aggkit:local + hostname: aggkit-001 + # Matches op-pp/docker-compose.yml's aggkit-001: since /tmp is bind-mounted to the host + # below (for GetAggsenderDBPath()/GetAggkitDataDir()), run as the host UID/GID (injected by + # newDockerComposeCmd) so files the container writes to /tmp stay host-writable/removable + # instead of landing owned by the container's own root (which cleanAggkitDataDir then can't + # remove on the next run under non-rootless Docker). + user: "${UID:-1000}:${GID:-1000}" + entrypoint: ["/usr/local/bin/aggkit"] + command: + - "run" + - "--cfg=/etc/aggkit/config.toml" + - "--components=aggsender,aggoracle,bridge,autoclaim" + volumes: + - ./config/001/aggkit-config.toml:/etc/aggkit/config.toml:ro + - ./config/001/sequencer.keystore:/etc/aggkit/sequencer.keystore:ro + - ./config/001/aggoracle.keystore:/etc/aggkit/aggoracle.keystore:ro + - ./config/001/sovereignadmin.keystore:/etc/aggkit/sovereignadmin.keystore:ro + - ./aggkit-001-data:/tmp + ports: + - "14576:5576" # JSON-RPC (debug) + - "14577:5577" # bridge REST API + depends_on: + anvil-001: + condition: service_healthy + l2-anvil-001: + condition: service_healthy + agglayer: + condition: service_healthy + restart: unless-stopped + environment: + - RUST_BACKTRACE=1 + # aggkit-001 carries NO healthcheck of its own -- its image is genuinely distroless + # (no shell at all). Its own depends_on above on agglayer is `service_healthy` (see the + # file header comment); dependents of aggkit-001 (aggkit-proxy-001, below) still use + # `condition: service_started`, since aggkit-001 has no healthcheck to gate on. + + aggkit-002: + image: aggkit:local + hostname: aggkit-002 + entrypoint: ["/usr/local/bin/aggkit"] + command: + - "run" + - "--cfg=/etc/aggkit/config.toml" + - "--components=aggsender,aggoracle,bridge,autoclaim" + volumes: + - ./config/002/aggkit-config.toml:/etc/aggkit/config.toml:ro + - ./config/002/sequencer.keystore:/etc/aggkit/sequencer.keystore:ro + - ./config/002/aggoracle.keystore:/etc/aggkit/aggoracle.keystore:ro + - ./config/002/sovereignadmin.keystore:/etc/aggkit/sovereignadmin.keystore:ro + ports: + - "15576:5576" # JSON-RPC (debug) + - "15577:5577" # bridge REST API + depends_on: + anvil-001: + condition: service_healthy + l2-anvil-002: + condition: service_healthy + agglayer: + condition: service_healthy + restart: unless-stopped + environment: + - RUST_BACKTRACE=1 + # Same "no healthcheck, distroless image" note as aggkit-001 above. + + aggkit-proxy-001: + image: aggkit:local + hostname: aggkit-proxy-001 + entrypoint: ["/usr/local/bin/aggkit-proxy"] + command: + - "run" + - "--cfg=/etc/aggkit-proxy/config.toml" + - "--components=proxy,tracker" + volumes: + - ./config/aggkit-proxy/aggkit-proxy.toml:/etc/aggkit-proxy/config.toml:ro + ports: + - "15601:8080" # bridge + tracker REST + depends_on: + agglayer: + condition: service_healthy + aggkit-001: + condition: service_started + aggkit-002: + condition: service_started + restart: unless-stopped + environment: + - RUST_BACKTRACE=1 + # Same "no healthcheck, distroless image" note as aggkit-00X above. Its own depends_on + # on agglayer is `service_healthy`; its depends_on on aggkit-00X stays `service_started` + # since aggkit-00X has no healthcheck of its own. + +# Anvil family (anvil-001/l2-anvil-001/l2-anvil-002) keeps its baked state -- no bind mounts, +# never overridden: swapping config there would lose the captured devnet state. agglayer/ +# aggkit-00X/aggkit-proxy-001 all bind-mount their config from ./config/ (read-only) so A3 can +# tune them without rebuilding an image. diff --git a/test/e2e/envs/anvil-2chains/summary.json b/test/e2e/envs/anvil-2chains/summary.json new file mode 100644 index 000000000..c69e015a7 --- /dev/null +++ b/test/e2e/envs/anvil-2chains/summary.json @@ -0,0 +1,269 @@ +{ + "snapshot_name": "anvil-2chains (kurtosis-cdk fc160450, K8 publish run 31787941750)", + "enclave": "cdk", + "created_at": "2026-08-14T09:33:46Z", + "networks": { + "l1": { + "chain_id": "271828", + "contracts": { + "rollup_manager": "0x6c6c009cC348976dB4A908c92B24433d4F6edA43", + "global_exit_root_v2": "0x1f7ad7caA53e35b4f0D138dC5CBF91aC108a2674", + "bridge": "0xC8cbEBf950B9Df44d987c8619f092beA980fF038", + "pol_token": "0xEdE9cf798E0fE25D35469493f43E88FeA4a5da0E" + }, + "services": { + "geth": { + "http_rpc": { + "internal": "http://anvil-001:8545", + "external": "http://localhost:13545" + } + } + }, + "accounts": [ + { + "address": "0x8943545177806ED17B9F23F0a21ee5948eCaa776", + "private_key": "0xbcdf20249abf0ed6d944c0288fad489e33f66b3960d9e6229c1cd214ed3bbe31", + "description": "L1 pre-funded account" + }, + { + "address": "0xE25583099BA105D9ec0A67f5Ae86D90e50036425", + "private_key": "0x39725efee3fb28614de3bacaffe4cc4bd8c436257e2c8bb887c4b5c4be45e76d", + "description": "L1 pre-funded account" + }, + { + "address": "0x614561D2d143621E126e87831AEF287678B442b8", + "private_key": "0x53321db7c1e331d93a11a41d16f004d7ff63972ec8ec7c25db329728ceeb1710", + "description": "L1 pre-funded account" + }, + { + "address": "0xf93Ee4Cf8c6c40b329b0c0626F28333c132CF241", + "private_key": "0xab63b23eb7941c1251757e24b3d2350d2bc05c3c388d06f8fe6feafefb1e8c70", + "description": "L1 pre-funded account" + }, + { + "address": "0x802dCbE1B1A97554B4F50DB5119E37E8e7336417", + "private_key": "0x5d2344259f42259f82d2c140aa66102ba89b57b4883ee441a8b312622bd42491", + "description": "L1 pre-funded account" + }, + { + "address": "0xAe95d8DA9244C37CaC0a3e16BA966a8e852Bb6D6", + "private_key": "0x27515f805127bebad2fb9b183508bdacb8c763da16f54e0678b16e8f28ef3fff", + "description": "L1 pre-funded account" + }, + { + "address": "0x2c57d1CFC6d5f8E4182a56b4cf75421472eBAEa4", + "private_key": "0x7ff1a4c1d57e5e784d327c4c7651e952350bc271f156afb3d00d20f5ef924856", + "description": "L1 pre-funded account" + }, + { + "address": "0x741bFE4802cE1C4b5b00F9Df2F5f179A1C89171A", + "private_key": "0x3a91003acaf4c21b3953d94fa4a6db694fa69e5242b2e37be05dd82761058899", + "description": "L1 pre-funded account" + }, + { + "address": "0xc3913d4D8bAb4914328651C2EAE817C8b78E1f4c", + "private_key": "0xbb1d0f125b4fb2bb173c318cdead45468474ca71474e2247776b2b4c0fa2d3f5", + "description": "L1 pre-funded account" + }, + { + "address": "0x65D08a056c17Ae13370565B04cF77D2AfA1cB9FA", + "private_key": "0x850643a0224065ecce3882673c21f56bcf6eef86274cc21cadff15930b59fc8c", + "description": "L1 pre-funded account" + } + ] + }, + "agglayer": { + "services": { + "grpc_rpc": { + "internal": "http://agglayer:4443", + "external": "http://localhost:13443" + }, + "read_rpc": { + "internal": "http://agglayer:4444", + "external": "http://localhost:13444" + }, + "admin_api": { + "internal": "http://agglayer:4446", + "external": "http://localhost:13446" + }, + "metrics": { + "internal": "http://agglayer:9092/metrics", + "external": "http://localhost:13092/metrics" + } + } + }, + "l2_networks": { + "001": { + "chain_id": "20201", + "contracts": { + "l1_bridge": "0xC8cbEBf950B9Df44d987c8619f092beA980fF038", + "l2_bridge": "0xC8cbEBf950B9Df44d987c8619f092beA980fF038", + "rollup_manager": "0x6c6c009cC348976dB4A908c92B24433d4F6edA43", + "global_exit_root": "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa", + "sovereign_rollup_l1": "0x414e9E227e4b589aF92200508aF5399576530E4e" + }, + "services": { + "op-geth": { + "http_rpc": { + "internal": "http://l2-anvil-001:8545", + "external": "http://localhost:14545" + } + }, + "aggkit": { + "rpc": { + "internal": "http://aggkit-001:5576", + "external": "http://localhost:14576" + }, + "rest_api": { + "internal": "http://aggkit-001:5577", + "external": "http://localhost:14577" + } + } + }, + "accounts": [ + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "private_key": "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "description": "L2 network 001 pre-funded account" + }, + { + "address": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "private_key": "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", + "description": "L2 network 001 pre-funded account" + }, + { + "address": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + "private_key": "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a", + "description": "L2 network 001 pre-funded account" + }, + { + "address": "0x90F79bf6EB2c4f870365E785982E1f101E93b906", + "private_key": "0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6", + "description": "L2 network 001 pre-funded account" + }, + { + "address": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + "private_key": "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a", + "description": "L2 network 001 pre-funded account" + }, + { + "address": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "private_key": "0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba", + "description": "L2 network 001 pre-funded account" + }, + { + "address": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", + "private_key": "0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e", + "description": "L2 network 001 pre-funded account" + }, + { + "address": "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", + "private_key": "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356", + "description": "L2 network 001 pre-funded account" + }, + { + "address": "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", + "private_key": "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97", + "description": "L2 network 001 pre-funded account" + }, + { + "address": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", + "private_key": "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6", + "description": "L2 network 001 pre-funded account" + } + ] + }, + "002": { + "chain_id": "20202", + "contracts": { + "l1_bridge": "0xC8cbEBf950B9Df44d987c8619f092beA980fF038", + "l2_bridge": "0xC8cbEBf950B9Df44d987c8619f092beA980fF038", + "rollup_manager": "0x6c6c009cC348976dB4A908c92B24433d4F6edA43", + "global_exit_root": "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa", + "sovereign_rollup_l1": "0x5D1A491A416feEbf8C123A558ec28A239960bd0E" + }, + "services": { + "op-geth": { + "http_rpc": { + "internal": "http://l2-anvil-002:8545", + "external": "http://localhost:15545" + } + }, + "aggkit": { + "rpc": { + "internal": "http://aggkit-002:5576", + "external": "http://localhost:15576" + }, + "rest_api": { + "internal": "http://aggkit-002:5577", + "external": "http://localhost:15577" + } + } + }, + "accounts": [ + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "private_key": "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "description": "L2 network 002 pre-funded account" + }, + { + "address": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "private_key": "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", + "description": "L2 network 002 pre-funded account" + }, + { + "address": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + "private_key": "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a", + "description": "L2 network 002 pre-funded account" + }, + { + "address": "0x90F79bf6EB2c4f870365E785982E1f101E93b906", + "private_key": "0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6", + "description": "L2 network 002 pre-funded account" + }, + { + "address": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + "private_key": "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a", + "description": "L2 network 002 pre-funded account" + }, + { + "address": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "private_key": "0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba", + "description": "L2 network 002 pre-funded account" + }, + { + "address": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", + "private_key": "0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e", + "description": "L2 network 002 pre-funded account" + }, + { + "address": "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", + "private_key": "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356", + "description": "L2 network 002 pre-funded account" + }, + { + "address": "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", + "private_key": "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97", + "description": "L2 network 002 pre-funded account" + }, + { + "address": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", + "private_key": "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6", + "description": "L2 network 002 pre-funded account" + } + ] + } + } + }, + "test_accounts": { + "l1_mnemonic": "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete", + "l2_mnemonic": "test test test test test test test test test test test junk", + "note": "Pre-funded test accounts are derived from these mnemonics -- byte-identical to op-pp-2chains's own mnemonics, so every contract address above matches op-pp-2chains's address-for-address. Use with cast: cast wallet address --mnemonic \"\" --mnemonic-index <0-N>" + }, + "notes": { + "json_labels": "The 'geth' (L1) and 'op-geth' (L2) service labels above are aggkit's schema field names, not the running service technology: every execution client in this env is anvil (Foundry v1.5.1), not go-ethereum. aggkit's summaryJSON struct offers no third label, so these names are reused verbatim -- see README.md.", + "accounts": "Only mnemonic-derived pre-funded accounts with a private_key are included (10 per network, funded_on-filtered from the kurtosis-cdk bundle's accounts.funded list). Precompile/predeploy addresses are excluded.", + "services": "Internal URLs are for use within the Docker network (docker-compose service DNS names). External URLs are for access from the host machine.", + "provenance": "Full provenance (kurtosis-cdk commit, params files, image tags/digests, regenerate procedure) is recorded in this directory's README.md, not here -- this file only carries what test/e2e/envs/loader.go and checks.go actually read." + } +} diff --git a/test/e2e/envs/checks.go b/test/e2e/envs/checks.go index d76f124e0..11246f157 100644 --- a/test/e2e/envs/checks.go +++ b/test/e2e/envs/checks.go @@ -66,9 +66,11 @@ func (e *Env) checkConfiguration() error { return fmt.Errorf("L1 Transactor is nil") } - // Expected L2A chain ID depends on the env: op-pp uses 2151908, op-pp-2chains uses 20201. + // Expected L2A chain ID depends on the env: op-pp uses 2151908, op-pp-2chains and + // anvil-2chains both use 20201. Not derived from summary.json: this check exists to catch a + // stale/wrong summary, so comparing a parsed value against itself would be circular. wantL2AChainID := "2151908" - if e.envName == EnvOpPP2Chains { + if e.envName == EnvOpPP2Chains || e.envName == EnvAnvil2Chains { wantL2AChainID = "20201" } if err := checkL2Configured(e.L2, wantL2AChainID, "L2"); err != nil { diff --git a/test/e2e/envs/loader.go b/test/e2e/envs/loader.go index 0dfaecf5d..12c413a3c 100644 --- a/test/e2e/envs/loader.go +++ b/test/e2e/envs/loader.go @@ -37,6 +37,10 @@ const ( // EnvOpPP2Chains is a testing env that has two OP-PP L2 networks deployed (L2A + L2B) EnvOpPP2Chains ENVName = "op-pp-2chains" + // EnvAnvil2Chains is a testing env that has two anvil-backed L2 networks deployed (L2A + L2B), + // settling against an anvil L1, sourced from a kurtosis-cdk anvil snapshot bundle. + EnvAnvil2Chains ENVName = "anvil-2chains" + // l2NetworkKeyA is the summary.json key of the primary L2 network (L2A). l2NetworkKeyA = "001" // l2NetworkKeyB is the summary.json key of the secondary L2 network (L2B), present @@ -310,9 +314,11 @@ func LoadEnv(ctx context.Context, envName ENVName) (*Env, error) { return nil, fmt.Errorf("load L2 network %s: %w", l2NetworkKeyA, err) } - // Load secondary L2 network (L2B, key "002") only for multi-chain envs. + // Load secondary L2 network (L2B, key "002") only for multi-chain envs, detected by the + // presence of the "002" key in summary.json rather than by env name. This generalizes to any + // multi-chain env (present and future) without adding a name to a list. var l2B *L2Config - if envName == EnvOpPP2Chains { + if _, ok := summary.Networks.L2Networks[l2NetworkKeyB]; ok { l2B, err = loadL2Config(ctx, summary, l2NetworkKeyB) if err != nil { return nil, fmt.Errorf("load L2 network %s: %w", l2NetworkKeyB, err) @@ -693,6 +699,27 @@ func (e *Env) DockerComposeLogs(ctx context.Context, args ...string) ([]byte, er return out, nil } +// ComposeServices returns every service name defined in this environment's docker-compose.yml, by +// shelling out to "docker compose config --services". This is used instead of a hardcoded per-env +// service list (which cannot stay in sync across envs, and summary.json's schema has no key for +// services like beacon/validator/op-node) so log collection covers every service in any env, +// present or future, with zero per-env code. +func (e *Env) ComposeServices(ctx context.Context) ([]string, error) { + cmd := newDockerComposeCmd(ctx, e.EnvDir, "config", "--services") + out, err := cmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("docker compose config --services: %w\nOutput:\n%s", err, string(out)) + } + var services []string + for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") { + line = strings.TrimSpace(line) + if line != "" { + services = append(services, line) + } + } + return services, nil +} + // cleanAggkitDataDir removes the aggkit data directory and recreates it with /tmp-like // permissions so bind-mounts remain writable even under rootless Docker, where the // mount may appear as root:root inside the container. diff --git a/test/e2e/testmain_test.go b/test/e2e/testmain_test.go index b712f7aab..dbe8155a1 100644 --- a/test/e2e/testmain_test.go +++ b/test/e2e/testmain_test.go @@ -15,23 +15,25 @@ import ( var testEnv *envs.Env -// containerLogServices lists the docker compose services whose logs are dumped to -// test/e2e/.log when a test run fails, so the CI artifact-upload step (which globs -// test/e2e/*.log) actually captures something to debug the failure with. -var containerLogServices = []string{ - "geth", "beacon", "validator", "op-geth-001", "op-node-001", "aggkit-001", "agglayer", -} - -// dumpContainerLogs writes "docker compose logs" output for each service in containerLogServices to -// test/e2e/.log, relative to the test binary's working directory (test/e2e when run via -// `go test ./test/e2e/...`, matching the CI artifact glob). Services absent from the loaded env -// (e.g. an env without op-node-001) simply error and are skipped; failures here are logged, not -// fatal, since this only runs to aid debugging an already-failed run. +// dumpContainerLogs writes "docker compose logs" output for every service in the loaded env's +// docker-compose.yml (discovered via Env.ComposeServices, i.e. "docker compose config --services") +// to test/e2e/.log, relative to the test binary's working directory (test/e2e when run +// via `go test ./test/e2e/...`, matching the CI artifact glob). This covers every service in any +// env, present or future, with zero per-env code -- summary.json's schema has no key for services +// like beacon/validator/op-node, so a hardcoded list (or a summary.json-derived one) would always +// under-cover. Failures here are logged, not fatal, since this only runs to aid debugging an +// already-failed run. func dumpContainerLogs(env *envs.Env) { ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) defer cancel() - for _, service := range containerLogServices { + services, err := env.ComposeServices(ctx) + if err != nil { + log.Infof("[TEARDOWN] failed to list compose services: %v", err) + return + } + + for _, service := range services { out, err := env.DockerComposeLogs(ctx, "--no-log-prefix", service) if err != nil { log.Infof("[TEARDOWN] failed to fetch logs for service %q: %v", service, err) From 6c7d76f9895519254bf69430259c0875c419b40f Mon Sep 17 00:00:00 2001 From: Arnau Bennassar Date: Fri, 14 Aug 2026 11:31:28 +0000 Subject: [PATCH 2/4] tune(e2e): anvil-2chains settlement/GER-poll latency knobs, measured A3: sweep and measure config knobs for test/e2e/envs/anvil-2chains against the TestMain-only wall-clock (AGGKIT_E2E_ENV=anvil-2chains make test-e2e TEST_RUN='TestZZZNoSuchTest'), 27 timed runs across 7 configurations. - config/agglayer/config.toml: add [settlement.pessimistic-proof-tx-config.retry-on-not-included-on-l1] initial-interval = "5s" (was unset/60s default). Confirmed via container log (sleep_duration dropped from ~62s to ~9-14s) and via `agglayer validate-config` dump. Real isolated effect on this measured command is smaller than the design doc expected (~6s), because the measured post-test bridge check reads state through aggkit's own L1InfoTreeSync (watches L1 directly), not through agglayer's internal settlement-confirmation bookkeeping -- kept anyway since it is free (no reorg-safety cost) and matters for any future test that reads agglayer's own certificate-status API instead. - config/{001,002}/aggkit-config.toml: AggOracle.WaitPeriodNextGER and AggOracle.EVMSender.WaitPeriodMonitorTx lowered 10s -> 1s. This, not the settlement retry interval, is the dominant lever found for this env: it collapsed a reproducible ~36s/~43s bimodal split (poll-cycle alignment noise on the L1->L2 GER-injection wait) into a tight ~35-37s cluster. WaitPeriodMonitorTx=1s restores config/default.go's own upstream default; WaitPeriodNextGER=1s is a deliberate deviation below its 10s default, justified since it is a pure poll-frequency knob with no finality/ reorg-safety semantics. Measured median: 43.18s (pre-tuning) -> 35.58s (chosen config), verified green 3 consecutive times from clean docker in two independent batches. settlement-policy=LatestBlock was tested and NOT shipped (no measured benefit over SafeBlock at this scale). The MinimumNewCertificateInterval default discrepancy (aggsender/config/config.go's dead 1h Go-struct fallback vs config/default.go's 5m vs the env comment's incorrect 5s claim) is resolved in writing with evidence: the real effective default is 5m, confirmed via `aggkit run --save-config-path` dump (Duration = 300000000000ns), and does not gate this measured command since only one certificate per network is ever required by it. Full measurement table, raw run logs, and effective-config dumps in plans/snapshot-v2-aggkit-e2e/A3-evidence/. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015Q5xUjjWCiNQdm7cmeWYrs --- .../config/001/aggkit-config.toml | 23 ++++++++++++++--- .../config/002/aggkit-config.toml | 23 ++++++++++++++--- .../anvil-2chains/config/agglayer/config.toml | 25 +++++++++++++------ 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/test/e2e/envs/anvil-2chains/config/001/aggkit-config.toml b/test/e2e/envs/anvil-2chains/config/001/aggkit-config.toml index d276e4ece..cb53620cb 100644 --- a/test/e2e/envs/anvil-2chains/config/001/aggkit-config.toml +++ b/test/e2e/envs/anvil-2chains/config/001/aggkit-config.toml @@ -333,8 +333,18 @@ MaxAttempts = 20 # ------------------------------------------------------------------------------ # Duration expressed in units: [ns, us, ms, s, m, h, d]" +# A3 measurement (plans/snapshot-v2-aggkit-e2e/A3-evidence/): this poll +# period is the single largest lever found for this env's measured +# TestMain post-test bridge check. At the prior 10s value (which matches +# this repo's own config/default.go template default -- not a regression, +# just conservative), the L1->L2 GER-injection wait showed a bimodal +# ~36s/~43s split across otherwise-identical runs depending on poll-cycle +# alignment; at 1s it collapsed to a tight ~35-37s cluster (5 consecutive +# runs, spread <2s). Lowering only changes how often AggOracle checks L1 +# for a new GER to inject -- it does not touch any finality/reorg-safety +# knob, so this is not a raciness trade-off. # ------------------------------------------------------------------------------ -WaitPeriodNextGER = "10s" +WaitPeriodNextGER = "1s" # ------------------------------------------------------------------------------ # @@ -359,8 +369,15 @@ GlobalExitRootL2 = "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa" # ------------------------------------------------------------------------------ # Duration expressed in units: [ns, us, ms, s, m, h, d]" -# ------------------------------------------------------------------------------ -WaitPeriodMonitorTx = "10s" +# A3 measurement: restores this repo's own config/default.go template +# default (1s) -- the prior 10s value here was 10x slower than upstream's +# own default, not an intentional devnet choice. Measured with +# WaitPeriodNextGER alone at 1s (WaitPeriodMonitorTx still 10s) this did +# NOT collapse the bimodal split on its own (median stayed ~43s); it is +# kept at the default anyway since it costs nothing and is the correct +# baseline value. +# ------------------------------------------------------------------------------ +WaitPeriodMonitorTx = "1s" # ------------------------------------------------------------------------------ # diff --git a/test/e2e/envs/anvil-2chains/config/002/aggkit-config.toml b/test/e2e/envs/anvil-2chains/config/002/aggkit-config.toml index dc4d389a2..6d264035d 100644 --- a/test/e2e/envs/anvil-2chains/config/002/aggkit-config.toml +++ b/test/e2e/envs/anvil-2chains/config/002/aggkit-config.toml @@ -333,8 +333,18 @@ MaxAttempts = 20 # ------------------------------------------------------------------------------ # Duration expressed in units: [ns, us, ms, s, m, h, d]" +# A3 measurement (plans/snapshot-v2-aggkit-e2e/A3-evidence/): this poll +# period is the single largest lever found for this env's measured +# TestMain post-test bridge check. At the prior 10s value (which matches +# this repo's own config/default.go template default -- not a regression, +# just conservative), the L1->L2 GER-injection wait showed a bimodal +# ~36s/~43s split across otherwise-identical runs depending on poll-cycle +# alignment; at 1s it collapsed to a tight ~35-37s cluster (5 consecutive +# runs, spread <2s). Lowering only changes how often AggOracle checks L1 +# for a new GER to inject -- it does not touch any finality/reorg-safety +# knob, so this is not a raciness trade-off. # ------------------------------------------------------------------------------ -WaitPeriodNextGER = "10s" +WaitPeriodNextGER = "1s" # ------------------------------------------------------------------------------ # @@ -359,8 +369,15 @@ GlobalExitRootL2 = "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa" # ------------------------------------------------------------------------------ # Duration expressed in units: [ns, us, ms, s, m, h, d]" -# ------------------------------------------------------------------------------ -WaitPeriodMonitorTx = "10s" +# A3 measurement: restores this repo's own config/default.go template +# default (1s) -- the prior 10s value here was 10x slower than upstream's +# own default, not an intentional devnet choice. Measured with +# WaitPeriodNextGER alone at 1s (WaitPeriodMonitorTx still 10s) this did +# NOT collapse the bimodal split on its own (median stayed ~43s); it is +# kept at the default anyway since it costs nothing and is the correct +# baseline value. +# ------------------------------------------------------------------------------ +WaitPeriodMonitorTx = "1s" # ------------------------------------------------------------------------------ # diff --git a/test/e2e/envs/anvil-2chains/config/agglayer/config.toml b/test/e2e/envs/anvil-2chains/config/agglayer/config.toml index 36c9cab0f..0d5837b78 100644 --- a/test/e2e/envs/anvil-2chains/config/agglayer/config.toml +++ b/test/e2e/envs/anvil-2chains/config/agglayer/config.toml @@ -60,13 +60,24 @@ confirmations = 1 # crates/agglayer-config/tests/fixtures/settlement/*.toml at v0.6.0-rc.8, # which all use the PascalCase variant names verbatim). settlement-policy = "SafeBlock" -# retry-on-transient-failure / retry-on-not-included-on-l1 and -# gas-limit-multiplier-factor are intentionally left unset here (upstream -# defaults apply): the old [outbound.rpc.settle] max-retries/retry-interval -# had no 1:1 mapping onto the new schema's two separate retry-policy tables, -# and settlement-timeout = 1200 has no equivalent anywhere in the new schema -# at all (grepped SettlementTransactionConfig/SettlementServiceConfig at -# v0.6.0-rc.8 -- no timeout field exists upstream; that intent has no home). +# retry-on-transient-failure and gas-limit-multiplier-factor are +# intentionally left unset here (upstream defaults apply): the old +# [outbound.rpc.settle] max-retries/retry-interval had no 1:1 mapping onto +# the new schema's two separate retry-policy tables, and settlement-timeout +# = 1200 has no equivalent anywhere in the new schema at all (grepped +# SettlementTransactionConfig/SettlementServiceConfig at v0.6.0-rc.8 -- no +# timeout field exists upstream; that intent has no home). +# +# A3 measurement (plans/snapshot-v2-aggkit-e2e/A3-evidence/): the first +# receipt check for a settlement tx fires immediately after broadcast (0 +# confirmations elapsed yet), returns NotIncludedYet, and upstream's default +# retry-on-not-included-on-l1.initial-interval (1m, unset here previously) +# gates the *next* attempt -- confirmed via the agglayer container log +# ("Transient error while executing retryable callback, error: +# NotIncludedYet, retry_attempt: 1, sleep_duration: 61.936s") on this exact +# env. Lowered here since the anvil L1 reaches 1 confirmation within ~1s. +[settlement.pessimistic-proof-tx-config.retry-on-not-included-on-l1] +initial-interval = "5s" [log] # level = "info" From 507133baf7b23d6ecfa01f575b054806493ccd26 Mon Sep 17 00:00:00 2001 From: Arnau Bennassar Date: Fri, 14 Aug 2026 11:50:55 +0000 Subject: [PATCH 3/4] ci(e2e): run anvil snapshot smoke in CI --- .github/workflows/test-go-e2e.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test-go-e2e.yml b/.github/workflows/test-go-e2e.yml index 0a0bed5e0..cc92bbfa4 100644 --- a/.github/workflows/test-go-e2e.yml +++ b/.github/workflows/test-go-e2e.yml @@ -51,14 +51,19 @@ jobs: # This commit generated the op-pp-2chains env snapshot docker compose pull geth beacon validator agglayer \ op-reth-001 op-node-001 op-reth-002 op-node-002 + cd ../anvil-2chains + # Digest-pinned kurtosis-cdk anvil snapshot images. The aggkit services + # deliberately use the locally built aggkit:local image and are excluded. + docker compose pull anvil-001 l2-anvil-001 l2-anvil-002 agglayer - name: Save pulled Docker images run: | - imgs="" - for d in op-pp op-pp-2chains; do - imgs="$imgs $(cd test/e2e/envs/$d && docker compose config --images | grep -v 'aggkit:local')" - done - docker save $(echo "$imgs" | tr ' ' '\n' | sort -u) -o /tmp/docker-images.tar + mapfile -t imgs < <( + for d in op-pp op-pp-2chains anvil-2chains; do + (cd test/e2e/envs/$d && docker compose config --images | grep -v 'aggkit:local') + done | sort -u + ) + docker save "${imgs[@]}" -o /tmp/docker-images.tar - name: Upload Docker images artifact uses: actions/upload-artifact@v4 @@ -70,7 +75,9 @@ jobs: test-go-e2e: name: Run Go E2E Tests (${{ matrix.env }} / ${{ matrix.group }}) runs-on: ubuntu-latest - timeout-minutes: 60 + # The anvil-2chains TestMain-only run measured 35.58s median locally (A3). + # 15 minutes leaves real CI setup/image-load headroom without inheriting 60m. + timeout-minutes: 15 needs: [build-docker-image, pull-docker-images] strategy: fail-fast: false @@ -99,6 +106,11 @@ jobs: - env: op-pp-2chains group: default run: TestBridgeL2ToL2|TestAutoClaimL2ToL2AllowAll|TestBridgeTrackerL1ToL2 + # TestMain-only smoke check: the intentionally non-matching regex runs + # environment startup and the post-test bridge health-check without tests. + - env: anvil-2chains + group: default + run: ^TestZZZNoSuchTest$ steps: - name: Checkout code uses: actions/checkout@v5 From 0a6481ccdb1cafeb0be1028bf50b01866137d6ad Mon Sep 17 00:00:00 2001 From: Arnau Bennassar Date: Fri, 14 Aug 2026 12:16:19 +0000 Subject: [PATCH 4/4] ci(e2e): isolate anvil smoke timeout --- .github/workflows/test-go-e2e.yml | 62 +++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-go-e2e.yml b/.github/workflows/test-go-e2e.yml index cc92bbfa4..881a55173 100644 --- a/.github/workflows/test-go-e2e.yml +++ b/.github/workflows/test-go-e2e.yml @@ -75,9 +75,7 @@ jobs: test-go-e2e: name: Run Go E2E Tests (${{ matrix.env }} / ${{ matrix.group }}) runs-on: ubuntu-latest - # The anvil-2chains TestMain-only run measured 35.58s median locally (A3). - # 15 minutes leaves real CI setup/image-load headroom without inheriting 60m. - timeout-minutes: 15 + timeout-minutes: 60 needs: [build-docker-image, pull-docker-images] strategy: fail-fast: false @@ -106,11 +104,6 @@ jobs: - env: op-pp-2chains group: default run: TestBridgeL2ToL2|TestAutoClaimL2ToL2AllowAll|TestBridgeTrackerL1ToL2 - # TestMain-only smoke check: the intentionally non-matching regex runs - # environment startup and the post-test bridge health-check without tests. - - env: anvil-2chains - group: default - run: ^TestZZZNoSuchTest$ steps: - name: Checkout code uses: actions/checkout@v5 @@ -159,6 +152,59 @@ jobs: test/e2e/envs/**/logs/ if-no-files-found: ignore + test-go-e2e-anvil-2chains: + name: Run Go E2E Tests (anvil-2chains / default) + runs-on: ubuntu-latest + # The TestMain-only run measured 35.58s median locally (A3); 15 minutes + # provides CI setup and image-load headroom without constraining the shared matrix. + timeout-minutes: 15 + needs: [build-docker-image, pull-docker-images] + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: 1.25.7 + + - name: Install Docker Compose + run: | + sudo apt-get update + sudo apt-get install -y docker-compose + + - name: Download aggkit Docker image + uses: actions/download-artifact@v4 + with: + name: aggkit-docker-image + path: /tmp + + - name: Download pulled Docker images + uses: actions/download-artifact@v4 + with: + name: pulled-docker-images + path: /tmp + + - name: Load Docker images + run: | + docker load -i /tmp/aggkit-image.tar + docker load -i /tmp/docker-images.tar + + - name: Run E2E tests + env: + AGGKIT_E2E_ENV: anvil-2chains + run: make test-e2e TEST_RUN="^TestZZZNoSuchTest$" + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: e2e-test-results-anvil-2chains-default + path: | + test/e2e/*.log + test/e2e/envs/**/logs/ + if-no-files-found: ignore + test-go-e2e-force-ger-update: name: Run force_ger_update E2E (isolated) runs-on: ubuntu-latest