Daily Sepolia Health Check #639
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Daily Sepolia Health Check | |
| on: | |
| schedule: | |
| - cron: '0 8 * * *' # Daily at 08:00 UTC | |
| pull_request: | |
| # `labeled` lets adding the `run-e2e` label trigger a full run on demand. | |
| types: [opened, synchronize, reopened, labeled] | |
| workflow_dispatch: | |
| inputs: | |
| network: | |
| description: 'Network to run the E2E tests against' | |
| type: choice | |
| required: true | |
| default: sepolia | |
| options: | |
| - sepolia | |
| - mainnet | |
| permissions: | |
| contents: read | |
| # The on-chain e2e must serialize on the shared Sepolia account (nonce safety), | |
| # so real runs share one queue. Skip-fast PR runs — which only report the | |
| # required check and exit — use a per-ref group so they never wait behind a | |
| # 45-min run. | |
| concurrency: | |
| group: >- | |
| ${{ (github.event_name == 'schedule' | |
| || github.event_name == 'workflow_dispatch' | |
| || contains(github.event.pull_request.labels.*.name, 'run-e2e')) | |
| && 'sepolia-e2e-account' | |
| || format('e2e-skip-{0}', github.ref) }} | |
| cancel-in-progress: false | |
| # Version pins — bump these to force a rebuild of cached deps. | |
| env: | |
| SEQUENCER_TAG: e6b6fd2e9932909107833579e5b6efd6c75fa0af | |
| PROVING_UTILS_REV: 0a97f45a15809e7b5a821b7adc49212af7b399f1 | |
| STWO_NIGHTLY: nightly-2026-01-15 | |
| EXPECTED_VIRTUAL_OS_PROGRAM_HASH: '0x53f6c9fcfd31d27279ff7d7e422b44623550a732b59fe193354a7316a96daa1' | |
| SCARB_VERSION: 2.15.0 | |
| STARKNET_FOUNDRY_VERSION: 0.60.0 | |
| # The prebuilt deps release tag is read from the deps-version file at the | |
| # repo root (single source of truth; see RELEASING.md). | |
| # The on-chain e2e is NOT a per-PR gate (fast build/test live in ci.yml). It | |
| # runs on the daily schedule, on manual dispatch, and on PRs labelled | |
| # `run-e2e`. On any other PR the job only reports the required check and exits. | |
| RUN_E2E: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'run-e2e') }} | |
| jobs: | |
| e2e-test: | |
| runs-on: ubuntu-latest | |
| # Keep enough room for the source-build fallback when a deps-v* pin is | |
| # bumped before the matching prebuilt release/cache exists. | |
| timeout-minutes: 90 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Check version sync | |
| run: ./scripts/check-versions.sh | |
| # Validate the release metadata before consulting the dependency cache. | |
| # A cache key containing the source pins does not prove that the cached | |
| # prebuilt release was produced from those revisions. | |
| - name: Validate pinned deps release provenance | |
| if: env.RUN_E2E == 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| ./scripts/check-deps-release.sh \ | |
| "$(cat deps-version)" \ | |
| "$SEQUENCER_TAG" \ | |
| "$PROVING_UTILS_REV" \ | |
| "$STWO_NIGHTLY" \ | |
| "${{ github.repository }}" | |
| - name: Install Rust toolchain | |
| if: env.RUN_E2E == 'true' | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Install stwo nightly toolchain | |
| if: env.RUN_E2E == 'true' | |
| run: rustup toolchain install ${{ env.STWO_NIGHTLY }} | |
| - name: Cache cargo artifacts | |
| if: env.RUN_E2E == 'true' | |
| uses: actions/cache@v6 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| - name: Build snip36 CLI | |
| if: env.RUN_E2E == 'true' | |
| run: cargo build --release -p snip36-cli | |
| - name: Install starknet-foundry (sncast) | |
| if: env.RUN_E2E == 'true' | |
| shell: bash | |
| run: | | |
| curl -L https://raw.githubusercontent.com/foundry-rs/starknet-foundry/master/scripts/install.sh | sh | |
| source "$HOME/.bashrc" 2>/dev/null || true | |
| export PATH="$HOME/.local/bin:$HOME/.starknet-foundry/bin:$PATH" | |
| snfoundryup --version "$STARKNET_FOUNDRY_VERSION" | |
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" | |
| echo "$HOME/.starknet-foundry/bin" >> "$GITHUB_PATH" | |
| - name: Install scarb | |
| if: env.RUN_E2E == 'true' | |
| uses: software-mansion/setup-scarb@v1 | |
| with: | |
| scarb-version: ${{ env.SCARB_VERSION }} | |
| - name: Set up Python 3.12 | |
| if: env.RUN_E2E == 'true' | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.12' | |
| - name: Verify tools | |
| if: env.RUN_E2E == 'true' | |
| run: | | |
| ./snip36 --help | |
| sncast --version | |
| scarb --version | |
| python3 --version | |
| # Cache the heavy deps: prebuilt binaries + cairo-compile venv. Key | |
| # includes the pins so it rebuilds when they change. | |
| - name: Cache prebuilt deps | |
| id: deps-cache | |
| if: env.RUN_E2E == 'true' | |
| uses: actions/cache@v6 | |
| with: | |
| path: | | |
| deps | |
| sequencer_venv | |
| key: ${{ runner.os }}-deps-${{ env.SEQUENCER_TAG }}-${{ env.PROVING_UTILS_REV }}-${{ env.STWO_NIGHTLY }}-${{ hashFiles('deps-version') }} | |
| save-always: true | |
| - name: Provision deps (prebuilt release, source-build fallback) | |
| if: env.RUN_E2E == 'true' && steps.deps-cache.outputs.cache-hit != 'true' | |
| run: | | |
| set -uo pipefail | |
| DEPS_RELEASE_TAG="$(cat deps-version)" | |
| # Fast path: download the pinned prebuilt deps release (~30s) and | |
| # install cairo-lang into the venv download-deps.sh creates (it can't | |
| # on its own — it has no sequencer checkout). Fall back to a full | |
| # from-source build (~30 min) if anything is missing or mismatched. | |
| if ./scripts/download-deps.sh "$DEPS_RELEASE_TAG" \ | |
| && curl -fsSL "https://raw.githubusercontent.com/starkware-libs/sequencer/${SEQUENCER_TAG}/scripts/requirements.txt" -o /tmp/seq-requirements.txt \ | |
| && sequencer_venv/bin/pip install --quiet -r /tmp/seq-requirements.txt \ | |
| && [ -x sequencer_venv/bin/cairo-compile ] \ | |
| && [ -x deps/sequencer/target/release/starknet_transaction_prover ] \ | |
| && [ -n "$(find deps/compiler-tools -path '*/bin/starknet-sierra-compile' -type f -perm -111 | head -n 1)" ] \ | |
| && [ -x deps/bin/stwo-run-and-prove ]; then | |
| echo "Provisioned deps from prebuilt release $DEPS_RELEASE_TAG" | |
| else | |
| echo "Prebuilt provisioning incomplete; cleaning and building from source" | |
| rm -rf deps sequencer_venv | |
| ./snip36 setup | |
| fi | |
| - name: Probe Starknet RPC from runner | |
| if: env.RUN_E2E == 'true' | |
| env: | |
| STARKNET_RPC_URL: ${{ inputs.network == 'mainnet' && secrets.MAINNET_STARKNET_RPC_URL || secrets.SEPOLIA_STARKNET_RPC_URL }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| print_body_prefix() { | |
| local body="$1" | |
| head -c 300 "$body" | tr '\n' ' ' | |
| echo | |
| } | |
| rpc_call() { | |
| local method="$1" | |
| local params="${2:-[]}" | |
| local body meta status content_type | |
| body="$(mktemp)" | |
| meta="$( | |
| jq -nc --arg method "$method" --argjson params "$params" \ | |
| '{jsonrpc:"2.0",id:1,method:$method,params:$params}' | | |
| curl -sS -o "$body" -w '%{http_code} %{content_type}' \ | |
| -H 'Content-Type: application/json' \ | |
| --data @- \ | |
| "$STARKNET_RPC_URL" || true | |
| )" | |
| status="${meta%% *}" | |
| content_type="${meta#* }" | |
| echo "$method: HTTP ${status:-unknown}, content-type: ${content_type:-unknown}" | |
| if [[ ! "$status" =~ ^[0-9][0-9][0-9]$ ]]; then | |
| echo "$method: curl did not return a valid HTTP status" | |
| print_body_prefix "$body" | |
| rm -f "$body" | |
| return 1 | |
| fi | |
| if (( status < 200 || status >= 300 )); then | |
| echo "$method: non-2xx response body prefix:" | |
| print_body_prefix "$body" | |
| rm -f "$body" | |
| return 1 | |
| fi | |
| if ! jq -e . "$body" >/dev/null 2>&1; then | |
| echo "$method: non-JSON response body prefix:" | |
| print_body_prefix "$body" | |
| rm -f "$body" | |
| return 1 | |
| fi | |
| if jq -e '.error' "$body" >/dev/null; then | |
| jq -c '{error: .error}' "$body" | |
| else | |
| jq -c '{result: .result}' "$body" | |
| fi | |
| rm -f "$body" | |
| } | |
| rpc_call starknet_chainId | |
| rpc_call starknet_blockNumber | |
| echo "Probing write-method response shape with an intentionally invalid transaction." | |
| invalid_invoke_params="$(cat <<'JSON' | |
| { | |
| "invoke_transaction": { | |
| "type": "INVOKE", | |
| "version": "0x3", | |
| "sender_address": "0x1", | |
| "calldata": [], | |
| "signature": [], | |
| "nonce": "0x0", | |
| "resource_bounds": { | |
| "l1_gas": { | |
| "max_amount": "0x0", | |
| "max_price_per_unit": "0x0" | |
| }, | |
| "l2_gas": { | |
| "max_amount": "0x0", | |
| "max_price_per_unit": "0x0" | |
| }, | |
| "l1_data_gas": { | |
| "max_amount": "0x0", | |
| "max_price_per_unit": "0x0" | |
| } | |
| }, | |
| "tip": "0x0", | |
| "paymaster_data": [], | |
| "account_deployment_data": [], | |
| "nonce_data_availability_mode": "L1", | |
| "fee_data_availability_mode": "L1" | |
| } | |
| } | |
| JSON | |
| )" | |
| rpc_call starknet_addInvokeTransaction "$invalid_invoke_params" | |
| - name: Run E2E counter test | |
| if: env.RUN_E2E == 'true' | |
| env: | |
| STARKNET_RPC_URL: ${{ inputs.network == 'mainnet' && secrets.MAINNET_STARKNET_RPC_URL || secrets.SEPOLIA_STARKNET_RPC_URL }} | |
| STARKNET_ACCOUNT_ADDRESS: ${{ inputs.network == 'mainnet' && secrets.MAINNET_STARKNET_ACCOUNT_ADDRESS || secrets.STARKNET_ACCOUNT_ADDRESS }} | |
| STARKNET_PRIVATE_KEY: ${{ inputs.network == 'mainnet' && secrets.MAINNET_STARKNET_PRIVATE_KEY || secrets.STARKNET_PRIVATE_KEY }} | |
| STARKNET_GATEWAY_URL: ${{ inputs.network == 'mainnet' && secrets.MAINNET_STARKNET_GATEWAY_URL || secrets.STARKNET_GATEWAY_URL }} | |
| STARKNET_CHAIN_ID: ${{ inputs.network == 'mainnet' && 'SN_MAIN' || 'SN_SEPOLIA' }} | |
| run: ./snip36 e2e | |
| - name: Verify virtual-OS program hash | |
| if: env.RUN_E2E == 'true' | |
| run: | | |
| set -euo pipefail | |
| facts_file="$(find output/e2e -type f -name '*.proof_facts' -print -quit)" | |
| if [ -z "$facts_file" ]; then | |
| echo "Counter E2E did not produce a proof_facts file" >&2 | |
| exit 1 | |
| fi | |
| actual="$(jq -er '.[2]' "$facts_file")" | |
| if [ "$actual" != "$EXPECTED_VIRTUAL_OS_PROGRAM_HASH" ]; then | |
| echo "Unexpected virtual-OS program hash" >&2 | |
| echo " expected: $EXPECTED_VIRTUAL_OS_PROGRAM_HASH" >&2 | |
| echo " actual: $actual" >&2 | |
| exit 1 | |
| fi | |
| echo "Virtual-OS program hash verified: $actual" | |
| - name: Run E2E messages test | |
| if: env.RUN_E2E == 'true' | |
| env: | |
| STARKNET_RPC_URL: ${{ inputs.network == 'mainnet' && secrets.MAINNET_STARKNET_RPC_URL || secrets.SEPOLIA_STARKNET_RPC_URL }} | |
| STARKNET_ACCOUNT_ADDRESS: ${{ inputs.network == 'mainnet' && secrets.MAINNET_STARKNET_ACCOUNT_ADDRESS || secrets.STARKNET_ACCOUNT_ADDRESS }} | |
| STARKNET_PRIVATE_KEY: ${{ inputs.network == 'mainnet' && secrets.MAINNET_STARKNET_PRIVATE_KEY || secrets.STARKNET_PRIVATE_KEY }} | |
| STARKNET_GATEWAY_URL: ${{ inputs.network == 'mainnet' && secrets.MAINNET_STARKNET_GATEWAY_URL || secrets.STARKNET_GATEWAY_URL }} | |
| STARKNET_CHAIN_ID: ${{ inputs.network == 'mainnet' && 'SN_MAIN' || 'SN_SEPOLIA' }} | |
| run: ./snip36 e2e-messages |