Skip to content
Open
6 changes: 3 additions & 3 deletions .env.devnet
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
############################### IMAGES #####################################
PACAYA_PROTOCOL_IMAGE=nethermind/catalyst-taiko-protocol:taiko-alethia-protocol-v2.3.1-new
PROTOCOL_IMAGE=nethermind/surge-protocol:sha-b85d39a
NETHERMIND_CLIENT_IMAGE=nethermindeth/nethermind:master
TAIKO_CLIENT_IMAGE=nethermind/taiko-client:pr-260-3b8a522d
NETHERMIND_CLIENT_IMAGE=nethermindeth/nethermind:master-334488b
TAIKO_CLIENT_IMAGE=nethermind/taiko-client:surge-shasta-0933c0f9
CATALYST_IMAGE=nethermind/catalyst-node:v1.30.0
RAIKO_IMAGE=nethermind/surge-raiko-zk:sha-76f481b
RAIKO_IMAGE=nethermind/surge-raiko-zk:sha-2c34626

############################### DEFAULT #####################################
CHAINSPEC_PATH_ON_HOST=./deployment/surge_chainspec.json
Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Claude Code

on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]

jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1

- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
19 changes: 17 additions & 2 deletions deploy-surge-full.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,13 @@ trap cleanup EXIT
generate_prover_chain_spec() {
log_info "Generating prover chain spec list json..."

# Sanitize "null" values from jq extraction (mock mode may not deploy real verifiers)
local zero_addr="0x0000000000000000000000000000000000000000"
[[ "${SHASTA_SP1_VERIFIER:-}" == "null" || -z "${SHASTA_SP1_VERIFIER:-}" ]] && export SHASTA_SP1_VERIFIER="$zero_addr"
[[ "${SHASTA_RISC0_VERIFIER:-}" == "null" || -z "${SHASTA_RISC0_VERIFIER:-}" ]] && export SHASTA_RISC0_VERIFIER="$zero_addr"
[[ "${PACAYA_SP1_RETH_VERIFIER:-}" == "null" || -z "${PACAYA_SP1_RETH_VERIFIER:-}" ]] && export PACAYA_SP1_RETH_VERIFIER="$zero_addr"
[[ "${PACAYA_RISC0_RETH_VERIFIER:-}" == "null" || -z "${PACAYA_RISC0_RETH_VERIFIER:-}" ]] && export PACAYA_RISC0_RETH_VERIFIER="$zero_addr"

local genesis_time
local beacon_endpoint="${L1_BEACON_HTTP_EXTERNAL:-${L1_BEACON_HTTP:-http://localhost:33001}}"
if ! genesis_time=$(curl -s "${beacon_endpoint}/eth/v1/beacon/genesis" | jq -r '.data.genesis_time' 2>/dev/null); then
Expand Down Expand Up @@ -1533,7 +1540,7 @@ generate_l2_genesis() {

update_env_var "$ENV_FILE" "SHASTA_TIMESTAMP" "$HEX_TIMESTAMP"

cat "$SURGE_GENESIS_FILE" | jq --arg hex_timestamp "$HEX_TIMESTAMP" '. * {difficulty: 0, config: {taiko: true, londonBlock: 0, ontakeBlock: 0, pacayaBlock: 1, shastaTimestamp: $hex_timestamp, useSurgeGasPriceOracle: true, feeCollector: "0x0000000000000000000000000000000000000000", shanghaiTime: 0}} | del(.config.clique)' | jq --from-file <(curl -s https://raw.githubusercontent.com/NethermindEth/core-scripts/refs/heads/main/gen2spec/gen2spec.jq) | jq --arg hex_timestamp "$HEX_TIMESTAMP" '.engine.Taiko.shastaTimestamp = $hex_timestamp' > "$DEPLOYMENT_DIR/surge_chainspec.json"
cat "$SURGE_GENESIS_FILE" | jq --arg hex_timestamp "$HEX_TIMESTAMP" '. * {difficulty: 0, config: {taiko: true, londonBlock: 0, ontakeBlock: 0, pacayaBlock: 1, shastaTimestamp: $hex_timestamp, useSurgeGasPriceOracle: true, feeCollector: "0x0000000000000000000000000000000000000000", shanghaiTime: 0}} | del(.config.clique)' | jq --from-file <(curl -s https://raw.githubusercontent.com/NethermindEth/core-scripts/refs/heads/main/gen2spec/gen2spec.jq) | jq --arg hex_timestamp "$HEX_TIMESTAMP" '.engine.Taiko.shastaTimestamp = $hex_timestamp' | jq '.params.rip7728TransitionTimestamp = "0x0"' > "$DEPLOYMENT_DIR/surge_chainspec.json"

echo
echo "╔══════════════════════════════════════════════════════════════╗"
Expand All @@ -1543,7 +1550,15 @@ generate_l2_genesis() {

log_info "Fetching genesis hash..."
# Get genesis hash first by running Nethermind with the chainspec
docker run -d --name nethermind-genesis-hash -v ./deployment/surge_chainspec.json:/chainspec.json "${NETHERMIND_CLIENT_IMAGE}" --config=none --Init.ChainSpecPath=/chainspec.json
# Pass L1 endpoint so NMC can initialize L1SLOAD (RIP-7728) when enabled in chainspec
local l1_rpc_docker="${L1_ENDPOINT_HTTP_DOCKER:-http://host.docker.internal:32003}"
docker run -d --name nethermind-genesis-hash \
--add-host=host.docker.internal:host-gateway \
-v ./deployment/surge_chainspec.json:/chainspec.json \
"${NETHERMIND_CLIENT_IMAGE}" \
--config=none \
--Init.ChainSpecPath=/chainspec.json \
--Surge.L1EthApiEndpoint="$l1_rpc_docker"

sleep 30

Expand Down
209 changes: 132 additions & 77 deletions remove-surge-full.sh
Original file line number Diff line number Diff line change
Expand Up @@ -272,126 +272,172 @@ cleanup_kurtosis_resources() {
fi
}

# Force-remove containers by name (fallback when docker compose down fails)
force_remove_containers() {
local containers=("$@")
local found=()

for name in "${containers[@]}"; do
if docker inspect "$name" >/dev/null 2>&1; then
found+=("$name")
fi
done

if [[ ${#found[@]} -gt 0 ]]; then
docker rm -f "${found[@]}" >/dev/null 2>&1 || true
fi
}

# Known L2 stack container names (from docker-compose.yml and docker-compose-protocol.yml)
readonly L2_CONTAINERS=(
surge-l2-deployer
l2-nethermind-execution-client
l2-taiko-consensus-client
web3signer-l1
web3signer-l2
l2-catalyst-node
l2-taiko-prover-relayer-client
l2-raiko-zk-client
redis-zk
l2-tx-spammer
l2-blockscout-postgres
l2-blockscout-verif
l2-blockscout
l2-blockscout-frontend
surge-l1-deployer
pacaya-deployer
surge-genesis-generator
nethermind-genesis-hash
surge-switch-fork
surge-bond-deposit
surge-accept-ownership
surge-sp1-verifier-setup
surge-risc0-verifier-setup
surge-sgx-reth-verifier-setup
surge-sgx-geth-verifier-setup
surge-proposer-wrapper-deployer
)

# Known relayer container names (from docker-compose-relayer.yml)
readonly RELAYER_CONTAINERS=(
relayer-l1-indexer
relayer-l1-processor
relayer-l1-api
relayer-l2-indexer
relayer-l2-processor
relayer-l2-api
relayer-migrations
relayer-db
relayer-rabbitmq
bridge-ui
)

# Remove L2 stack containers
remove_l2_stack() {
local mode_choice="$1"

log_info "Removing L2 stack containers..."

local exit_status=0
local temp_output="/tmp/surge_remove_l2_output_$$"


# Try docker compose down first (works when .env is present)
if [[ "$mode_choice" == "debug" ]]; then
# Debug mode: run in foreground with full output
{
docker compose --profile driver --profile proposer --profile spammer --profile prover --profile blockscout down --remove-orphans 2>&1
docker compose -f docker-compose-protocol.yml --profile l1-deployer --profile proposer-wrapper-deployer --profile sgx-reth-verifier-setup --profile sgx-geth-verifier-setup --profile sp1-verifier-setup --profile risc0-verifier-setup --profile bond-deposit --profile l2-deployer down --remove-orphans 2>&1
} | tee "$temp_output"
exit_status=${PIPESTATUS[0]}
docker compose --profile driver --profile catalyst --profile proposer --profile spammer --profile prover --profile blockscout down --remove-orphans 2>&1
docker compose -f docker-compose-protocol.yml --profile l1-deployer --profile pacaya-deployer --profile genesis-generator --profile switch-fork --profile accept-ownership --profile sgx-reth-verifier-setup --profile sgx-geth-verifier-setup --profile sp1-verifier-setup --profile risc0-verifier-setup --profile bond-deposit --profile l2-deployer down --remove-orphans 2>&1
} || true
else
# Silent mode: run in background with progress indicator
{
docker compose --profile driver --profile proposer --profile spammer --profile prover --profile blockscout down --remove-orphans 2>&1
docker compose -f docker-compose-protocol.yml --profile l1-deployer --profile proposer-wrapper-deployer --profile sgx-reth-verifier-setup --profile sgx-geth-verifier-setup --profile sp1-verifier-setup --profile risc0-verifier-setup --profile bond-deposit --profile l2-deployer down --remove-orphans 2>&1
} >"$temp_output" 2>&1 &
local remove_pid=$!

show_progress $remove_pid "Removing L2 stack containers..."

wait $remove_pid
exit_status=$?
fi

if [[ $exit_status -eq 0 ]]; then
log_success "L2 stack containers removed successfully"
return 0
else
log_error "Failed to remove L2 stack containers (exit code: $exit_status)"
if [[ "$mode_choice" == "silence" ]]; then
log_error "Run with debug mode for more details: --mode debug"
fi
if [[ -f "$temp_output" ]]; then
log_error "Removal output saved in: $temp_output"
fi
return 1
docker compose --profile driver --profile catalyst --profile proposer --profile spammer --profile prover --profile blockscout down --remove-orphans 2>&1
docker compose -f docker-compose-protocol.yml --profile l1-deployer --profile pacaya-deployer --profile genesis-generator --profile switch-fork --profile accept-ownership --profile sgx-reth-verifier-setup --profile sgx-geth-verifier-setup --profile sp1-verifier-setup --profile risc0-verifier-setup --profile bond-deposit --profile l2-deployer down --remove-orphans 2>&1
} >/dev/null 2>&1 || true
fi

# Always follow up with force-remove to catch anything compose missed (e.g. .env was gone)
force_remove_containers "${L2_CONTAINERS[@]}"

log_success "L2 stack containers removed"
return 0
}

# Remove relayer containers
remove_relayers() {
local mode_choice="$1"

log_info "Removing relayer containers..."

local exit_status=0
local temp_output="/tmp/surge_remove_relayers_output_$$"


# Try docker compose down first
if [[ "$mode_choice" == "debug" ]]; then
# Debug mode: run in foreground with full output
{
docker compose -f docker-compose-relayer.yml --profile relayer-l1 --profile relayer-l2 --profile relayer-api --profile bridge-ui down --remove-orphans 2>&1
docker compose -f docker-compose-relayer.yml --profile relayer-init --profile relayer-migrations down --remove-orphans 2>&1
} | tee "$temp_output"
exit_status=${PIPESTATUS[0]}
} || true
else
# Silent mode: run in background with progress indicator
{
docker compose -f docker-compose-relayer.yml --profile relayer-l1 --profile relayer-l2 --profile relayer-api --profile bridge-ui down --remove-orphans 2>&1
docker compose -f docker-compose-relayer.yml --profile relayer-init --profile relayer-migrations down --remove-orphans 2>&1
} >"$temp_output" 2>&1 &
local remove_pid=$!

show_progress $remove_pid "Removing relayer containers..."

wait $remove_pid
exit_status=$?
} >/dev/null 2>&1 || true
fi

if [[ $exit_status -eq 0 ]]; then
log_success "Relayer containers removed successfully"

# Force-remove to catch anything compose missed
force_remove_containers "${RELAYER_CONTAINERS[@]}"

log_success "Relayer containers removed"
return 0
}

# Remove a directory, falling back to docker-based removal for root-owned files
remove_dir_force() {
local dir="$1"
local abs_dir
abs_dir="$(cd "$(dirname "$dir")" && pwd)/$(basename "$dir")"
local parent_dir
parent_dir="$(dirname "$abs_dir")"
local base_name
base_name="$(basename "$abs_dir")"

# Try normal rm first
if rm -rf "$dir" 2>/dev/null && [[ ! -d "$dir" ]]; then
return 0
else
log_error "Failed to remove relayer containers (exit code: $exit_status)"
if [[ "$mode_choice" == "silence" ]]; then
log_error "Run with debug mode for more details: --mode debug"
fi
if [[ -f "$temp_output" ]]; then
log_error "Removal output saved in: $temp_output"
fi
return 1
fi

# Fallback: mount the parent dir so we can delete the target dir entirely
log_info "Using Docker to remove root-owned directory: $dir"
if docker run --rm -v "$parent_dir:/host_parent" alpine rm -rf "/host_parent/$base_name" 2>/dev/null; then
[[ ! -d "$dir" ]] && return 0
fi

return 1
}

# Remove persistent data directories
remove_data() {
log_info "Removing persistent data directories..."

local removed_dirs=()
local failed_dirs=()

for dir in "${DATA_DIRS[@]}"; do
if [[ -d "$dir" ]]; then
if rm -rf "$dir" 2>/dev/null; then
if remove_dir_force "$dir"; then
removed_dirs+=("$dir")
else
failed_dirs+=("$dir")
fi
fi
done

if [[ ${#removed_dirs[@]} -gt 0 ]]; then
log_success "Removed data directories: ${removed_dirs[*]}"
fi

if [[ ${#failed_dirs[@]} -gt 0 ]]; then
log_error "Failed to remove data directories: ${failed_dirs[*]}"
return 1
fi

if [[ ${#removed_dirs[@]} -eq 0 ]]; then
log_info "No data directories found to remove"
fi

return 0
}

Expand Down Expand Up @@ -668,14 +714,19 @@ main() {
has_relayers=true
fi

# Get component selection if not specified
local components_to_remove
if [[ -z "${remove_l1_devnet:-}${remove_l2_stack:-}${remove_relayers:-}${remove_data:-}${remove_configs:-}${remove_env:-}" ]]; then
# Get component selection
if [[ "$force" == "true" ]]; then
# With --force, default unset flags to "true" (remove everything)
[[ -z "$remove_l1_devnet" ]] && remove_l1_devnet="true"
[[ -z "$remove_l2_stack" ]] && remove_l2_stack="true"
[[ -z "$remove_relayers" ]] && remove_relayers="true"
[[ -z "$remove_data" ]] && remove_data="true"
[[ -z "$remove_configs" ]] && remove_configs="true"
# Note: remove_env is NOT defaulted — only removed when explicitly requested
elif [[ -z "${remove_l1_devnet:-}${remove_l2_stack:-}${remove_relayers:-}${remove_data:-}${remove_configs:-}${remove_env:-}" ]]; then
# Interactive mode: prompt for component selection
local components_to_remove
components_to_remove=$(prompt_component_selection)
fi

# Parse component selection
if [[ -n "${components_to_remove:-}" ]]; then
local COMPONENTS=()
IFS=',' read -ra COMPONENTS <<< "$components_to_remove"
for component in "${COMPONENTS[@]}"; do
Expand All @@ -693,7 +744,11 @@ main() {
# Get mode choice
local mode_choice
if [[ -z "${mode:-}" ]]; then
mode_choice=$(prompt_mode_selection)
if [[ "$force" == "true" ]]; then
mode_choice="silence"
else
mode_choice=$(prompt_mode_selection)
fi
else
mode_choice=$mode
fi
Expand Down