Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ Start a local **L1 (Anvil)** and **L2 (zksync_os_server)** directly from officia

## Features

- Downloads official binaries + state (`genesis.json`, `zkos-l1-state.json`)
- Downloads official binaries + `local-chains.tar.gz`
- Uses protocol-aware local chains from `./local-chains/<protocol_version>`
- Falls back to legacy assets for older tags that do not include `local-chains.tar.gz`
- Boots both **L1 (Anvil)** and **L2 (zksync_os_server)**
- Exports `ETH_RPC` and `ZKSYNC_RPC` for your subsequent test steps

Expand All @@ -31,6 +33,7 @@ Example setup:
uses: dutterbutter/zksync-server-action@v0.1.0
with:
version: latest
protocol_version: v31.0
```

### **Pinned version**
Expand Down Expand Up @@ -61,8 +64,9 @@ Example setup:
| `version` | `latest` | Release tag (e.g. `v0.8.2`) or `latest` |
| `include_prerelease` | `false` | If `true` and `version=latest`, allows pre-releases |
| `l1_port` | `8545` | L1 RPC port (Anvil) |
| `l2_port` | `3050` | L2 RPC port (zksync_os_server) |
| `l2_port` | `3050` | L2 RPC port (zksync_os_server) |
| `linux_arch` | `x86_64` | Architecture for binary (`x86_64` or `aarch64`) |
| `protocol_version` | `v31.0` | Protocol folder under `local-chains` (e.g. `v30.2`, `v31.0`) for `v0.15.0+` |
| `set_env` | `true` | Export `ETH_RPC` and `ZKSYNC_RPC` to `GITHUB_ENV` |
| `anvil_logs` | `false` | Print Anvil log (`.zks/anvil.log`) at the end |
| `zksync_logs` | `false` | Print zksync-os-server log (`.zks/zksyncos.log`) at the end |
Expand All @@ -79,7 +83,12 @@ Example setup:

### Configuration inputs

This action **always starts `zksync-os-server` with an explicit config file** (`--config ./config.yaml`).
From `zksync-os-server` `v0.15.0+`, this action uses `local-chains.tar.gz` and:

- Decompresses `./local-chains/<protocol_version>/l1-state.json.gz` (or `l1.state.json.gz`) for Anvil
- Starts server with `--config ./local-chains/<protocol_version>/default/config.yaml` (unless overridden)

For tags before `v0.15.0`, it automatically falls back to legacy release assets (`zkos-l1-state.json`, `genesis.json`).

You may configure the server in one of two ways:

Expand Down Expand Up @@ -107,7 +116,7 @@ Example:

#### Option 2: Override individual operator keys

If `config_yaml` is **not** provided, the action will generate a default config and allow selective overrides of the L1 sender operator keys:
If `config_yaml` is **not** provided, the action uses the protocol default config from `local-chains` and allows selective overrides of the L1 sender operator keys:

| Name | Default | Description |
| --------------------- | --------------- | ---------------------------------------- |
Expand Down
174 changes: 137 additions & 37 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ inputs:
linux_arch:
description: Linux arch (x86_64 or aarch64)
default: x86_64
protocol_version:
description: Protocol version inside local-chains (e.g. v31.0)
default: v31.0
set_env:
description: 'If ''true'', export ETH_RPC and ZKSYNC_RPC to GITHUB_ENV'
default: 'true'
Expand Down Expand Up @@ -126,70 +129,163 @@ runs:
tag="${{ steps.resolve.outputs.tag }}"
arch="${{ inputs.linux_arch }}"
tgz="zksync-os-server-${tag}-${arch}-unknown-linux-gnu.tar.gz"
local_chains_tgz="local-chains.tar.gz"

curl -fL --retry 5 --retry-all-errors -o "$tgz" "$base/$tgz"
curl -fL --retry 5 --retry-all-errors -O "$base/zkos-l1-state.json"
curl -fL --retry 5 --retry-all-errors -O "$base/genesis.json"

mkdir -p genesis
mv genesis.json genesis/genesis.json

tar -xzf "$tgz"
chmod +x ./zksync-os-server

if curl -fL --retry 5 --retry-all-errors -o "$local_chains_tgz" "$base/$local_chains_tgz"; then
tar -xzf "$local_chains_tgz"

protocol_dir="./local-chains/${{ inputs.protocol_version }}"
if [[ ! -d "$protocol_dir" ]]; then
echo "Protocol directory '$protocol_dir' was not found in local-chains.tar.gz"
echo "Available protocol versions:"
ls -1 ./local-chains || true
exit 1
fi
else
echo "local-chains.tar.gz not found for $tag; falling back to legacy state assets."
curl -fL --retry 5 --retry-all-errors -O "$base/zkos-l1-state.json"
curl -fL --retry 5 --retry-all-errors -O "$base/genesis.json"

mkdir -p genesis
mv genesis.json genesis/genesis.json
fi
- name: Start Anvil (L1)
shell: bash
run: |
set -euo pipefail
cd .zks

nohup anvil --load-state zkos-l1-state.json \
if ! command -v anvil >/dev/null 2>&1; then
echo "Anvil binary was not found in PATH."
echo "Install Foundry before using this action (e.g. foundry-rs/foundry-toolchain@v1)."
exit 1
fi

echo "Using anvil: $(command -v anvil)"
anvil --version || true

state_json="./zkos-l1-state.json"
protocol_dir="./local-chains/${{ inputs.protocol_version }}"
if [[ -d "$protocol_dir" ]]; then
state_gz="$protocol_dir/l1-state.json.gz"

if [[ ! -f "$state_gz" ]]; then
alt_state_gz="$protocol_dir/l1.state.json.gz"
if [[ -f "$alt_state_gz" ]]; then
state_gz="$alt_state_gz"
else
echo "Missing L1 state archive. Expected one of:"
echo " $protocol_dir/l1-state.json.gz"
echo " $protocol_dir/l1.state.json.gz"
exit 1
fi
fi

gzip -dfk "$state_gz"
state_json="${state_gz%.gz}"
elif [[ ! -f "$state_json" ]]; then
echo "Missing L1 state file. Expected either protocol state under local-chains or $state_json"
exit 1
fi

STATE_PATH_ABS="$(realpath "$state_json")"
echo "Starting anvil with --load-state $STATE_PATH_ABS"

nohup anvil --load-state "$STATE_PATH_ABS" \
--port ${{ inputs.l1_port }} \
> anvil.log 2>&1 < /dev/null &

echo $! > anvil.pid
sleep "${{ inputs.boot_grace_seconds }}"

kill -0 "$(cat anvil.pid)"

- name: Write zksync-os-server config
PID="$(cat anvil.pid)"
if ! kill -0 "$PID" 2>/dev/null; then
echo "::group::anvil exited early"
echo "PID $PID is not running."
echo "Last 200 lines of anvil.log:"
tail -n 200 anvil.log || true
echo "::endgroup::"
exit 1
fi

- id: config
name: Resolve zksync-os-server config
shell: bash
run: |
set -euo pipefail
cd .zks

CONFIG_PATH="$(pwd)/config.yaml"
PROTOCOL_CONFIG="$(pwd)/local-chains/${{ inputs.protocol_version }}/default/config.yaml"

if [[ -n "${{ inputs.config_yaml }}" ]]; then
CONFIG_PATH="$(pwd)/config.yaml"
printf '%s\n' "${{ inputs.config_yaml }}" > "$CONFIG_PATH"
else
COMMIT_SK="${{ inputs.operator_commit_sk }}"
PROVE_SK="${{ inputs.operator_prove_sk }}"
EXECUTE_SK="${{ inputs.operator_execute_sk }}"

[[ -n "$COMMIT_SK" ]] || COMMIT_SK="0xe9b7176c87e267fdfea1b47f2d340a98692a2d9a32811abc9983d6d46d8745e0"
[[ -n "$PROVE_SK" ]] || PROVE_SK="0x58a7d54878fe84e74c85e9a333de3abb46203be6d38b6f7a7d72494b5d871f2f"
[[ -n "$EXECUTE_SK" ]] || EXECUTE_SK="0x31f5aa09d23d41bad736cc6e2b4332cf76cdf40c256b5b7cd660a06940ea8629"

printf '%s\n' \
'genesis:' \
' bridgehub_address: "0xb9247a375fffc63b3383c75024946fa9c3a8601f"' \
' bytecode_supplier_address: "0x194505775af2efaee14fcefe21ab0b271b61f35d"' \
" genesis_input_path: \"$(pwd)/genesis/genesis.json\"" \
' chain_id: 6565' \
'' \
'l1_sender:' \
" operator_commit_sk: \"$COMMIT_SK\"" \
" operator_prove_sk: \"$PROVE_SK\"" \
" operator_execute_sk: \"$EXECUTE_SK\"" \
'' \
'external_price_api_client:' \
' source: "Forced"' \
' forced_prices:' \
' "0x0000000000000000000000000000000000000001": 3000' \
> "$CONFIG_PATH"
if [[ -f "$PROTOCOL_CONFIG" ]]; then
CONFIG_PATH="$PROTOCOL_CONFIG"
else
CONFIG_PATH="$(pwd)/config.yaml"
printf '%s\n' \
'genesis:' \
' bridgehub_address: "0xb9247a375fffc63b3383c75024946fa9c3a8601f"' \
' bytecode_supplier_address: "0x194505775af2efaee14fcefe21ab0b271b61f35d"' \
" genesis_input_path: \"$(pwd)/genesis/genesis.json\"" \
' chain_id: 6565' \
'' \
'l1_sender:' \
' operator_commit_sk: "0xe9b7176c87e267fdfea1b47f2d340a98692a2d9a32811abc9983d6d46d8745e0"' \
' operator_prove_sk: "0x58a7d54878fe84e74c85e9a333de3abb46203be6d38b6f7a7d72494b5d871f2f"' \
' operator_execute_sk: "0x31f5aa09d23d41bad736cc6e2b4332cf76cdf40c256b5b7cd660a06940ea8629"' \
'' \
'external_price_api_client:' \
' source: "Forced"' \
' forced_prices:' \
' "0x0000000000000000000000000000000000000001": 3000' \
> "$CONFIG_PATH"
fi
fi

COMMIT_SK="${{ inputs.operator_commit_sk }}"
PROVE_SK="${{ inputs.operator_prove_sk }}"
EXECUTE_SK="${{ inputs.operator_execute_sk }}"

if [[ -n "$COMMIT_SK" || -n "$PROVE_SK" || -n "$EXECUTE_SK" ]]; then
OVERRIDE_CONFIG="$(pwd)/config.override.yaml"
cp "$CONFIG_PATH" "$OVERRIDE_CONFIG"

update_key() {
local key="$1"
local value="$2"

if ! grep -Eq "^[[:space:]]*${key}:[[:space:]]*" "$OVERRIDE_CONFIG"; then
echo "Could not find key '${key}' in $OVERRIDE_CONFIG"
exit 1
fi

sed -E -i.bak "s|^([[:space:]]*${key}:[[:space:]]*).*$|\\1\"${value}\"|" "$OVERRIDE_CONFIG"
rm -f "${OVERRIDE_CONFIG}.bak"
}

if [[ -n "$COMMIT_SK" ]]; then
update_key "operator_commit_sk" "$COMMIT_SK"
fi
if [[ -n "$PROVE_SK" ]]; then
update_key "operator_prove_sk" "$PROVE_SK"
fi
if [[ -n "$EXECUTE_SK" ]]; then
update_key "operator_execute_sk" "$EXECUTE_SK"
fi

CONFIG_PATH="$OVERRIDE_CONFIG"
fi

test -s "$CONFIG_PATH"
echo "path=$CONFIG_PATH" >> "$GITHUB_OUTPUT"

- name: Start ZKsync OS server (L2)
shell: bash
Expand All @@ -198,12 +294,16 @@ runs:
cd .zks

export L1_RPC="http://127.0.0.1:${{ inputs.l1_port }}"
export GENESIS="$(pwd)/genesis/genesis.json"
GENESIS_PATH="$(pwd)/local-chains/${{ inputs.protocol_version }}/genesis/genesis.json"
if [[ ! -f "$GENESIS_PATH" ]]; then
GENESIS_PATH="$(pwd)/genesis/genesis.json"
fi
export GENESIS="$GENESIS_PATH"
export PORT="${{ inputs.l2_port }}"
export RUST_LOG=info
export RUST_BACKTRACE=1

CONFIG_ABS="$(pwd)/config.yaml"
CONFIG_ABS="${{ steps.config.outputs.path }}"

echo "Launching: ./zksync-os-server --config $CONFIG_ABS"
setsid bash -c "exec ./zksync-os-server --config \"$CONFIG_ABS\" </dev/null >>zksyncos.log 2>&1" &
Expand Down