204 lines
8.5 KiB
Bash
Executable File
204 lines
8.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# UDP Ingress Test — sends Nostr events via UDP datagrams and verifies storage
|
|
set -e
|
|
|
|
RELAY_HOST="127.0.0.1"
|
|
RELAY_PORT="${RELAY_PORT:-8888}"
|
|
UDP_PORT="${UDP_PORT:-8888}"
|
|
DB_NAME="${DB_NAME:-crelay}"
|
|
DB_USER="${DB_USER:-crelay}"
|
|
DB_HOST="${DB_HOST:-localhost}"
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
|
|
# Color constants
|
|
RED='\033[31m'
|
|
GREEN='\033[32m'
|
|
YELLOW='\033[33m'
|
|
BLUE='\033[34m'
|
|
BOLD='\033[1m'
|
|
RESET='\033[0m'
|
|
|
|
pass() { echo -e "${GREEN}✅ PASS:${RESET} $1"; PASS=$((PASS+1)); }
|
|
fail() { echo -e "${RED}❌ FAIL:${RESET} $1"; FAIL=$((FAIL+1)); }
|
|
info() { echo -e "${BLUE}[INFO]${RESET} $1"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${RESET} $1"; }
|
|
|
|
echo -e "${BOLD}=== UDP Ingress Test ===${RESET}"
|
|
echo "Relay: ${RELAY_HOST}:${RELAY_PORT}, UDP: ${UDP_PORT}"
|
|
echo ""
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Test 1: Check relay is running
|
|
# ---------------------------------------------------------------------------
|
|
if pgrep -f "c_relay_pg" >/dev/null 2>&1; then
|
|
pass "Relay process is running"
|
|
else
|
|
fail "Relay process is not running"
|
|
echo "Start it with: ./make_and_restart_relay.sh"
|
|
exit 1
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Test 2: Check UDP socket is listening
|
|
# ---------------------------------------------------------------------------
|
|
if ss -lun 2>/dev/null | grep -q ":${UDP_PORT} "; then
|
|
pass "UDP socket is listening on port ${UDP_PORT}"
|
|
else
|
|
fail "UDP socket is NOT listening on port ${UDP_PORT}"
|
|
echo ""
|
|
echo "Enable UDP ingress with:"
|
|
echo " PGPASSWORD=crelay psql -h localhost -U crelay -d crelay -c \\"
|
|
echo " \"UPDATE config SET value='true' WHERE key='udp_ingress_enabled';\""
|
|
echo " PGPASSWORD=crelay psql -h localhost -U crelay -d crelay -c \\"
|
|
echo " \"UPDATE config SET value='8888' WHERE key='udp_ingress_port';\""
|
|
echo "Then restart the relay or wait for hot-reload."
|
|
exit 1
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Test 3: Send a valid event via UDP and verify it was stored
|
|
# ---------------------------------------------------------------------------
|
|
info "Test 3: Sending valid Nostr event via UDP..."
|
|
|
|
if command -v nak >/dev/null 2>&1; then
|
|
# Generate a random key and event using nak
|
|
RAND_SEC=$(nak key generate 2>/dev/null)
|
|
EVENT_JSON=$(nak event -k 1 -c "UDP ingress test $(date +%s)" --sec "$RAND_SEC" 2>/dev/null)
|
|
if [ -n "$EVENT_JSON" ]; then
|
|
EVENT_ID=$(echo "$EVENT_JSON" | jq -r '.id' 2>/dev/null)
|
|
if [ -n "$EVENT_ID" ] && [ "$EVENT_ID" != "null" ]; then
|
|
# Send via UDP using Python
|
|
echo "$EVENT_JSON" | python3 -c "
|
|
import socket, sys
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
data = sys.stdin.read().encode()
|
|
sock.sendto(data, ('${RELAY_HOST}', ${UDP_PORT}))
|
|
" 2>/dev/null || {
|
|
# Fallback: try nc -u
|
|
echo "$EVENT_JSON" | nc -u -w1 "${RELAY_HOST}" "${UDP_PORT}" 2>/dev/null || true
|
|
}
|
|
sleep 1 # Give relay time to process
|
|
|
|
# Verify event was stored in database
|
|
RESULT=$(PGPASSWORD=crelay psql -h "${DB_HOST}" -U "${DB_USER}" -d "${DB_NAME}" \
|
|
-tAc "SELECT 1 FROM events WHERE id='${EVENT_ID}'" 2>/dev/null)
|
|
if [ "$RESULT" = "1" ]; then
|
|
pass "Valid event stored in database (id=${EVENT_ID:0:16}...)"
|
|
else
|
|
fail "Valid event NOT found in database (id=${EVENT_ID:0:16}...)"
|
|
fi
|
|
else
|
|
fail "Could not extract event ID from nak output"
|
|
fi
|
|
else
|
|
fail "nak event generation failed"
|
|
fi
|
|
else
|
|
warn "nak not found — generating event manually via Python"
|
|
# Fallback: create a minimal (but properly signed) event using Python
|
|
# This requires the secp256k1 library; if unavailable, skip.
|
|
if python3 -c "import secp256k1" 2>/dev/null; then
|
|
EVENT_JSON=$(python3 -c "
|
|
import json, time, hashlib, secp256k1
|
|
# Generate random private key
|
|
priv = secp256k1.PrivateKey()
|
|
pub = priv.pubkey.serialize().hex()
|
|
# Build event
|
|
event = {
|
|
'id': '',
|
|
'pubkey': pub,
|
|
'created_at': int(time.time()),
|
|
'kind': 1,
|
|
'tags': [],
|
|
'content': 'UDP ingress test ' + str(int(time.time())),
|
|
'sig': ''
|
|
}
|
|
# Compute id
|
|
serial = json.dumps([0, pub, event['created_at'], 1, [], event['content']], separators=(',', ':'))
|
|
event['id'] = hashlib.sha256(serial.encode()).hexdigest()
|
|
# Sign
|
|
sig = priv.ecdsa_serialize(priv.ecdsa_sign(hashlib.sha256(bytes.fromhex(event['id'])).digest()))
|
|
event['sig'] = sig.hex()
|
|
print(json.dumps(event))
|
|
" 2>/dev/null)
|
|
if [ -n "$EVENT_JSON" ]; then
|
|
EVENT_ID=$(echo "$EVENT_JSON" | jq -r '.id' 2>/dev/null)
|
|
echo "$EVENT_JSON" | python3 -c "
|
|
import socket, sys
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
sock.sendto(sys.stdin.read().encode(), ('${RELAY_HOST}', ${UDP_PORT}))
|
|
" 2>/dev/null || true
|
|
sleep 1
|
|
RESULT=$(PGPASSWORD=crelay psql -h "${DB_HOST}" -U "${DB_USER}" -d "${DB_NAME}" \
|
|
-tAc "SELECT 1 FROM events WHERE id='${EVENT_ID}'" 2>/dev/null)
|
|
if [ "$RESULT" = "1" ]; then
|
|
pass "Valid event stored in database (id=${EVENT_ID:0:16}...)"
|
|
else
|
|
fail "Valid event NOT found in database (id=${EVENT_ID:0:16}...)"
|
|
fi
|
|
else
|
|
fail "Python event generation failed (no secp256k1?)"
|
|
fi
|
|
else
|
|
fail "nak not installed and Python secp256k1 unavailable — cannot generate valid event"
|
|
fi
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Test 4: Send an invalid event (bad signature) — should be silently dropped
|
|
# ---------------------------------------------------------------------------
|
|
info "Test 4: Sending invalid event (bad signature) via UDP..."
|
|
INVALID_ID="0000000000000000000000000000000000000000000000000000000000000000"
|
|
echo '{"id":"0000000000000000000000000000000000000000000000000000000000000000","pubkey":"0000000000000000000000000000000000000000000000000000000000000000","created_at":1,"kind":1,"tags":[],"content":"invalid","sig":"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}' \
|
|
| python3 -c "
|
|
import socket, sys
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
sock.sendto(sys.stdin.read().encode(), ('${RELAY_HOST}', ${UDP_PORT}))
|
|
" 2>/dev/null || echo "$INVALID_JSON" | nc -u -w1 "${RELAY_HOST}" "${UDP_PORT}" 2>/dev/null || true
|
|
sleep 1
|
|
|
|
# Verify the invalid event was NOT stored
|
|
RESULT=$(PGPASSWORD=crelay psql -h "${DB_HOST}" -U "${DB_USER}" -d "${DB_NAME}" \
|
|
-tAc "SELECT 1 FROM events WHERE id='${INVALID_ID}'" 2>/dev/null)
|
|
if [ "$RESULT" != "1" ]; then
|
|
pass "Invalid event was silently dropped (not stored)"
|
|
else
|
|
fail "Invalid event was incorrectly stored!"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Test 5: Send an oversized datagram — should be rejected
|
|
# ---------------------------------------------------------------------------
|
|
info "Test 5: Sending oversized datagram via UDP..."
|
|
# Build a payload larger than the default max datagram size (65535 bytes)
|
|
LARGE_CONTENT=$(python3 -c "print('x' * 70000)" 2>/dev/null)
|
|
LARGE_JSON="{\"id\":\"1111111111111111111111111111111111111111111111111111111111111111\",\"pubkey\":\"1111111111111111111111111111111111111111111111111111111111111111\",\"created_at\":1,\"kind\":1,\"tags\":[],\"content\":\"${LARGE_CONTENT}\",\"sig\":\"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\"}"
|
|
echo "$LARGE_JSON" | python3 -c "
|
|
import socket, sys
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
data = sys.stdin.read().encode()
|
|
try:
|
|
sock.sendto(data, ('${RELAY_HOST}', ${UDP_PORT}))
|
|
except OSError as e:
|
|
print(f'Message too large (expected): {e}')
|
|
" 2>/dev/null || true
|
|
sleep 1
|
|
|
|
# The oversized message should not have been stored
|
|
RESULT=$(PGPASSWORD=crelay psql -h "${DB_HOST}" -U "${DB_USER}" -d "${DB_NAME}" \
|
|
-tAc "SELECT 1 FROM events WHERE id='1111111111111111111111111111111111111111111111111111111111111111'" 2>/dev/null)
|
|
if [ "$RESULT" != "1" ]; then
|
|
pass "Oversized datagram was rejected (not stored)"
|
|
else
|
|
fail "Oversized datagram was incorrectly stored!"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Summary
|
|
# ---------------------------------------------------------------------------
|
|
echo ""
|
|
echo -e "${BOLD}=== Results: ${PASS} passed, ${FAIL} failed ===${RESET}"
|
|
exit $FAIL
|