OverviewFilesCommitsIssuesDocsReleasesPeers

hyperswarm-peer / integration / run_layer10.sh

174 lines · bash ref:
#!/usr/bin/env bash
# Layer 10: Full E2E with keet-daemon
# Tests complete flow: daemon connect → room create/join → mesh discovery →
# role-based authority election → durable state round-trip.
# Requires keet-daemon (probes existing, starts if needed).
set -euo pipefail
cd "$(dirname "$0")"

echo "=== Layer 10: Full E2E with keet-daemon ==="

SOCKET_PATH="/tmp/keet-daemon.sock"
STARTED_DAEMON=false
DAEMON_PID=""

# Cleanup
rm -f /tmp/layer10_peer_*.txt /tmp/layer10_room_id.txt /tmp/layer10_invite.txt \
      /tmp/layer10_daemon_ready.txt /tmp/layer_bootstrap.txt

# ---- Daemon Probe ----
echo "[orchestrator] probing daemon at $SOCKET_PATH..."
if python3 -c "
import socket, sys
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
    s.settimeout(2)
    s.connect('$SOCKET_PATH')
    s.close()
    sys.exit(0)
except:
    sys.exit(1)
" 2>/dev/null; then
    echo "[orchestrator] existing daemon found at $SOCKET_PATH"
else
    echo "[orchestrator] no daemon found, starting test daemon..."
    STORAGE_DIR="/tmp/layer10_storage"
    DAEMON_DIR="/storage/projects/code/hypercore-kb/hyper-fleeky/packages/keet-daemon"
    rm -rf "$STORAGE_DIR"
    # Copy helper into keet-daemon dir so relative imports resolve correctly
    cp layer10_daemon.js "$DAEMON_DIR/_layer10_daemon.js"
    SOCKET_PATH="$SOCKET_PATH" STORAGE_DIR="$STORAGE_DIR" bare "$DAEMON_DIR/_layer10_daemon.js" &
    DAEMON_PID=$!
    STARTED_DAEMON=true

    # Wait for daemon ready
    for i in $(seq 1 60); do
        [ -f /tmp/layer10_daemon_ready.txt ] && break
        sleep 0.5
    done
    if [ ! -f /tmp/layer10_daemon_ready.txt ]; then
        echo "FAIL: daemon did not start within 30s"
        kill "$DAEMON_PID" 2>/dev/null || true
        exit 1
    fi
    echo "[orchestrator] test daemon ready (PID $DAEMON_PID)"
fi

# Cleanup handler
cleanup() {
    # Kill peers
    for pid in "${PIDS[@]:-}"; do
        kill "$pid" 2>/dev/null || true
        pkill -P "$pid" 2>/dev/null || true
    done
    sleep 1
    for pid in "${PIDS[@]:-}"; do
        kill -9 "$pid" 2>/dev/null || true
        pkill -9 -P "$pid" 2>/dev/null || true
    done
    # Kill testnet
    [ -n "${TESTNET_PID:-}" ] && kill "$TESTNET_PID" 2>/dev/null || true
    # Kill daemon only if we started it
    if $STARTED_DAEMON && [ -n "$DAEMON_PID" ]; then
        echo "[orchestrator] stopping test daemon..."
        kill "$DAEMON_PID" 2>/dev/null || true
        sleep 1
        kill -9 "$DAEMON_PID" 2>/dev/null || true
    fi
    rm -f /tmp/layer10_peer_*.txt /tmp/layer10_room_id.txt /tmp/layer10_invite.txt \
          /tmp/layer10_daemon_ready.txt /tmp/layer_bootstrap.txt
    rm -f /storage/projects/code/hypercore-kb/hyper-fleeky/packages/keet-daemon/_layer10_daemon.js
}
trap cleanup EXIT

# ---- Start testnet ----
echo "[orchestrator] starting DHT testnet..."
node layer2_testnet.js &
TESTNET_PID=$!

for i in $(seq 1 30); do
    [ -f /tmp/layer_bootstrap.txt ] && break
    sleep 0.2
done
[ -f /tmp/layer_bootstrap.txt ] || { echo "FAIL: testnet timeout"; exit 1; }
echo "[orchestrator] testnet ready"

# Project dirs for each peer
DIRS=(project project_b project_c)

# Start 3 peers
PIDS=()
for i in 0 1 2; do
    echo "[orchestrator] starting peer $i (${DIRS[$i]})..."
    timeout -k 10 150 godot4 --headless --path "${DIRS[$i]}" --script res://layer10_e2e.gd -- --peer-index $i --socket-path "$SOCKET_PATH" 2>&1 | sed "s/^/  /" &
    PIDS+=($!)
    sleep 0.5
done

# Wait for all peers to complete (max 120s — room operations take time)
echo "[orchestrator] waiting for all peers to complete (max 120s)..."
ELAPSED=0
ALL_PASS=false
while [ $ELAPSED -lt 120 ]; do
    ALL_PASS=true
    ANY_FAIL=false
    for i in 0 1 2; do
        STATUS=$(cat /tmp/layer10_peer_${i}_status.txt 2>/dev/null || echo "")
        if echo "$STATUS" | grep -q "^FAIL"; then
            ANY_FAIL=true
            echo "[orchestrator] Peer $i FAILED: $STATUS"
        fi
        if [ "$STATUS" != "PASS" ] && ! echo "$STATUS" | grep -q "^FAIL"; then
            ALL_PASS=false
        fi
    done

    if $ANY_FAIL; then
        echo ""
        for i in 0 1 2; do
            echo "Peer $i: $(cat /tmp/layer10_peer_${i}_status.txt 2>/dev/null || echo 'no status')"
        done
        echo "=== Layer 10: FAILED ==="
        exit 1
    fi

    if $ALL_PASS; then
        break
    fi

    sleep 1
    ELAPSED=$((ELAPSED + 1))
done

# Report results
echo ""
for i in 0 1 2; do
    echo "Peer $i: $(cat /tmp/layer10_peer_${i}_status.txt 2>/dev/null || echo 'no status')"
done

# Check authority consistency
AUTH0=$(cat /tmp/layer10_peer_0_authority.txt 2>/dev/null || echo "?")
AUTH1=$(cat /tmp/layer10_peer_1_authority.txt 2>/dev/null || echo "?")
AUTH2=$(cat /tmp/layer10_peer_2_authority.txt 2>/dev/null || echo "?")
PK0=$(cat /tmp/layer10_peer_0_pk.txt 2>/dev/null || echo "?")
if [ "$AUTH0" = "$AUTH1" ] && [ "$AUTH1" = "$AUTH2" ] && [ "$AUTH0" = "$PK0" ]; then
    echo "Authority: consistent (room creator peer 0 = ${AUTH0:0:12}...)"
else
    echo "Authority: INCONSISTENT (${AUTH0:0:12} vs ${AUTH1:0:12} vs ${AUTH2:0:12}, expected ${PK0:0:12})"
fi

# Check durable state
for i in 1 2; do
    STATE=$(cat /tmp/layer10_peer_${i}_durable.txt 2>/dev/null || echo "?")
    echo "Peer $i durable state: $STATE"
done

# Final verdict
if $ALL_PASS; then
    echo "=== Layer 10: PASS ==="
    exit 0
else
    echo "=== Layer 10: TIMEOUT ==="
    exit 1
fi