diff --git a/.github/workflows/upgrade-agglayer-e2e.yml b/.github/workflows/upgrade-agglayer-e2e.yml new file mode 100644 index 000000000..c2e466794 --- /dev/null +++ b/.github/workflows/upgrade-agglayer-e2e.yml @@ -0,0 +1,153 @@ +name: Nightly Agglayer Build + +on: + schedule: + - cron: "0 1 * * *" # 01:00 UTC nightly + workflow_dispatch: + inputs: + FROM_TAG: { description: "From version tag", required: false } + TO_TAG: { description: "To version tag", required: false } + ACTION: + description: "Action to perform" + required: false + default: "upgrade" + type: choice + options: [upgrade, downgrade] + KURTOSIS_PACKAGE_HASH: { description: "Kurtosis package hash", required: false } + ENCLAVE_NAME: { description: "Enclave name", required: false } + SP1_NETWORK_KEY: { description: "SP1 network key", required: false } + push: + branches: [ upgrade-agglayer-cdk-opgeth ] + +jobs: + run-nightly: + runs-on: ubuntu-latest + timeout-minutes: 180 + + # Defaults for scheduled runs; inputs override on manual runs + env: + # Inputs (manual) → Repo Variables (nightly) → hard default (always) + FROM_TAG: ${{ inputs.FROM_TAG || vars.NIGHTLY_FROM_TAG || '0.3.0-rc.21' }} + TO_TAG: ${{ inputs.TO_TAG || vars.NIGHTLY_TO_TAG || '0.3.5' }} + ACTION: ${{ inputs.ACTION || vars.NIGHTLY_ACTION || 'upgrade' }} + KURTOSIS_PACKAGE_HASH: ${{ inputs.KURTOSIS_PACKAGE_HASH || vars.KURTOSIS_PACKAGE_HASH || '' }} + ENCLAVE_NAME: ${{ inputs.ENCLAVE_NAME || vars.KURTOSIS_ENCLAVE_NAME || 'cdk' }} + SP1_NETWORK_KEY: ${{ inputs.SP1_NETWORK_KEY || secrets.SP1_NETWORK_KEY || '' }} + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME || '' }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN || '' }} + POLYCLI_VERSION: v0.1.84 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Login to Docker Hub + if: ${{ env.DOCKER_USERNAME != '' && env.DOCKER_TOKEN != '' }} + uses: docker/login-action@v3 + with: + username: ${{ env.DOCKER_USERNAME }} + password: ${{ env.DOCKER_TOKEN }} + + # GHCR login + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Install kurtosis + - name: Install Kurtosis (via apt.fury.io) + run: | + set -euxo pipefail + echo "deb [trusted=yes] https://apt.fury.io/kurtosis-tech/ /" | sudo tee /etc/apt/sources.list.d/kurtosis.list + sudo apt update + sudo apt install -y kurtosis-cli + kurtosis version || kurtosis --version + + # Install polycli + - name: Install polycli + run: | + polycli_version="${{ env.POLYCLI_VERSION }}" + pushd $(mktemp -d) || exit 1 + curl -s -L "https://github.com/0xPolygon/polygon-cli/releases/download/${polycli_version}/polycli_${polycli_version}_linux_amd64.tar.gz" > polycli.tar.gz + tar xf polycli.tar.gz + mv polycli_* /usr/local/bin/polycli + polycli version + popd + + + - name: Install jq 1.6 and yq v4 + run: | + set -euxo pipefail + + # Create a temp directory + tmpdir="$(mktemp -d)" + + # Install jq 1.6 + curl -L -o "$tmpdir/jq" "https://github.com/jqlang/jq/releases/download/jq-1.6/jq-linux64" + sudo install -m 0755 "$tmpdir/jq" /usr/local/bin/jq + jq --version # should print jq-1.6 + + # Install yq v4 (example version v4.43.1, change if needed) + YQ_VERSION="v4.43.1" + curl -L -o "$tmpdir/yq" "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" + sudo install -m 0755 "$tmpdir/yq" /usr/local/bin/yq + yq --version # should print yq version 4.x + + # Clean up temp dir + rm -rf "$tmpdir" + + + - name: Prepare /tmp run folder + run: | + set -euxo pipefail + mkdir -p /tmp/agglayer-run + rsync -a scenarios/agglayer-upgrade-with-supplied-version/ /tmp/agglayer-run/ + chmod +x /tmp/agglayer-run/run.sh || true + chmod +x /tmp/agglayer-run/lxly.sh || true + chmod +x /tmp/agglayer-run/check_verification.sh || true + + - name: Create .env from env.example + working-directory: /tmp/agglayer-run + run: | + set -euxo pipefail + cp env.example .env + sed -i "s|{{KURTOSIS_PACKAGE_HASH}}|${KURTOSIS_PACKAGE_HASH}|g" .env + sed -i "s|{{ENCLAVE_NAME}}|${ENCLAVE_NAME}|g" .env + sed -i "s|{{SP1_NETWORK_KEY}}|${SP1_NETWORK_KEY}|g" .env + sed -i "s|{{FROM_TAG}}|${FROM_TAG}|g" .env + sed -i "s|{{TO_TAG}}|${TO_TAG}|g" .env + sed -i "s|{{ACTION}}|${ACTION}|g" .env + + - name: Sanity check + working-directory: /tmp/agglayer-run + run: | + set -euxo pipefail + ls -la + test -f .env + test -f run.sh + test -f lxly.sh + test -d assets + jq --version + yq --version + + - name: Run nightly + working-directory: /tmp/agglayer-run + env: + FROM_TAG: ${{ env.FROM_TAG }} + TO_TAG: ${{ env.TO_TAG }} + ACTION: ${{ env.ACTION }} + run: | + set -euxo pipefail + : "${FROM_TAG:?FROM_TAG missing}" + : "${TO_TAG:?TO_TAG missing}" + ./run.sh "$FROM_TAG" "$TO_TAG" "$ACTION" + + - name: Upload outputs + if: always() + uses: actions/upload-artifact@v4 + with: + name: agglayer-run-outputs + path: /tmp/agglayer-run/ + if-no-files-found: warn diff --git a/scenarios/agglayer-upgrade-with-supplied-version/README.md b/scenarios/agglayer-upgrade-with-supplied-version/README.md new file mode 100644 index 000000000..8d433b7fe --- /dev/null +++ b/scenarios/agglayer-upgrade-with-supplied-version/README.md @@ -0,0 +1,69 @@ +# Agglayer Upgrade/Downgrade Automation + +This repo contains e2e test scenario to automate **Agglayer image upgrade and downgrade** while running rollup testnets inside **Kurtosis**. + +It is designed to: + +- Bootstrap multiple rollup flavors ( e.g both opgeth and erigon stack ) into the **same Kurtosis enclave**. +- Start Agglayer using a **FROM_TAG** version. +- Upgrade Agglayer to a **TO_TAG** version. +- Optionally downgrade back to the original **FROM_TAG** for validation. +- Cleanly tear down the setup using: + ```bash + kurtosis enclave rm cdk --force + ``` + +- Test file: https://github.com/agglayer/e2e/actions/runs/17996146694/workflow?pr=168 +- Reference: https://github.com/agglayer/e2e/blob/main/tests/lxly/lxly.bats +- Branch-seen: (https://github.com/agglayer/e2e/blob/jihwan/multi-chain-bridge-workflow/tests/lxly/multi-chain-bridge.bats) +- Branch-sug: https://github.com/agglayer/e2e/blob/main/tests/lxly/lxly.bats +- Branch-seen: jihwan/multi-chain-bridge-workflow +- Kurtosis-branch: jhilliard/aggsender-validator-committee +- . ./common.sh && _setup_vars +# shellcheck source=./lxly.sh +# source "$SCRIPT_DIR/lxly.sh" +# main native +--- + +## Scripts + +- `run.sh` + Main orchestration script. Handles bootstrap, upgrade, and optional downgrade. + - **Create the .env file:** + ```bash + cp env.example .env + ``` + + - **Upgrade usage:** + ```bash + ./run.sh FROM_TAG TO_TAG + ``` + + example + + ```bash + ./run.sh 0.3.4 0.3.5 + ``` + + - **Downgrade usage:** + ```bash + ./run.sh FROM_TAG TO_TAG downgrade + ``` + example + + ```bash + ./run.sh 0.3.5 0.3.4 downgrade + ``` +## Arguments & Descriptions + +| Argument | Required | Description | +|------------------------|----------|-------------| +| `` | Yes | Docker image tag to start with (e.g., `0.3.4`)| +| `` | Yes | Docker image tag to upgrade to (e.g., `0.3.5`)| +| `[ACTION]` | No | Optional: set to `downgrade` to downgrade back to `` after upgrade | + +--- + + + +```` diff --git a/scenarios/agglayer-upgrade-with-supplied-version/assets/add_rollup_type.json b/scenarios/agglayer-upgrade-with-supplied-version/assets/add_rollup_type.json new file mode 100644 index 000000000..6fb808903 --- /dev/null +++ b/scenarios/agglayer-upgrade-with-supplied-version/assets/add_rollup_type.json @@ -0,0 +1,17 @@ +{ + "type": "EOA", + "consensusContract": "PolygonPessimisticConsensus", + "consensusContractAddress": "0xAC77A07dD1683DeA96c08F286b67783fB1e4B583", + "polygonRollupManagerAddress": "0x2F50ef6b8e8Ee4E579B17619A92dE3E2ffbD8AD2", + "verifierAddress": "0xf22E2B040B639180557745F47aB97dFA95B1e22a", + "description": "Type: Pessimistic, Upgrade v0.2.x to v0.3.x", + "forkID": 12, + "timelockDelay": 0, + "timelockSalt": "", + "deployerPvtKey": "0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625", + "maxFeePerGas": "", + "maxPriorityFeePerGas": "", + "multiplierGas": "", + "genesisRoot": "0xd9c8633088ce212acd9280c5f64623add99871234145a808ab6fbdbac5659a87", + "programVKey": "0x00e60517ac96bf6255d81083269e72c14ad006e5f336f852f7ee3efb91b966be" +} diff --git a/scenarios/agglayer-upgrade-with-supplied-version/assets/erigon-sovereign.yml b/scenarios/agglayer-upgrade-with-supplied-version/assets/erigon-sovereign.yml new file mode 100644 index 000000000..ba3e186ea --- /dev/null +++ b/scenarios/agglayer-upgrade-with-supplied-version/assets/erigon-sovereign.yml @@ -0,0 +1,27 @@ +deployment_stages: + deploy_l1: false + deploy_agglayer: false + deploy_l2_contracts: true + deploy_optimism_rollup: false + deploy_op_succinct: false + +args: + sequencer_type: erigon + consensus_contract_type: pessimistic + + # aggkit_image: ghcr.io/agglayer/aggkit:0.5.0-beta4 + aggkit_image: ghcr.io/agglayer/aggkit:0.7.0-beta6 + zkevm_prover_image: hermeznetwork/zkevm-prover:v8.0.0-RC16-fork.12 + cdk_erigon_node_image: hermeznetwork/cdk-erigon:v2.63.0-rc4 + + sp1_prover_key: "" + agglayer_prover_primary_prover: mock-prover + erigon_strict_mode: false + gas_token_enabled: false + zkevm_use_real_verifier: false + enable_normalcy: true + aggkit_components: aggsender,bridge + + zkevm_rollup_chain_id: 2151910 + deployment_suffix: "-003" + zkevm_rollup_id: 3 diff --git a/scenarios/agglayer-upgrade-with-supplied-version/assets/opgeth-ecdsa.yml b/scenarios/agglayer-upgrade-with-supplied-version/assets/opgeth-ecdsa.yml new file mode 100644 index 000000000..fd7dbb59a --- /dev/null +++ b/scenarios/agglayer-upgrade-with-supplied-version/assets/opgeth-ecdsa.yml @@ -0,0 +1,36 @@ +# This args-file deploys the OP Succinct chain. +deployment_stages: + deploy_l1: false + deploy_agglayer: false + deploy_op_succinct: false + +args: + # aggkit_image: "ghcr.io/agglayer/aggkit:0.7.0-beta3" # https://github.com/agglayer/aggkit/tree/v0.7.0-beta3 + aggkit_image: "ghcr.io/agglayer/aggkit:0.7.0-beta6" + # Agghcain ecdsa_multisig consensus. + consensus_contract_type: ecdsa_multisig + use_agg_sender_validator: True + aggkit_prover_primary_prover: "mock-prover" + # The below parameter will be used for aggsender multisig to have "agg_sender_validator_total_number" aggsender validators. + agg_sender_validator_total_number: 1 + agg_sender_multisig_threshold: 1 + zkevm_rollup_chain_id: 2151909 + deployment_suffix: "-002" + zkevm_rollup_id: 2 + # OP Networks rely on L1 blocks to have finalisation on L2. This means if the L1 blocktime is very fast, OP Succinct proof requests will have to bundle many L1 blocks into a single proof. + # This will significantly increase cycles even if the L2 network is empty. Instead of having 2s, for OP Succinct deployments, we recommend 12s. + # Note this will noticeably increase the deployment time because of the increased L1 finality. + l1_seconds_per_slot: 2 # TEMPORARY - DO NOT MERGE + +optimism_package: + chains: + - proposer_params: + enabled: false + challenger_params: + enabled: false + network_params: + name: "002" + network_id: "2151909" + seconds_per_slot: 1 + observability: + enabled: false diff --git a/scenarios/agglayer-upgrade-with-supplied-version/assets/opgeth-soverign.yml b/scenarios/agglayer-upgrade-with-supplied-version/assets/opgeth-soverign.yml new file mode 100644 index 000000000..ceb3bfca5 --- /dev/null +++ b/scenarios/agglayer-upgrade-with-supplied-version/assets/opgeth-soverign.yml @@ -0,0 +1,56 @@ +# This args-file deploys the OP Succinct chain. +deployment_stages: + deploy_optimism_rollup: true + # deploy_op_succinct: true + deploy_agglayer: true +args: + sp1_prover_key: "bcdf20249abf0ed6d944c0288fad489e33f66b3960d9e6229c1cd214ed3bbe31" # XXX: Replace with an active key + # Valid values are: "network-prover", "mock-prover" + agglayer_prover_primary_prover: "mock-prover" + # Valid values are: "network-prover", "mock-prover" + aggkit_prover_primary_prover: "mock-prover" + consensus_contract_type: pessimistic + # aggkit_prover_image: "ghcr.io/agglayer/aggkit-prover:feat-rust-proposer" + # false = network + # Using the network provers will use the real SP1 verifier contract which is also deployed together in the Kurtosis devnet. + # A mock verifier is used otherwise. + op_succinct_mock: false + # Enable real (non-mock) verifier on contracts + zkevm_use_real_verifier: false + # Enable the integration with the Agglayer + op_succinct_agglayer: true + # Proof type. Must match the verifier gateway contract type. Options: "plonk", "groth16", "compressed", "core" + op_succinct_agg_proof_mode: "compressed" + # The minimum interval in L2 blocks at which checkpoints must be submitted. An aggregation proof can be posted for any range larger than this interval. + op_succinct_submission_interval: "1" + # The maximum number of concurrent proof requests to send to the `op-succinct-server` + op_succinct_max_concurrent_proof_requests: "1" + # The maximum number of concurrent witness generation processes to run on the `op-succinct-server` + op_succinct_max_concurrent_witness_gen: "1" + # Must match network_id field in network_params.network_id + # The number following the "-" should be identical to network_params.name + deployment_suffix: "-001" + zkevm_rollup_id: 1 + zkevm_rollup_chain_id: 2151908 + # Size of the range proof. + op_succinct_range_proof_interval: "1800" + # OP Networks rely on L1 blocks to have finalization on L2. This means if the L1 blocktime is very fast, OP Succinct proof requests will have to bundle many L1 blocks into a single proof. + # This will significantly increase cycles even if the L2 network is empty. Instead of having 2s, for OP Succinct deployments, we recommend 12s. + # Note this will noticeably increase the deployment time because of the increased L1 finality. + l1_seconds_per_slot: 12 + +optimism_package: + chains: + - proposer_params: + enabled: false + challenger_params: + enabled: false + network_params: + name: "001" + network_id: "2151908" + seconds_per_slot: 1 + observability: + enabled: true +# optimism_package: +# observability: +# enabled: true diff --git a/scenarios/agglayer-upgrade-with-supplied-version/check_verification.sh b/scenarios/agglayer-upgrade-with-supplied-version/check_verification.sh new file mode 100644 index 000000000..fde18a26a --- /dev/null +++ b/scenarios/agglayer-upgrade-with-supplied-version/check_verification.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +set -euo pipefail + +# These get replaced by the host helper script +ROLLUP_MANAGER="__ROLLUP_MANAGER__" # from combined.json: polygonRollupManagerAddress + + +RPC_URL="${RPC_URL:-http://el-1-geth-lighthouse:8545}" +LOOKBACK="${LOOKBACK:-100}" # set the look back for event to be 100 blocks + +EVENT="VerifyBatchesTrustedAggregator(uint32,uint64,bytes32,bytes32,address)" + +CURRENT="$(cast block-number --rpc-url "$RPC_URL")" # gets the lates blocknumber captured by CURRENT variable +FROM=$(( CURRENT - LOOKBACK )) # set the start of scan window +if (( FROM < 0 )); then FROM=0; fi # if the scan range is less than 0 we set it to begin from 0 + +echo "Scanning blocks $FROM → $CURRENT on $RPC_URL" +echo " contract (rollup manager): $ROLLUP_MANAGER" +echo " event: $EVENT" +echo " lookback: $LOOKBACK blocks" + +TMP_JSON="$(mktemp -t verify-logs.XXXXXX.json)" +trap 'rm -f "$TMP_JSON"' EXIT + +# Stream logs as pretty JSON to terminal (if jq is installed) and save raw JSON to file +if command -v jq >/dev/null 2>&1; then + cast logs \ + --address "$ROLLUP_MANAGER" "$EVENT" \ + --from-block "$FROM" --to-block "$CURRENT" \ + --rpc-url "$RPC_URL" \ + --json \ + | tee "$TMP_JSON" | jq -C . +else + echo "(jq not found — showing raw JSON)" + cast logs \ + --address "$ROLLUP_MANAGER" "$EVENT" \ + --from-block "$FROM" --to-block "$CURRENT" \ + --rpc-url "$RPC_URL" \ + --json | tee "$TMP_JSON" +fi + +FOUND=0 +if command -v jq >/dev/null 2>&1; then + FOUND="$(jq 'length' "$TMP_JSON")" # count how many item are in the json file +else + # crude fallback: count '[' then subtract brackets; if it's "[]", FOUND stays 0 + # (safe enough for our purpose when jq isn't available) + if grep -q '"address"' "$TMP_JSON"; then # checks is tem_json contains substring address + FOUND="$(grep -c '"address"' "$TMP_JSON" || true)" # counts lines that contains the address substring + fi +fi + +if [[ $FOUND -gt 0 ]]; then + echo "[SUCCESS---] Found ${FOUND} verification event(s) for $ROLLUP_MANAGER in the last $LOOKBACK blocks." + if command -v jq >/dev/null 2>&1; then + echo "--- summary ---" + # Show a compact, human-readable list + jq -r '.[] | "block=\(.blockNumber) tx=\(.transactionHash) logIndex=\(.logIndex) rollupIdTopic=\(.topics[1]) aggregatorTopic=\(.topics[2])"' "$TMP_JSON" + fi + exit 0 +else + echo "[ERROR---] No verification events found for $ROLLUP_MANAGER in the last $LOOKBACK blocks." + exit 2 +fi diff --git a/scenarios/agglayer-upgrade-with-supplied-version/common.sh b/scenarios/agglayer-upgrade-with-supplied-version/common.sh new file mode 100755 index 000000000..6c5726a65 --- /dev/null +++ b/scenarios/agglayer-upgrade-with-supplied-version/common.sh @@ -0,0 +1,236 @@ +#!/usr/bin/env bash +# common.sh — standalone version of common.bash (no Bats). +# Usage: +# source ./common.sh && _setup_vars # to use from another script +# ./common.sh # to run once and print vars + +set -euo pipefail + +# Quiet shellcheck about variables we set later +# shellcheck disable=SC2034 +declare status output || true + +log() { echo "[$(date -Is)] $*" >&2; } + +function _setup_vars() { + # These vars are set when calling this function: + # l2_rpc_url, l1_rpc_url, l2_private_key, l1_private_key, + # l1_eth_address, l2_eth_address, l2_chain_id, l1_chain_id, + # l2_type, l2_node_url, l1_system_config_addr, l1_optimism_portal_addr, + # kurtosis_enclave_name, l1_bridge_addr, l2_bridge_addr, + # l1_network_id, l2_network_id + + # + # Paths for libs, etc + # + HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + + + # Keep this for compatibility; harmless if Bats isn't used. + export BATS_LIB_PATH="${BATS_LIB_PATH:-}:$HERE/lib" + export PROJECT_ROOT="${PROJECT_ROOT:-$HERE/../..}" + log "PROJECT_ROOT=$PROJECT_ROOT BATS_LIB_PATH=$BATS_LIB_PATH" + + # + # L2 RPC URL + # + l2_rpc_url="${L2_RPC_URL:-}" + if [[ -z "${ENCLAVE_NAME:-}" && -z "$l2_rpc_url" ]]; then + # If no L2_RPC_URL and no ENCLAVE_NAME, try default enclave "cdk" + ENCLAVE_NAME="cdk" + fi + + if [[ -n "${ENCLAVE_NAME:-}" ]]; then + export kurtosis_enclave_name="$ENCLAVE_NAME" + if kurtosis_l2_rpc_url="$(kurtosis port print "$kurtosis_enclave_name" op-el-1-op-geth-op-node-001 rpc 2>/dev/null)"; then + l2_type="op-geth" + elif kurtosis_l2_rpc_url="$(kurtosis port print "$kurtosis_enclave_name" cdk-erigon-rpc-001 rpc 2>/dev/null)"; then + l2_type="cdk-erigon" + else + unset kurtosis_l2_rpc_url + fi + fi + + # If both are set, enforce they match + if [[ -n "${l2_rpc_url:-}" && -n "${kurtosis_l2_rpc_url:-}" && "$l2_rpc_url" != "$kurtosis_l2_rpc_url" ]]; then + log "L2_RPC_URL ($l2_rpc_url) != Kurtosis L2 RPC ($kurtosis_l2_rpc_url). Omit one of L2_RPC_URL or ENCLAVE_NAME." + exit 1 + fi + if [[ -z "${l2_rpc_url:-}" && -n "${kurtosis_l2_rpc_url:-}" ]]; then + l2_rpc_url="$kurtosis_l2_rpc_url" + fi + + if [[ -n "${l2_rpc_url:-}" ]]; then + l2_chain_id="$(cast chain-id --rpc-url "$l2_rpc_url" 2>/dev/null || echo "")" + if [[ -n "${L2_CHAIN_ID:-}" && -n "$l2_chain_id" && "$L2_CHAIN_ID" != "$l2_chain_id" ]]; then + log "L2_CHAIN_ID ($L2_CHAIN_ID) != chain id from $l2_rpc_url ($l2_chain_id)." + exit 1 + fi + log "l2_rpc_url=$l2_rpc_url l2_chain_id=$l2_chain_id" + export l2_rpc_url l2_chain_id + fi + + # + # L1 RPC URL + # + l1_rpc_url="${L1_RPC_URL:-}" + + if [[ -n "${kurtosis_enclave_name:-}" ]]; then + if kurtosis_l1_rpc_url="$(kurtosis port print "$kurtosis_enclave_name" el-1-geth-lighthouse rpc 2>/dev/null)"; then + true + else + unset kurtosis_l1_rpc_url + fi + fi + + # Enforce match if both are set (note: Kurtosis returns host:port) + if [[ -n "${l1_rpc_url:-}" && -n "${kurtosis_l1_rpc_url:-}" && "$l1_rpc_url" != "http://${kurtosis_l1_rpc_url}" ]]; then + log "L1_RPC_URL ($l1_rpc_url) != Kurtosis L1 RPC (http://${kurtosis_l1_rpc_url}). Omit one of L1_RPC_URL or ENCLAVE_NAME." + exit 1 + fi + if [[ -z "${l1_rpc_url:-}" && -n "${kurtosis_l1_rpc_url:-}" ]]; then + l1_rpc_url="http://${kurtosis_l1_rpc_url}" + fi + + if [[ -n "${l1_rpc_url:-}" ]]; then + l1_chain_id="$(cast chain-id --rpc-url "$l1_rpc_url" 2>/dev/null || echo "")" + if [[ -n "${L1_CHAIN_ID:-}" && -n "$l1_chain_id" && "$L1_CHAIN_ID" != "$l1_chain_id" ]]; then + log "L1_CHAIN_ID ($L1_CHAIN_ID) != chain id from $l1_rpc_url ($l1_chain_id)." + exit 1 + fi + log "l1_rpc_url=$l1_rpc_url l1_chain_id=$l1_chain_id" + export l1_rpc_url l1_chain_id + fi + + # + # Default private keys & addresses (TEST ONLY) + # + l1_private_key="${L1_PRIVATE_KEY:-12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625}" + l2_private_key="${L2_PRIVATE_KEY:-12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625}" + l1_eth_address="$(cast wallet address --private-key "$l1_private_key")" + l2_eth_address="$(cast wallet address --private-key "$l2_private_key")" + export l1_private_key l2_private_key l1_eth_address l2_eth_address + log "l1_eth_address=$l1_eth_address l1_private_key=$l1_private_key" + log "l2_eth_address=$l2_eth_address l2_private_key=$l2_private_key" + + # + # OP Stack–specific vars (only if op-geth + Kurtosis) + # + if [[ "${l2_type:-}" == "op-geth" && -n "${kurtosis_enclave_name:-}" ]]; then + l2_node_url="${L2_NODE_URL:-"$(kurtosis port print "$kurtosis_enclave_name" op-cl-1-op-node-op-geth-001 http 2>/dev/null || true)"}" + if [[ -n "${l2_node_url:-}" ]]; then + if output="$(cast rpc --rpc-url "$l2_node_url" optimism_rollupConfig 2>/dev/null)"; then + l1_system_config_addr="$(echo "$output" | jq -r '.l1_system_config_address')" + if [[ -n "$l1_system_config_addr" && -n "${l1_rpc_url:-}" ]]; then + if l1_optimism_portal_addr="$(cast call "$l1_system_config_addr" "optimismPortal()(address)" --rpc-url "$l1_rpc_url" 2>/dev/null)"; then + export l1_optimism_portal_addr + log "l2_node_url=$l2_node_url l1_system_config_addr=$l1_system_config_addr l1_optimism_portal_addr=$l1_optimism_portal_addr" + else + log "l2_node_url=$l2_node_url l1_system_config_addr=$l1_system_config_addr" + fi + else + log "l2_node_url=$l2_node_url" + fi + else + log "l2_node_url=$l2_node_url" + fi + else + log "Could not determine L2 node URL" + fi + fi + + # + # Kurtosis combined.json (for bridge addresses) + # + if [[ -n "${kurtosis_enclave_name:-}" ]]; then + combined_url="$(kurtosis port print "$kurtosis_enclave_name" contracts-001 http 2>/dev/null || true)" + combined_json_data="" + if [[ -n "$combined_url" ]]; then + combined_json_data="$(curl -s "$combined_url/opt/zkevm/combined-001.json" || true)" + fi + if [[ -z "${combined_json_data:-}" ]] || ! echo "$combined_json_data" | jq empty >/dev/null 2>&1; then + unset combined_json_data + fi + fi + + # + # Bridge addresses (env overrides, else from combined.json) + # + l1_bridge_addr="${L1_BRIDGE_ADDR:-}" + l2_bridge_addr="${L2_BRIDGE_ADDR:-}" + + if [[ -n "${combined_json_data:-}" ]]; then + kurtosis_l1_bridge_addr="$(echo "$combined_json_data" | jq -r .polygonZkEVMBridgeAddress)" + kurtosis_l2_bridge_addr="$(echo "$combined_json_data" | jq -r .polygonZkEVML2BridgeAddress)" + + if [[ -z "$l1_bridge_addr" ]]; then + l1_bridge_addr="$kurtosis_l1_bridge_addr" + elif [[ "$l1_bridge_addr" != "$kurtosis_l1_bridge_addr" ]]; then + log "L1_BRIDGE_ADDR ($l1_bridge_addr) != Kurtosis ($kurtosis_l1_bridge_addr)." + exit 1 + fi + + if [[ -z "$l2_bridge_addr" ]]; then + l2_bridge_addr="$kurtosis_l2_bridge_addr" + elif [[ "$l2_bridge_addr" != "$kurtosis_l2_bridge_addr" ]]; then + log "L2_BRIDGE_ADDR ($l2_bridge_addr) != Kurtosis ($kurtosis_l2_bridge_addr)." + exit 1 + fi + fi + + if [[ -n "${l1_bridge_addr:-}" && -n "${l2_bridge_addr:-}" ]]; then + export l1_bridge_addr l2_bridge_addr + log "l1_bridge_addr=$l1_bridge_addr l2_bridge_addr=$l2_bridge_addr" + elif [[ -n "${l1_bridge_addr:-}" ]]; then + export l1_bridge_addr + log "l1_bridge_addr=$l1_bridge_addr" + elif [[ -n "${l2_bridge_addr:-}" ]]; then + export l2_bridge_addr + log "l2_bridge_addr=$l2_bridge_addr" + fi + + # + # Network IDs (via bridge contracts) + # + if [[ -n "${l1_rpc_url:-}" && -n "${l1_bridge_addr:-}" ]]; then + l1_network_id="$(cast call --rpc-url "$l1_rpc_url" "$l1_bridge_addr" 'networkID()(uint32)')" + fi + if [[ -n "${l2_rpc_url:-}" && -n "${l2_bridge_addr:-}" ]]; then + l2_network_id="$(cast call --rpc-url "$l2_rpc_url" "$l2_bridge_addr" 'networkID()(uint32)')" + fi + + if [[ -n "${l1_network_id:-}" && -n "${l2_network_id:-}" ]]; then + export l1_network_id l2_network_id + log "l1_network_id=$l1_network_id l2_network_id=$l2_network_id" + elif [[ -n "${l1_network_id:-}" ]]; then + export l1_network_id + log "l1_network_id=$l1_network_id" + elif [[ -n "${l2_network_id:-}" ]]; then + export l2_network_id + log "l2_network_id=$l2_network_id" + fi +} + +# If executed directly, run setup and print a concise summary +if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then + _setup_vars + echo + echo "=== common.sh initialized ===" + echo "PROJECT_ROOT=$PROJECT_ROOT" + echo "l1_rpc_url=${l1_rpc_url:-}" + echo "l2_rpc_url=${l2_rpc_url:-}" + echo "l1_chain_id=${l1_chain_id:-}" + echo "l2_chain_id=${l2_chain_id:-}" + echo "l1_eth_address=${l1_eth_address:-}" + echo "l2_eth_address=${l2_eth_address:-}" + echo "l1_bridge_addr=${l1_bridge_addr:-}" + echo "l2_bridge_addr=${l2_bridge_addr:-}" + echo "l1_network_id=${l1_network_id:-}" + echo "l2_network_id=${l2_network_id:-}" + echo "l2_type=${l2_type:-}" + echo "l2_node_url=${l2_node_url:-}" + echo "l1_system_config_addr=${l1_system_config_addr:-}" + echo "l1_optimism_portal_addr=${l1_optimism_portal_addr:-}" + echo "l1_private_key=${l1_private_key:-}" + echo "l2_private_key=${l2_private_key:-}" +fi \ No newline at end of file diff --git a/scenarios/agglayer-upgrade-with-supplied-version/env.example b/scenarios/agglayer-upgrade-with-supplied-version/env.example new file mode 100644 index 000000000..8175f03fd --- /dev/null +++ b/scenarios/agglayer-upgrade-with-supplied-version/env.example @@ -0,0 +1,8 @@ +ENCLAVE_NAME=cdk +KURTOSIS_PACKAGE_HASH=c5309bd43c15094baa4427d8c04bf441f59edca7 # Latest commit +# KURTOSIS_PACKAGE_HASH=08225fbfb036f8269eaae4a3eae3d67d2904f76a # Latest commit +SP1_NETWORK_KEY=0x50630eb71ab68ff812fcb9b9c58859f9e9f70a62b7e9e97c9c1cc09d86c760d8 # Replace with valid key +FROM_TAG=0.3.4 +TO_TAG=0.3.5 + + diff --git a/scenarios/agglayer-upgrade-with-supplied-version/lxly.sh b/scenarios/agglayer-upgrade-with-supplied-version/lxly.sh new file mode 100755 index 000000000..6ab9ed5bf --- /dev/null +++ b/scenarios/agglayer-upgrade-with-supplied-version/lxly.sh @@ -0,0 +1,303 @@ +#!/usr/bin/env bash +# lxly.sh — standalone replacement for lxly.bats +# Usage: +# ./lxly.sh native +# ./lxly.sh erc20-roundtrip +# Env (optional): +# ENCLAVE_NAME, L1_RPC_URL, L2_RPC_URL, L1_BRIDGE_ADDR, L2_BRIDGE_ADDR +# BRIDGE_SERVICE_URL, CLAIMTXMANAGER_ADDR, CLAIM_WAIT_DURATION +# TRANSACTION_RECEIPT_TIMEOUT, ERC20_TOKEN_NAME, ERC20_TOKEN_SYMBOL +# +# Notes: +# - This version removes --wait (unsupported in your polycli) and uses a retry loop. +# - It passes --transaction-receipt-timeout consistently to bridge/claim. +# - It computes depositCount safely (pre/post) to pick the correct index to claim. + +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# --- config defaults (override via env) --- +: "${CLAIMTXMANAGER_ADDR:=0x5f5dB0D4D58310F53713eF4Df80ba6717868A9f8}" +: "${CLAIM_WAIT_DURATION:=1h}" # e.g., 10m, 120s, or just 600 also 1h +: "${TRANSACTION_RECEIPT_TIMEOUT:=3600}" # 1h = 3600 or 60 seconds +: "${ERC20_TOKEN_NAME:=e2e test}" +: "${ERC20_TOKEN_SYMBOL:=E2E}" + +log() { echo "[$(date -Is)] $*" >&2; } + +need() { + command -v "$1" >/dev/null 2>&1 || { echo "Missing dependency: $1" >&2; exit 127; } +} + +preflight() { + need cast + need jq + need kurtosis + need polycli + need curl +} + +# Parse CLAIM_WAIT_DURATION into seconds (supports Xm, Xs, or plain X) +duration_to_seconds() { + local d="${1:-600}" + if [[ "$d" =~ ^[0-9]+m$ ]]; then + echo $(( ${d%m} * 60 )) + elif [[ "$d" =~ ^[0-9]+s$ ]]; then + echo "${d%s}" + elif [[ "$d" =~ ^[0-9]+$ ]]; then + echo "$d" + else + # fallback: 10 minutes + echo 600 + fi +} + +# Retry wrapper around `polycli ulxly claim asset` +# Args: bridge_addr priv_key rpc_url deposit_count deposit_network bridge_service_url timeout_secs +claim_with_retry() { + local bridge_addr="$1" priv="$2" rpc="$3" dep_count="$4" dep_net="$5" svc="$6" timeout_secs="$7" + local start rc now + start="$(date +%s)" + while :; do + set +e + polycli ulxly claim asset \ + --bridge-address "$bridge_addr" \ + --private-key "$priv" \ + --rpc-url "$rpc" \ + --deposit-count "$dep_count" \ + --deposit-network "$dep_net" \ + --bridge-service-url "$svc" \ + --transaction-receipt-timeout "$TRANSACTION_RECEIPT_TIMEOUT" + rc=$? + set -e + if (( rc == 0 )); then + log "Claim succeeded (deposit_count=$dep_count deposit_network=$dep_net)" + return 0 + fi + now="$(date +%s)" + if (( now - start >= timeout_secs )); then + log "Claim timed out after ${timeout_secs}s (last rc=$rc). Auto-claimer may have handled it." + return $rc + fi + log "Claim not ready yet; retrying in 10s..." + sleep 10 + done +} + +# shellcheck source=common.sh +load_common() { + if [[ -f "$HERE/common.sh" ]]; then + # shellcheck source=/dev/null + source "$HERE/common.sh" + else + # shellcheck source=/dev/null + source "$HERE/../../core/helpers/common.bash" + fi + _setup_vars + : "${BRIDGE_SERVICE_URL:="$(kurtosis port print "${kurtosis_enclave_name:-cdk}" zkevm-bridge-service-001 rpc)"}" + log "using BRIDGE_SERVICE_URL=$BRIDGE_SERVICE_URL" +} + +# Fund the claim_tx_manager so the bridge service can pay gas for claims (devnets) +fund_claim_tx_manager() { + local balance + balance="$(cast balance --rpc-url "$l2_rpc_url" "$CLAIMTXMANAGER_ADDR")" + if [[ "$balance" != "0" ]]; then + log "ClaimTxManager already funded on L2 (balance=$balance)" + return + fi + log "Funding ClaimTxManager on L2 with 1 ether" + cast send --legacy --value 1ether \ + --rpc-url "$l2_rpc_url" \ + --private-key "$l2_private_key" \ + "$CLAIMTXMANAGER_ADDR" +} + +native() { + preflight + load_common + fund_claim_tx_manager + + local pre_dc post_dc deposit_count bridge_amount claim_secs + claim_secs="$(duration_to_seconds "$CLAIM_WAIT_DURATION")" + + # L1 -> L2 native + pre_dc="$(cast call --rpc-url "$l1_rpc_url" "$l1_bridge_addr" 'depositCount()(uint256)' | awk '{print $1}')" + bridge_amount="$(date +%s)" + + log "Bridging native ETH L1 -> L2 amount=$bridge_amount" + polycli ulxly bridge asset \ + --bridge-address "$l1_bridge_addr" \ + --destination-address "$l2_eth_address" \ + --destination-network "$l2_network_id" \ + --private-key "$l1_private_key" \ + --rpc-url "$l1_rpc_url" \ + --transaction-receipt-timeout "$TRANSACTION_RECEIPT_TIMEOUT" \ + --value "$bridge_amount" + + post_dc="$(cast call --rpc-url "$l1_rpc_url" "$l1_bridge_addr" 'depositCount()(uint256)' | awk '{print $1}')" + if [[ "$post_dc" == "$pre_dc" ]]; then + echo "Deposit count did not increase on L1" >&2 + exit 1 + fi + # The deposit we just made corresponds to the *previous* counter value. + deposit_count="$pre_dc" + + # Claim on L2 + claim_with_retry "$l2_bridge_addr" "$l2_private_key" "$l2_rpc_url" \ + "$deposit_count" "0" "$BRIDGE_SERVICE_URL" "$claim_secs" \ + || log "Claim did not complete within ${claim_secs}s (auto-claimer may have handled it)." +} + +erc20_roundtrip() { + preflight + load_common + fund_claim_tx_manager + + local claim_secs + claim_secs="$(duration_to_seconds "$CLAIM_WAIT_DURATION")" + + # Ensure deterministic deployer is present on L2 + local salt deterministic_deployer_addr deterministic_deployer_code + salt="0x0000000000000000000000000000000000000000000000000000000000000000" + deterministic_deployer_addr=0x4e59b44847b379578588920ca78fbf26c0b4956c + deterministic_deployer_code="$(cast code --rpc-url "$l2_rpc_url" "$deterministic_deployer_addr")" + + if [[ "$deterministic_deployer_code" == "0x" ]]; then + log "Publishing deterministic deployer bytecode on L2" + cast send --legacy --value 0.1ether --rpc-url "$l2_rpc_url" --private-key "$l2_private_key" 0x3fab184622dc19b6109349b94811493bf2a45362 + cast publish --rpc-url "$l2_rpc_url" 0xf8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222 + fi + + # Deploy ERC20 via CREATE2 if missing + local erc_20_bytecode constructor_args test_erc20_addr + erc_20_bytecode="$(cat "$HERE/../../core/contracts/bin/erc20permitmock.bin" 2>/dev/null || cat core/contracts/bin/erc20permitmock.bin)" + erc_20_bytecode="${erc_20_bytecode#0x}" # safety: strip accidental 0x + + constructor_args="$(cast abi-encode 'f(string,string,address,uint256)' "$ERC20_TOKEN_NAME" "$ERC20_TOKEN_SYMBOL" "$l2_eth_address" 100000000000000000000)" + constructor_args="${constructor_args#0x}" + + test_erc20_addr="$(cast create2 --deployer 0x4e59b44847b379578588920ca78fbf26c0b4956c --salt "$salt" --init-code "$erc_20_bytecode$constructor_args")" + + if [[ "$(cast code --rpc-url "$l2_rpc_url" "$test_erc20_addr")" == "0x" ]]; then + log "Deploying ERC20 to $test_erc20_addr via CREATE2 and approving bridge" + cast send --legacy --rpc-url "$l2_rpc_url" --private-key "$l2_private_key" \ + "$deterministic_deployer_addr" "$salt$erc_20_bytecode$constructor_args" + cast send --legacy --rpc-url "$l2_rpc_url" --private-key "$l2_private_key" \ + "$test_erc20_addr" 'approve(address,uint256)' "$l2_bridge_addr" "$(cast max-uint)" + else + log "ERC20 already present at $test_erc20_addr" + fi + + # --- L2 -> L1 (ERC20) --- + local pre_dc_l2 post_dc_l2 deposit_count_l2 bridge_amount token_hash wrapped_token_addr + pre_dc_l2="$(cast call --rpc-url "$l2_rpc_url" "$l2_bridge_addr" 'depositCount()(uint256)' | awk '{print $1}')" + bridge_amount="$(date +%s)" + + log "Bridging ERC20 L2 -> L1 amount=$bridge_amount token=$test_erc20_addr" + polycli ulxly bridge asset \ + --destination-network 0 \ + --destination-address "$l1_eth_address" \ + --token-address "$test_erc20_addr" \ + --value "$bridge_amount" \ + --bridge-address "$l2_bridge_addr" \ + --rpc-url "$l2_rpc_url" \ + --private-key "$l2_private_key" \ + --transaction-receipt-timeout "$TRANSACTION_RECEIPT_TIMEOUT" + + post_dc_l2="$(cast call --rpc-url "$l2_rpc_url" "$l2_bridge_addr" 'depositCount()(uint256)' | awk '{print $1}')" + if [[ "$post_dc_l2" == "$pre_dc_l2" ]]; then + echo "Deposit count did not increase on L2" >&2 + exit 1 + fi + deposit_count_l2="$pre_dc_l2" + + log "Claiming on L1 for L2 depositCount=$deposit_count_l2" + claim_with_retry "$l1_bridge_addr" "$l1_private_key" "$l1_rpc_url" \ + "$deposit_count_l2" "$l2_network_id" "$BRIDGE_SERVICE_URL" "$(duration_to_seconds "$CLAIM_WAIT_DURATION")" + + # Find wrapped token on L1 + token_hash="$(cast keccak "$(cast abi-encode --packed 'f(uint32, address)' "$l2_network_id" "$test_erc20_addr")")" + wrapped_token_addr="$(cast call --rpc-url "$l1_rpc_url" "$l1_bridge_addr" 'tokenInfoToWrappedToken(bytes32)(address)' "$token_hash")" + + # --- L1 -> L2 (wrapped token) --- + local pre_dc_l1 post_dc_l1 deposit_count_l1 + pre_dc_l1="$(cast call --rpc-url "$l1_rpc_url" "$l1_bridge_addr" 'depositCount()(uint256)' | awk '{print $1}')" + + log "Bridging wrapped L1 token -> L2 amount=$bridge_amount token=$wrapped_token_addr" + polycli ulxly bridge asset \ + --destination-network "$l2_network_id" \ + --destination-address "$l2_eth_address" \ + --token-address "$wrapped_token_addr" \ + --value "$bridge_amount" \ + --bridge-address "$l1_bridge_addr" \ + --rpc-url "$l1_rpc_url" \ + --private-key "$l1_private_key" \ + --transaction-receipt-timeout "$TRANSACTION_RECEIPT_TIMEOUT" + + post_dc_l1="$(cast call --rpc-url "$l1_rpc_url" "$l1_bridge_addr" 'depositCount()(uint256)' | awk '{print $1}')" + if [[ "$post_dc_l1" == "$pre_dc_l1" ]]; then + echo "Deposit count did not increase on L1 (wrapped hop)" >&2 + exit 1 + fi + deposit_count_l1="$pre_dc_l1" + + # Claim on L2 (for the L1->L2 deposit) + claim_with_retry "$l2_bridge_addr" "$l2_private_key" "$l2_rpc_url" \ + "$deposit_count_l1" "0" "$BRIDGE_SERVICE_URL" "$(duration_to_seconds "$CLAIM_WAIT_DURATION")" \ + || log "L2 claim may have been handled by auto-claimer." + + # --- Repeat another L2 -> L1 (ERC20) and claim on L1 --- + local pre_dc_l2_b post_dc_l2_b deposit_count_l2_b + pre_dc_l2_b="$(cast call --rpc-url "$l2_rpc_url" "$l2_bridge_addr" 'depositCount()(uint256)' | awk '{print $1}')" + bridge_amount="$(date +%s)" + + log "Bridging ERC20 again L2 -> L1 amount=$bridge_amount" + polycli ulxly bridge asset \ + --destination-network 0 \ + --token-address "$test_erc20_addr" \ + --value "$bridge_amount" \ + --bridge-address "$l2_bridge_addr" \ + --rpc-url "$l2_rpc_url" \ + --private-key "$l2_private_key" \ + --transaction-receipt-timeout "$TRANSACTION_RECEIPT_TIMEOUT" + + post_dc_l2_b="$(cast call --rpc-url "$l2_rpc_url" "$l2_bridge_addr" 'depositCount()(uint256)' | awk '{print $1}')" + if [[ "$post_dc_l2_b" == "$pre_dc_l2_b" ]]; then + echo "Deposit count did not increase on L2 (second hop)" >&2 + exit 1 + fi + deposit_count_l2_b="$pre_dc_l2_b" + + log "Claiming on L1 for depositCount=$deposit_count_l2_b" + claim_with_retry "$l1_bridge_addr" "$l1_private_key" "$l1_rpc_url" \ + "$deposit_count_l2_b" "$l2_network_id" "$BRIDGE_SERVICE_URL" "$(duration_to_seconds "$CLAIM_WAIT_DURATION")" + + log "ERC20 roundtrip complete." +} + +usage() { + cat >&2 < + +Examples: + ENCLAVE_NAME=cdk $0 native + ENCLAVE_NAME=cdk $0 erc20-roundtrip + +Optionally set: + L1_RPC_URL, L2_RPC_URL, L1_BRIDGE_ADDR, L2_BRIDGE_ADDR, + BRIDGE_SERVICE_URL, CLAIMTXMANAGER_ADDR, CLAIM_WAIT_DURATION, + TRANSACTION_RECEIPT_TIMEOUT, ERC20_TOKEN_NAME, ERC20_TOKEN_SYMBOL +EOF +} + +main() { + case "${1:-}" in + native) native ;; + erc20-roundtrip) erc20_roundtrip ;; + *) usage; exit 2 ;; + esac +} + +main "$@" diff --git a/scenarios/agglayer-upgrade-with-supplied-version/run.sh b/scenarios/agglayer-upgrade-with-supplied-version/run.sh new file mode 100755 index 000000000..f4a80dc8d --- /dev/null +++ b/scenarios/agglayer-upgrade-with-supplied-version/run.sh @@ -0,0 +1,412 @@ +#!/bin/env bash +set -e + + +# ---------------------------------------------------------------------- +# Function: Adds rollup RPCs to agglayer and restarts the service +# ---------------------------------------------------------------------- +add_rollup_rpc_to_agglayer() { + echo "Updating agglayer config..." + + kurtosis service exec cdk agglayer ' + set -eu + file=/etc/zkevm/agglayer-config.toml + if ! grep -q "2 = http://op-el-1-op-geth-op-node-002:8545" "$file"; then + sed -i "/1 = \"http:\/\/op-el-1-op-geth-op-node-001:8545\"/a 2 = \"http://op-el-1-op-geth-op-node-002:8545\"" "$file" + sed -i "/2 = \"http:\/\/op-el-1-op-geth-op-node-002:8545\"/a 3 = \"http://cdk-erigon-rpc-003:8123\"" "$file" + + + fi + ' + + echo "Restarting agglayer..." + kurtosis service stop cdk agglayer + kurtosis service start cdk agglayer + + echo "Done." +} + + + +# ----------------------------------------------------------------------------------------------------------------- +# Function: to verify deployment by checking for events VerifyBatchesTrustedAggregator in the contract deployments +# ------------------------------------------------------------------------------------------------------------------ +run_verification_in_container() ( + set -euo pipefail + set -o pipefail + + SERVICE_NAME="${1:?Usage: run_verification_in_container [LOCAL_SCRIPT] [RPC_URL]}" + LOCAL_SCRIPT="${2:-./check_verification.sh}" + RPC_URL="${3:-http://el-1-geth-lighthouse:8545}" + + LOG_DIR="${LOG_DIR:-./logs}" + mkdir -p "$LOG_DIR" + LOG_FILE="${LOG_DIR}/${SERVICE_NAME}.log" + + START_TS="$(date -Is)" # current timestamp + echo "[START ${START_TS}] ${SERVICE_NAME}" + + # Find the kurtosis container for the service + # It finds the most recent container whose name starts with ${SERVICE_NAME}-- and stores its name in CONTAINER + CONTAINER="$(docker ps -a --format '{{.Names}}' | grep -E "^${SERVICE_NAME}--" | head -n1 || true)" + + # Run and stream output to both terminal and log + { + echo "---- ${START_TS} BEGIN ${SERVICE_NAME} ----" + # checks if no service was found and exits + if [[ -z "${CONTAINER}" ]]; then + echo "No container found matching \"${SERVICE_NAME}--*\"" >&2 + echo "---- $(date -Is) END ${SERVICE_NAME} rc=127 ----" + exit 127 + fi + + # continues execution if service was found + echo "Using container: ${CONTAINER}" + + # Copy local script into the container + REMOTE_SCRIPT="/tmp/check_verification.sh" + echo "Copying ${LOCAL_SCRIPT} -> ${CONTAINER}:${REMOTE_SCRIPT}" + docker cp "${LOCAL_SCRIPT}" "${CONTAINER}:${REMOTE_SCRIPT}" + + # Patch placeholders from combined.json and execute + docker exec -e RPC_URL="${RPC_URL}" "${CONTAINER}" bash -lc ' + set -euo pipefail + FILE=/opt/zkevm/combined.json + [[ -f "$FILE" ]] || { echo "Missing $FILE"; exit 1; } + + if command -v jq >/dev/null 2>&1; then + ROLLUP_MANAGER=$(jq -r ".polygonRollupManagerAddress" "$FILE") + else + ROLLUP_MANAGER=$(grep -oP "\"polygonRollupManagerAddress\"\\s*:\\s*\"\\K0x[0-9a-fA-F]+" "$FILE") + fi + + [[ -n "$ROLLUP_MANAGER" ]] || { echo "Could not parse polygonRollupManagerAddress"; exit 1; } + + + echo "ROLLUP_MANAGER=$ROLLUP_MANAGER" + + sed -i "s|__ROLLUP_MANAGER__|$ROLLUP_MANAGER|g" "'"$REMOTE_SCRIPT"'" + chmod +x "'"$REMOTE_SCRIPT"'" + "'"$REMOTE_SCRIPT"'" + ' + RC=$? # saves exits status of prev execution + echo "---- $(date -Is) END ${SERVICE_NAME} rc=${RC} ----" + exit "$RC" + } 2>&1 | tee -a "$LOG_FILE". # stdout & stderr from the prev cmd, pipes it & appends to log file shows also on terminal. + + # Preserve the exit code of the block (left side of the pipe) + RC=${PIPESTATUS[0]} # captures the exit code of the first command in the pipeline + END_TS="$(date -Is)" + if [[ $RC -eq 0 ]]; then + echo "[FINISH ${END_TS}] ${SERVICE_NAME} ✅ (rc=0) | log: ${LOG_FILE}" + elif [[ $RC -eq 2 ]]; then + echo "[FINISH ${END_TS}] ${SERVICE_NAME} ⚠️ No verification events (rc=2) | log: ${LOG_FILE}" + else + echo "[FINISH ${END_TS}] ${SERVICE_NAME} ❌ (rc=${RC}) | log: ${LOG_FILE}" + fi + exit "$RC" +) + + +# ---------------------------------------------------------------------- +# Load environment +# ---------------------------------------------------------------------- + +if [ -f .env ]; then + source .env +else + echo ".env not found — shell exiting." + exit 1 +fi + + +# ------------------------------------------------------------------------------ +# CLI Arguments +# ────────────────────────────────────────────────────────────────────────────── +# Usage: ./run.sh [action] +# e.g. 0.3.0-rc.21 +# e.g. 0.3.5 +# [1] optional: perform upgrade from kurtosis base image (either 1(kurtosis base image) or 2(base image from cli)) +# [action] optional: perform downgrade back to +# Exit immediately if no args provided +if [[ $# -lt 2 ]]; then + echo "Error: Missing required arguments." + echo "Usage: $0 [action]" + exit 1 +fi + + +PREV_FROM_TAG="$FROM_TAG" +PREV_TO_TAG="$TO_TAG" + + + +FROM_TAG="$1" +TO_TAG="$2" +ACTION="${3:-upgrade}" + + +if [[ "$ACTION" == "downgrade" ]]; then + if [[ "$TO_TAG" == "$PREV_FROM_TAG" && "$FROM_TAG" == "$PREV_TO_TAG" ]]; then + echo "Downgrade tags match previous upgrade." + else + echo "Downgrade tag mismatch!" + echo "Expected FROM_TAG=$PREV_TO_TAG, TO_TAG=$PREV_FROM_TAG" + echo "Got FROM_TAG=$FROM_TAG, TO_TAG=$TO_TAG" + exit 1 + fi +fi + +# Compose full image references +IMAGE_BASE="ghcr.io/agglayer/agglayer" +FROM_IMAGE="${IMAGE_BASE}:${FROM_TAG}" +TO_IMAGE="${IMAGE_BASE}:${TO_TAG}" + + + + +sed -i "s#^FROM_TAG=.*#FROM_TAG=$FROM_TAG#" .env +sed -i "s#^TO_TAG=.*#TO_TAG=$TO_TAG#" .env + + +KURTOSIS_HASH="$KURTOSIS_PACKAGE_HASH" +KURTOSIS_ENCLAVE_NAME="$ENCLAVE_NAME" + + + +echo ":-kurtosis hash:= $KURTOSIS_HASH" +echo ":-enclave name:= $KURTOSIS_ENCLAVE_NAME" +echo ":-from image:= $FROM_IMAGE" +echo ":-to image:= $TO_IMAGE" +echo ":-from tag:= $FROM_TAG" +echo ":-to tag:= $TO_TAG" +echo ":-action:= $ACTION" + + + + + + + +# yq -y --arg sp1key "$SP1_NETWORK_KEY" ' +# .args.sp1_prover_key = $sp1key +# ' ./assets/opgeth-soverign.yml > initial-opgeth-soverign.yml + +# yq -y --arg sp1key "$SP1_NETWORK_KEY" ' +# .args.agglayer_prover_sp1_key = $sp1key +# ' ./assets/opgeth-ecdsa.yml > initial-opgeth-ecdsa.yml + + +# yq -y --arg sp1key "$SP1_NETWORK_KEY" ' +# .args.agglayer_prover_sp1_key = $sp1key | +# .args.sp1_prover_key = $sp1key +# ' ./assets/erigon-sovereign.yml > initial-erigon-sovereign.yml + + +export SP1_NETWORK_KEY="$SP1_NETWORK_KEY" +yq e '.args.sp1_prover_key = strenv(SP1_NETWORK_KEY)' ./assets/opgeth-soverign.yml > initial-opgeth-soverign.yml + +yq e '.args.agglayer_prover_sp1_key = strenv(SP1_NETWORK_KEY)' ./assets/opgeth-ecdsa.yml > initial-opgeth-ecdsa.yml + +yq e '.args.agglayer_prover_sp1_key = strenv(SP1_NETWORK_KEY) | .args.sp1_prover_key = strenv(SP1_NETWORK_KEY)' ./assets/erigon-sovereign.yml > initial-erigon-sovereign.yml + + + + +# checks if the user is requesting for downgrade +if [[ "$ACTION" == "downgrade" ]]; then + + # check if there is a running enclave + ENCLAVE_EXIST=$(kurtosis enclave ls | awk '$3 == "RUNNING" {print $2; exit}') + if [[ "$ENCLAVE_EXIST" != "$KURTOSIS_ENCLAVE_NAME" ]]; then + echo "Enclave name is not $KURTOSIS_ENCLAVE_NAME. Exiting...OR No Running enclave " + exit 1 + fi + + echo '╔═══════════════════════════════════════════════════════════════════════════════════════════════════════╗' + echo '║ R U N N I N G D O W N G R A D E F O R A G G L A Y E R F R O M S U P L I E D T A G ║' + echo '╚═══════════════════════════════════════════════════════════════════════════════════════════════════════╝' + + + + TO_IMAGE_SERVICE_CONFIG_FILE=$(mktemp) + kurtosis service inspect cdk agglayer --output json \ + | jq --arg img "$TO_IMAGE" '.image = $img' > "$TO_IMAGE_SERVICE_CONFIG_FILE" + + echo "$TO_IMAGE_SERVICE_CONFIG_FILE" + cat "$TO_IMAGE_SERVICE_CONFIG_FILE" + + kurtosis service rm "$KURTOSIS_ENCLAVE_NAME" agglayer + kurtosis service add cdk agglayer --json-service-config "$TO_IMAGE_SERVICE_CONFIG_FILE" + rm "$TO_IMAGE_SERVICE_CONFIG_FILE" + + + + + TO_PROVER_IMAGE_SERVICE_CONFIG_FILE=$(mktemp) + kurtosis service inspect cdk agglayer-prover --output json \ + | jq --arg img "$TO_IMAGE" '.image = $img' > "$TO_PROVER_IMAGE_SERVICE_CONFIG_FILE" + kurtosis service add cdk agglayer-prover --json-service-config "$TO_PROVER_IMAGE_SERVICE_CONFIG_FILE" + rm "$TO_PROVER_IMAGE_SERVICE_CONFIG_FILE" + + + + echo "D O W N G R A D E D: C O N F I R M I N G R U N N I N G A G G L A Y E R W I T H T A R G E T D O W N G R A D E V E R S I O N: $TO_TAG " + echo "==========================================================================================================================================" + kurtosis service inspect cdk agglayer --output json + echo "==========================================================================================================================================" + + + echo '╔════════════════════════════════════════════════════════════════════════════════════════════════════════════╗' + echo '║ D O W N G R A D I N G A G G L A Y E R S U C C E S S F U L L ║' + echo '╚════════════════════════════════════════════════════════════════════════════════════════════════════════════╝' + + echo " Running Updating Agglayer with Rollup ERIGON ROLLUP RPC NODE" + add_rollup_rpc_to_agglayer + +else + + echo '╔═════════════════════════════════════════════════════════════════════════════╗' + echo '║ A T T A C H I N G C D K - O P G E T H P E R S S I M I S T I C ║' + echo '╚═════════════════════════════════════════════════════════════════════════════╝' + + + kurtosis run \ + --enclave "$KURTOSIS_ENCLAVE_NAME" \ + --args-file ./initial-opgeth-soverign.yml \ + "github.com/0xPolygon/kurtosis-cdk@$KURTOSIS_HASH" + + + + + echo '╔══════════════════════════════════════════════════════════════════════════════════════════════════╗' + echo '║ R U N N I N G U P G R A D E F O R A G G L A Y E R F R O M S U P L I E D T A G ║' + echo '╚══════════════════════════════════════════════════════════════════════════════════════════════════╝' + + + + echo "==================== R U N N I N G K U R T O S I S W I T H A G G L A Y E R F R O M I M A G E: $FROM_IMAGE ============" + # 1. Create a temporary file to hold the config json of the current kurtosis base image + FROM_IMAGE_SERVICE_CONFIG_FILE=$(mktemp) + + # 2. Dump the inspected JSON, update the image, and save to the temp file + kurtosis service inspect cdk agglayer --output json \ + | jq --arg img "$FROM_IMAGE" '.image = $img' > "$FROM_IMAGE_SERVICE_CONFIG_FILE" + + echo "$FROM_IMAGE_SERVICE_CONFIG_FILE" + cat "$FROM_IMAGE_SERVICE_CONFIG_FILE" + + kurtosis service rm "$KURTOSIS_ENCLAVE_NAME" agglayer + kurtosis service add cdk agglayer --json-service-config "$FROM_IMAGE_SERVICE_CONFIG_FILE" + + rm "$FROM_IMAGE_SERVICE_CONFIG_FILE" + + + + FROM_PROVER_IMAGE_SERVICE_CONFIG_FILE=$(mktemp) + echo "AGGLAYER - PROVER CONFIG JSON" + kurtosis service inspect cdk agglayer-prover --output json \ + | jq --arg img "$FROM_IMAGE" '.image = $img' > "$FROM_PROVER_IMAGE_SERVICE_CONFIG_FILE" + kurtosis service add cdk agglayer-prover --json-service-config "$FROM_PROVER_IMAGE_SERVICE_CONFIG_FILE" + rm "$FROM_PROVER_IMAGE_SERVICE_CONFIG_FILE" + + + + echo "==================== R U N N I N G K U R T O S I S W I T H A G G L A Y E R T O I M A G E: $TO_IMAGE =================" + # 1. Create a temporary file to hold the config json of the current service + TO_IMAGE_SERVICE_CONFIG_FILE=$(mktemp) + + # 2. Dump the inspected JSON, update the image, and save to the temp file + kurtosis service inspect cdk agglayer --output json \ + | jq --arg img "$TO_IMAGE" '.image = $img' > "$TO_IMAGE_SERVICE_CONFIG_FILE" + + echo "$TO_IMAGE_SERVICE_CONFIG_FILE" + cat "$TO_IMAGE_SERVICE_CONFIG_FILE" + + kurtosis service rm "$KURTOSIS_ENCLAVE_NAME" agglayer + kurtosis service add cdk agglayer --json-service-config "$TO_IMAGE_SERVICE_CONFIG_FILE" + + rm "$TO_IMAGE_SERVICE_CONFIG_FILE" + + + TO_PROVER_IMAGE_SERVICE_CONFIG_FILE=$(mktemp) + kurtosis service inspect cdk agglayer-prover --output json \ + | jq --arg img "$TO_IMAGE" '.image = $img' > "$TO_PROVER_IMAGE_SERVICE_CONFIG_FILE" + kurtosis service add cdk agglayer-prover --json-service-config "$TO_PROVER_IMAGE_SERVICE_CONFIG_FILE" + rm "$TO_PROVER_IMAGE_SERVICE_CONFIG_FILE" + + + + echo "C O N F I R M I N G R U N N I N G A G G L A Y E R F R O M S U P P L I E D B A S E V E R S I O N: $TO_TAG " + echo "========================================================================================================================" + kurtosis service inspect cdk agglayer --output json + echo "========================================================================================================================" + + + + echo '╔══════════════════════════════════════════════════════════════════════════════════════════════════════════╗' + echo '║ U P G R A D I N G A G G L A Y E R T O T A R G E T V E R S I O N S U C C E S S F U L L ║' + echo '╚══════════════════════════════════════════════════════════════════════════════════════════════════════════╝' + + + + echo '╔═════════════════════════════════════════════════════════════════════════════╗' + echo '║ A T T A C H I N G C D K - O P G E T H E C D S A ║' + echo '╚═════════════════════════════════════════════════════════════════════════════╝' + + + kurtosis run \ + --enclave "$KURTOSIS_ENCLAVE_NAME" \ + --args-file ./initial-opgeth-ecdsa.yml \ + "github.com/0xPolygon/kurtosis-cdk@$KURTOSIS_HASH" + + + echo '╔═════════════════════════════════════════════════════════════════════════════╗' + echo '║ A T T A C H I N G C D K - E R I G O N S O V E R E I G N ║' + echo '╚═════════════════════════════════════════════════════════════════════════════╝' + + kurtosis run \ + --enclave "$KURTOSIS_ENCLAVE_NAME" \ + --args-file ./initial-erigon-sovereign.yml \ + "github.com/0xPolygon/kurtosis-cdk@$KURTOSIS_HASH" + + + + echo "Modifying agglayer configuration to include new rollup" + add_rollup_rpc_to_agglayer + + + # live countdown for 3 minutes + for ((s=180; s>0; s--)); do + printf "\rWaiting 3 min to verify deployment - will execute VerifyBatchesTrustedAggregator event… %02d:%02d" $((s/60)) $((s%60)) + sleep 1 + done + printf "\r✓ done. \n" + + for n in 001 002 003; do + run_verification_in_container "contracts-$n" || true + done +fi + + + + +# native run +LOG_FILE="logs/run_native_bridging_$(date +%F).log" +PID_FILE="logs/run_native_bridging.pid" + +nohup ./lxly.sh native >>"$LOG_FILE" 2>&1 & +echo "Started native → $LOG_FILE (PID $(cat logs/run_native_bridging.pid))" +echo $! > "$PID_FILE" +echo "Started ./lxly.sh native (PID $(cat "$PID_FILE")) → $LOG_FILE" + +# erc20 roundtrip +LOG_FILE="logs/run_roundtrip_bridging_$(date +%F).log" +PID_FILE="logs/run_roundtrip_bridging.pid" + +nohup ./lxly.sh erc20-roundtrip >>"$LOG_FILE" 2>&1 & +echo "Started erc20-roundtrip → $LOG_FILE_RT (PID $(cat logs/run_roundtrip_bridging.pid))" +echo $! > "$PID_FILE" +echo "Started ./lxly.sh erc20-roundtrip (PID $(cat "$PID_FILE")) → $LOG_FILE" + +echo "Output of bridging can be found in teh /logs folder you need to wait for it to finish executing" \ No newline at end of file