#!/bin/bash # Test SUP-02: Multi-Hop Routing # Tests: Builder → Thrower A → Thrower B → Thrower C → Final Relay set -e TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$TEST_DIR/helpers/timing_utils.sh" source "$TEST_DIR/helpers/event_utils.sh" # Load test configuration KEYS_FILE="$TEST_DIR/fixtures/test_keys.json" RELAYS_FILE="$TEST_DIR/fixtures/test_relays.json" # Extract keys BUILDER_PRIVKEY=$(jq -r '.builder.privkey' "$KEYS_FILE") THROWER_A_PRIVKEY=$(jq -r '.thrower_a.privkey' "$KEYS_FILE") THROWER_A_PUBKEY=$(jq -r '.thrower_a.pubkey' "$KEYS_FILE") THROWER_B_PRIVKEY=$(jq -r '.thrower_b.privkey' "$KEYS_FILE") THROWER_B_PUBKEY=$(jq -r '.thrower_b.pubkey' "$KEYS_FILE") THROWER_C_PRIVKEY=$(jq -r '.thrower_c.privkey' "$KEYS_FILE") THROWER_C_PUBKEY=$(jq -r '.thrower_c.pubkey' "$KEYS_FILE") # Extract relays THROWER_A_RELAY=$(jq -r '.test_scenarios.multi_hop_3.thrower_a_relay' "$RELAYS_FILE") THROWER_B_RELAY=$(jq -r '.test_scenarios.multi_hop_3.thrower_b_relay' "$RELAYS_FILE") THROWER_C_RELAY=$(jq -r '.test_scenarios.multi_hop_3.thrower_c_relay' "$RELAYS_FILE") FINAL_RELAY=$(jq -r '.test_scenarios.multi_hop_3.final_relay' "$RELAYS_FILE") echo "=== SUP-02: Multi-Hop Routing Test (3 hops) ===" echo "Thrower A: $THROWER_A_PUBKEY → $THROWER_A_RELAY" echo "Thrower B: $THROWER_B_PUBKEY → $THROWER_B_RELAY" echo "Thrower C: $THROWER_C_PUBKEY → $THROWER_C_RELAY" echo "Final Relay: $FINAL_RELAY" echo "" # Test parameters DELAY_A=2 DELAY_B=2 DELAY_C=2 TOTAL_DELAY=$((DELAY_A + DELAY_B + DELAY_C)) AUDIT_TAG="test-multi-hop-3-$(date +%s)" TEST_CONTENT="Multi-hop test message at $(date)" echo "Step 1: Create innermost kind 1 event" INNER_EVENT=$(create_test_event "$BUILDER_PRIVKEY" "$TEST_CONTENT") INNER_EVENT_ID=$(echo "$INNER_EVENT" | jq -r '.id') echo "Created inner event: $INNER_EVENT_ID" echo "" echo "Step 2: Build onion routing layers (inside-out)" echo "" # Layer 3 (innermost): Routing from C to final relay echo "Layer 3: Thrower C → Final Relay" ROUTING_C=$(create_routing_payload "$INNER_EVENT" "$FINAL_RELAY" "$DELAY_C" "null" "$AUDIT_TAG") ENCRYPTED_C=$(encrypt_payload "$BUILDER_PRIVKEY" "$THROWER_C_PUBKEY" "$ROUTING_C") WRAPPER_C=$(create_routing_event "$BUILDER_PRIVKEY" "$THROWER_C_PUBKEY" "$ENCRYPTED_C") echo " Delay: ${DELAY_C}s" echo "" # Layer 2: Routing from B to C echo "Layer 2: Thrower B → Thrower C" ROUTING_B=$(create_routing_payload "$WRAPPER_C" "$THROWER_C_RELAY" "$DELAY_B" "$THROWER_C_PUBKEY" "$AUDIT_TAG") ENCRYPTED_B=$(encrypt_payload "$BUILDER_PRIVKEY" "$THROWER_B_PUBKEY" "$ROUTING_B") WRAPPER_B=$(create_routing_event "$BUILDER_PRIVKEY" "$THROWER_B_PUBKEY" "$ENCRYPTED_B") echo " Delay: ${DELAY_B}s" echo "" # Layer 1 (outermost): Routing from A to B echo "Layer 1: Thrower A → Thrower B" ROUTING_A=$(create_routing_payload "$WRAPPER_B" "$THROWER_B_RELAY" "$DELAY_A" "$THROWER_B_PUBKEY" "$AUDIT_TAG") ENCRYPTED_A=$(encrypt_payload "$BUILDER_PRIVKEY" "$THROWER_A_PUBKEY" "$ROUTING_A") WRAPPER_A=$(create_routing_event "$BUILDER_PRIVKEY" "$THROWER_A_PUBKEY" "$ENCRYPTED_A") WRAPPER_A_ID=$(echo "$WRAPPER_A" | jq -r '.id') echo " Delay: ${DELAY_A}s" echo " Routing event ID: $WRAPPER_A_ID" echo "" echo "Step 3: Publish initial routing event to Thrower A" PUBLISH_TIME=$(get_timestamp) publish_event "$WRAPPER_A" "$THROWER_A_RELAY" echo "Published at: $(date -d @$PUBLISH_TIME)" echo "" echo "Step 4: Monitor routing chain" echo "Expected total delay: ${TOTAL_DELAY}s (minimum)" echo "Monitoring for inner event on final relay..." echo "" # Monitor for the inner event on final relay TIMEOUT=$((TOTAL_DELAY + 60)) # Total delay + 60 seconds buffer FOUND=false START_MONITOR=$(get_timestamp) while [ $(($(get_timestamp) - START_MONITOR)) -lt $TIMEOUT ]; do if query_event "$INNER_EVENT_ID" "$FINAL_RELAY" 2 | grep -q "$INNER_EVENT_ID"; then ARRIVAL_TIME=$(get_timestamp) FOUND=true break fi # Show progress every 5 seconds ELAPSED=$(($(get_timestamp) - PUBLISH_TIME)) if [ $((ELAPSED % 5)) -eq 0 ]; then echo " Elapsed: ${ELAPSED}s / Expected: ${TOTAL_DELAY}s+" fi sleep 1 done if [ "$FOUND" = false ]; then echo "ERROR: Inner event not found on final relay within ${TIMEOUT}s" echo "This could indicate:" echo " - One or more throwers are not running" echo " - Routing chain is broken" echo " - Network connectivity issues" exit 1 fi echo "" echo "Step 5: Verify timing" ACTUAL_DELAY=$((ARRIVAL_TIME - PUBLISH_TIME)) echo "Actual total delay: ${ACTUAL_DELAY}s" echo "Expected minimum: ${TOTAL_DELAY}s" if [ $ACTUAL_DELAY -lt $TOTAL_DELAY ]; then echo "ERROR: Event arrived too early!" echo "Expected at least ${TOTAL_DELAY}s, got ${ACTUAL_DELAY}s" exit 1 fi # Check if delay is reasonable (not more than 3x expected + 30s buffer) MAX_DELAY=$((TOTAL_DELAY * 3 + 30)) if [ $ACTUAL_DELAY -gt $MAX_DELAY ]; then echo "WARNING: Event took much longer than expected" echo "Actual: ${ACTUAL_DELAY}s, Maximum expected: ${MAX_DELAY}s" fi echo "" echo "Step 6: Verify event content" FINAL_EVENT=$(query_event "$INNER_EVENT_ID" "$FINAL_RELAY" 5) FINAL_CONTENT=$(echo "$FINAL_EVENT" | jq -r '.content') if [ "$FINAL_CONTENT" != "$TEST_CONTENT" ]; then echo "ERROR: Content mismatch!" echo "Expected: $TEST_CONTENT" echo "Got: $FINAL_CONTENT" exit 1 fi echo "Content verified: $FINAL_CONTENT" echo "" echo "Step 7: Verify hop sequence (optional - requires relay monitoring)" echo "Note: Full hop verification requires monitoring intermediate relays" echo "This test verifies end-to-end delivery through 3 hops" echo "" echo "=== TEST PASSED ===" echo "✓ 3-hop routing successful" echo "✓ Total delay constraint respected (${ACTUAL_DELAY}s >= ${TOTAL_DELAY}s)" echo "✓ Event content preserved through all hops" echo "✓ Onion routing layers properly unwrapped" echo "✓ Event published to correct final relay" echo "" exit 0