Skip to content

Commit 7aa49ae

Browse files
chore(simple-surge-node): add e2e CI pipeline, bridge delivery checks, script improvements
1 parent f557565 commit 7aa49ae

7 files changed

Lines changed: 584 additions & 43 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
name: E2E Full Stack Test
2+
3+
on:
4+
pull_request:
5+
branches: [main, feat/shasta]
6+
workflow_dispatch:
7+
8+
concurrency:
9+
group: e2e-${{ github.head_ref || github.ref_name }}
10+
cancel-in-progress: true
11+
12+
jobs:
13+
e2e-full-stack:
14+
name: E2E Full Stack
15+
permissions:
16+
contents: read
17+
runs-on: ubuntu-22.04
18+
timeout-minutes: 60
19+
env:
20+
CI_L2_DEPLOYER_TIMEOUT: "300"
21+
CI_HEALTH_MAX_RETRIES: "60"
22+
CI_HEALTH_RETRY_INTERVAL: "10"
23+
CI_BRIDGE_DELIVERY_TIMEOUT: "1200"
24+
25+
steps:
26+
- name: Checkout simple-surge-node
27+
uses: actions/checkout@v4
28+
with:
29+
submodules: recursive
30+
fetch-depth: 0
31+
32+
- name: Set up Docker Buildx
33+
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
34+
35+
- name: Install system dependencies
36+
run: |
37+
sudo apt-get update
38+
sudo apt-get install -y curl jq bc
39+
40+
- name: Install Kurtosis CLI
41+
run: |
42+
echo "deb [trusted=yes] https://apt.fury.io/kurtosis-tech/ /" | sudo tee /etc/apt/sources.list.d/kurtosis.list
43+
sudo apt-get update
44+
sudo apt-get install -y kurtosis-cli
45+
kurtosis version
46+
47+
- name: Install Foundry
48+
uses: foundry-rs/foundry-toolchain@v1
49+
50+
- name: Verify prerequisites
51+
run: |
52+
docker --version
53+
docker compose version
54+
docker info
55+
kurtosis version
56+
cast --version
57+
58+
- name: Run E2E
59+
run: ./script/ci-e2e.sh
60+
61+
- name: Collect logs on failure
62+
if: failure()
63+
run: |
64+
echo "=== Docker Compose Services ==="
65+
docker compose ps || true
66+
echo "=== Docker Compose Logs (last 200 lines) ==="
67+
docker compose logs --tail=200 || true
68+
echo "=== Relayer Compose Logs ==="
69+
docker compose -f docker-compose-relayer.yml logs --tail=100 || true
70+
echo "=== Kurtosis Enclaves ==="
71+
kurtosis enclave ls || true
72+
echo "=== Kurtosis Inspect ==="
73+
kurtosis enclave inspect surge-devnet || true
74+
75+
- name: Upload artifacts on failure
76+
if: failure()
77+
uses: actions/upload-artifact@v4
78+
with:
79+
name: e2e-failure-logs
80+
path: |
81+
deployment/*.json
82+
deployment/*.lock
83+
*.log
84+
retention-days: 7
85+
if-no-files-found: ignore
86+
87+
- name: Cleanup
88+
if: always()
89+
run: |
90+
docker compose --profile driver --profile proposer --profile spammer --profile prover --profile blockscout down --remove-orphans 2>/dev/null || true
91+
docker compose -f docker-compose-protocol.yml down --remove-orphans 2>/dev/null || true
92+
docker compose -f docker-compose-relayer.yml down --remove-orphans 2>/dev/null || true
93+
docker network rm surge-network 2>/dev/null || true
94+
kurtosis enclave rm surge-devnet --force 2>/dev/null || true
95+
kurtosis clean -a 2>/dev/null || true
96+
docker system prune -af --volumes 2>/dev/null || true

β€ŽREADME.mdβ€Ž

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,74 @@
44

55
This repository is ideal for easy set up operating Layer 2 (L2) solutions, simplifying the process of running Surge node.
66

7+
## Prerequisites
8+
9+
### Docker and Docker Compose
10+
11+
Docker Engine and Docker Compose v2.1+ are required. Install Docker Desktop or Docker Engine following the [official docs](https://docs.docker.com/engine/install/).
12+
13+
Verify:
14+
15+
```bash
16+
docker --version
17+
docker compose version # must be >= 2.1
18+
```
19+
20+
### Foundry (cast)
21+
22+
```bash
23+
curl -L https://foundry.paradigm.xyz | bash
24+
foundryup
25+
```
26+
27+
### Node.js (using nvm - recommended)
28+
29+
```bash
30+
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
31+
source ~/.bashrc # or ~/.zshrc if using zsh
32+
nvm install --lts
33+
```
34+
35+
Or using apt on Ubuntu/Debian:
36+
37+
```bash
38+
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
39+
sudo apt-get install -y nodejs
40+
```
41+
42+
After installation, restart your terminal or run `source ~/.bashrc` or `source ~/.zshrc` to ensure the tools are available in your PATH.
43+
744
## Quick Start
845

946
### Deploy Complete Devnet
1047

1148
```bash
12-
# Deploy everything with defaults
49+
# Copy devnet environment (first time only)
50+
cp .env.devnet .env
51+
52+
# Deploy everything (non-interactive, defaults to devnet + new L1)
53+
./deploy-surge-full.sh --force
54+
55+
# Or specify options explicitly
1356
./deploy-surge-full.sh --environment devnet --deploy-devnet true --force
1457
```
1558

16-
### Remove Everything
59+
Without `--force` the script prompts interactively for environment, deployment mode, and L1 options.
60+
61+
**Options:**
62+
- `--environment ENV` - Surge environment: `devnet`, `staging`, `testnet` (defaults to `devnet` with `--force`)
63+
- `--deploy-devnet true` - Deploy a new L1 devnet (default with `--force`)
64+
- `--deploy-devnet false` - Skip L1 deployment, use existing chain
65+
- `--deployment local|remote` - Local or remote deployment (defaults to `local` with `--force`)
66+
67+
### Remove Stack
1768

1869
```bash
19-
# Remove all components
70+
# Remove L2 stack, relayers, configs (keeps L1 devnet running)
2071
./remove-surge-full.sh --force
72+
73+
# Remove everything including L1 devnet
74+
./remove-surge-full.sh --remove-l1-devnet true --force
2175
```
2276

2377
For detailed deployment and removal instructions, see [DEPLOYMENT.md](./DEPLOYMENT.md).

β€Žbridge-txs-spammer.shβ€Ž

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -223,20 +223,18 @@ send_bridge_tx() {
223223

224224
log_tx "[$tx_num/$total] Sending $label bridge tx..."
225225

226-
local nonce="$9"
227-
228226
local tx_output
229227
if tx_output=$(cast send "$bridge_address" \
230228
"sendMessage((uint64,uint64,uint32,address,uint64,address,uint64,address,address,uint256,bytes))" \
231229
"(0,$FEE_WEI,$GAS_LIMIT,$ZERO_ADDRESS,0,$PUBLIC_KEY,$dest_chain_id,$PUBLIC_KEY,$PUBLIC_KEY,$amount_wei,0x)" \
232230
--value "$total_value_wei" \
233231
--rpc-url "$rpc_url" \
234232
--private-key "$PRIVATE_KEY" \
235-
--nonce "$nonce" 2>&1); then
236-
log_tx "[$tx_num/$total] $label bridge tx sent successfully βœ“"
233+
2>&1); then
234+
log_tx "[$tx_num/$total] $label bridge tx sent successfully"
237235
return 0
238236
else
239-
log_error "[$tx_num/$total] $label bridge tx failed βœ—"
237+
log_error "[$tx_num/$total] $label bridge tx failed"
240238
log_error "Reason: $tx_output"
241239
return 1
242240
fi
@@ -265,37 +263,28 @@ run_bridge_spam() {
265263

266264
local success_count=0
267265
local fail_count=0
266+
local max_retries=5
267+
local retry_delay=5
268268

269-
# Get starting nonce to avoid nonce collisions when sending rapid txs.
270-
# We use pending nonce and increment locally, with retry on nonce conflicts
271-
# (the same key may be used by proposer/prover, causing contention).
272-
local current_nonce
273-
current_nonce=$(cast nonce "$PUBLIC_KEY" --rpc-url "$rpc_url" --pending 2>/dev/null || cast nonce "$PUBLIC_KEY" --rpc-url "$rpc_url")
274-
log_info "Starting nonce: $current_nonce"
275-
276-
local max_retries=3
277269
for ((i = 1; i <= count; i++)); do
278270
local attempt=0
279271
local sent=false
280272
while [[ "$sent" == "false" && $attempt -lt $max_retries ]]; do
281273
if send_bridge_tx "$bridge_address" "$rpc_url" "$dest_chain_id" \
282-
"$amount_wei" "$total_value_wei" "$direction_label" "$i" "$count" "$current_nonce"; then
274+
"$amount_wei" "$total_value_wei" "$direction_label" "$i" "$count"; then
283275
((success_count++)) || true
284276
sent=true
285277
else
286278
((attempt++)) || true
287279
if [[ $attempt -lt $max_retries ]]; then
288-
# Brief pause to allow pending txs to be mined before refreshing nonce
289-
sleep 2
290-
current_nonce=$(cast nonce "$PUBLIC_KEY" --rpc-url "$rpc_url" --pending 2>/dev/null || cast nonce "$PUBLIC_KEY" --rpc-url "$rpc_url")
291-
log_info "Retrying with refreshed nonce: $current_nonce (attempt $((attempt+1))/$max_retries)"
280+
log_info "Retrying in ${retry_delay}s (attempt $((attempt+1))/$max_retries)..."
281+
sleep "$retry_delay"
292282
fi
293283
fi
294284
done
295285
if [[ "$sent" == "false" ]]; then
296286
((fail_count++)) || true
297287
fi
298-
((current_nonce++)) || true
299288
done
300289

301290
echo
@@ -308,6 +297,7 @@ run_bridge_spam() {
308297

309298
if [[ $fail_count -gt 0 ]]; then
310299
log_warning "$fail_count transactions failed for $direction_label"
300+
return 1
311301
else
312302
log_success "All $direction_label transactions completed successfully"
313303
fi
@@ -417,6 +407,8 @@ main() {
417407
l1_rpc=$(echo "$l1_rpc" | sed 's/host\.docker\.internal/localhost/g')
418408
l2_rpc=$(echo "$l2_rpc" | sed 's/host\.docker\.internal/localhost/g')
419409

410+
log_info "Using deployer wallet: $PUBLIC_KEY"
411+
420412
# Map direction to label
421413
local dir_label
422414
case "$dir_choice" in
@@ -429,31 +421,30 @@ main() {
429421
confirm_execution "$tx_count" "$dir_label" "$eth_amount"
430422

431423
# Execute bridge spam
432-
local total_success=0
433-
local total_fail=0
424+
local has_failures=false
434425

435426
case "$dir_choice" in
436427
1)
437428
# L1 -> L2: call L1 bridge, dest = L2 chain
438429
run_bridge_spam "L1 β†’ L2" "$SHASTA_BRIDGE" "$l1_rpc" "$L2_CHAIN_ID" \
439-
"$amount_wei" "$total_value_wei" "$tx_count"
430+
"$amount_wei" "$total_value_wei" "$tx_count" || has_failures=true
440431
;;
441432
2)
442433
# L2 -> L1: call L2 bridge, dest = L1 chain
443434
run_bridge_spam "L2 β†’ L1" "$L2_BRIDGE" "$l2_rpc" "$L1_CHAIN_ID" \
444-
"$amount_wei" "$total_value_wei" "$tx_count"
435+
"$amount_wei" "$total_value_wei" "$tx_count" || has_failures=true
445436
;;
446437
3)
447438
# Both directions: send full count in each direction
448439
log_info "Sending $tx_count transactions in each direction ($((tx_count * 2)) total)"
449440

450441
# L1 -> L2
451442
run_bridge_spam "L1 β†’ L2" "$SHASTA_BRIDGE" "$l1_rpc" "$L2_CHAIN_ID" \
452-
"$amount_wei" "$total_value_wei" "$tx_count"
443+
"$amount_wei" "$total_value_wei" "$tx_count" || has_failures=true
453444

454445
# L2 -> L1
455446
run_bridge_spam "L2 β†’ L1" "$L2_BRIDGE" "$l2_rpc" "$L1_CHAIN_ID" \
456-
"$amount_wei" "$total_value_wei" "$tx_count"
447+
"$amount_wei" "$total_value_wei" "$tx_count" || has_failures=true
457448
;;
458449
esac
459450

@@ -464,7 +455,12 @@ main() {
464455
echo "β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"
465456
echo
466457

467-
log_success "Bridge transaction spamming complete!"
458+
if [[ "$has_failures" == "true" ]]; then
459+
log_error "Bridge transaction spamming finished with failures"
460+
exit 1
461+
else
462+
log_success "Bridge transaction spamming complete!"
463+
fi
468464
}
469465

470466
# Run main function

β€Ždeploy-surge-full.shβ€Ž

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,19 @@ validate_prerequisites() {
228228
log_error "Please install them first"
229229
return 1
230230
fi
231-
231+
232+
# Verify docker compose v2.1+ (required for --profile support)
233+
local compose_version
234+
compose_version=$(docker compose version --short 2>/dev/null || echo "0.0.0")
235+
local compose_major compose_minor
236+
compose_major=$(echo "$compose_version" | cut -d. -f1)
237+
compose_minor=$(echo "$compose_version" | cut -d. -f2)
238+
if [[ "$compose_major" -lt 2 ]] || [[ "$compose_major" -eq 2 && "$compose_minor" -lt 1 ]]; then
239+
log_error "docker compose >= 2.1 required (found: $compose_version)"
240+
return 1
241+
fi
242+
log_info "docker compose version: $compose_version"
243+
232244
# Create required directories
233245
for dir in "$DEPLOYMENT_DIR" "$CONFIGS_DIR" "driver-data"; do
234246
if [[ ! -d "$dir" ]]; then
@@ -1912,7 +1924,7 @@ wait_for_l2_blocks() {
19121924
l2_rpc=$(echo "$l2_rpc" | sed 's/host\.docker\.internal/localhost/g')
19131925

19141926
local waited=0
1915-
local max_wait=384 # Entire epoch duration
1927+
local max_wait=600 # 10 minutes
19161928
while [[ $waited -lt $max_wait ]]; do
19171929
local block_number
19181930
block_number=$(cast block-number --rpc-url "$l2_rpc" 2>/dev/null || echo "0")
@@ -1953,25 +1965,25 @@ deploy_l2() {
19531965
# Start L2 deployer in detached mode, then wait for it to finish
19541966
BROADCAST=true docker compose --profile l2-deployer up -d >"$temp_output" 2>&1 &
19551967
local deploy_pid=$!
1956-
1968+
19571969
show_progress $deploy_pid "Deploying L2 contracts..."
1958-
1970+
19591971
wait $deploy_pid
19601972
exit_status=$?
1961-
1973+
19621974
if [[ $exit_status -ne 0 ]]; then
19631975
log_error "Failed to start L2 deployer container (exit code: $exit_status)"
19641976
if [[ -f "$temp_output" ]]; then
19651977
log_error "Output saved in: $temp_output"
19661978
fi
19671979
return 1
19681980
fi
1969-
1981+
19701982
# Wait for the deployer container to finish (up to 600s)
19711983
# The deployer runs forge script --broadcast which needs L2 blocks to confirm txs
19721984
log_info "Waiting for L2 deployer to complete..."
19731985
local waited=0
1974-
local max_wait=600
1986+
local max_wait=${CI_L2_DEPLOYER_TIMEOUT:-600}
19751987
while [[ $waited -lt $max_wait ]]; do
19761988
# Check if the deployment artifact appeared on the host (most reliable signal)
19771989
if [[ -f "$DEPLOYMENT_DIR/setup_l2.json" ]]; then
@@ -2005,15 +2017,15 @@ deploy_l2() {
20052017
docker logs surge-l2-deployer 2>&1 | tail -20 >&2
20062018
return 1
20072019
fi
2008-
2020+
20092021
# Verify the deployment artifact was produced
20102022
if [[ -f "$DEPLOYMENT_DIR/setup_l2.json" ]]; then
20112023
log_success "L2 smart contracts deployed successfully"
20122024
touch "$L2_LOCK_FILE"
20132025
else
20142026
log_warning "L2 deployer exited 0 but setup_l2.json not found, continuing..."
20152027
fi
2016-
2028+
20172029
return 0
20182030
fi
20192031
}
@@ -2583,7 +2595,7 @@ main() {
25832595
# Deploy L1 contracts
25842596
local mock_proof
25852597
if [[ "$force" == "true" ]]; then
2586-
mock_proof=0 # default: mock prover
2598+
mock_proof=0
25872599
else
25882600
echo
25892601
echo "╔══════════════════════════════════════════════════════════════╗"

0 commit comments

Comments
Β (0)