OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / scripts / test-cross-relay.js

167 lines · javascript ref:
#!/usr/bin/env node

/**
 * Cross-Relay Discovery & Stability Test
 *
 * Joins the hiverelay-discovery-v1 topic via Hyperswarm, discovers
 * the local relay (f1b82032...) and fleet relay (6d905b17...),
 * then verifies both connections remain stable for 15 seconds.
 */

import Hyperswarm from 'hyperswarm'
import b4a from 'b4a'
import sodium from 'sodium-universal'

// --- Config ---
// Set via HIVERELAY_KNOWN_RELAYS env var: "name:pkPrefix:label,name2:pkPrefix2:label2"
const KNOWN_RELAYS = (() => {
  const env = process.env.HIVERELAY_KNOWN_RELAYS
  if (env) {
    const relays = {}
    for (const entry of env.split(',')) {
      const [name, pkPrefix, label] = entry.trim().split(':')
      relays[name] = { pkPrefix, label }
    }
    return relays
  }
  return {
    local: { pkPrefix: 'f1b82032', label: 'Local (127.0.0.1:9100)' }
  }
})()
const DISCOVERY_TIMEOUT_MS = 45_000
const STABILITY_WINDOW_MS = 15_000

// --- Derive topic ---
const RELAY_DISCOVERY_TOPIC = b4a.alloc(32)
sodium.crypto_generichash(RELAY_DISCOVERY_TOPIC, b4a.from('hiverelay-discovery-v1'))

// --- State ---
const startTime = Date.now()
const discovered = new Map() // pkHex -> { label, time, conn, disconnected }
const allConnections = [] // every connection we see
let stabilityTimer = null
let discoveryTimer = null

function elapsed () { return ((Date.now() - startTime) / 1000).toFixed(2) }

function identifyRelay (pkHex) {
  for (const [id, { pkPrefix, label }] of Object.entries(KNOWN_RELAYS)) {
    if (pkHex.startsWith(pkPrefix)) return { id, label }
  }
  return null
}

function allRelaysFound () {
  return Object.keys(KNOWN_RELAYS).every(id =>
    [...discovered.values()].some(d => d.id === id)
  )
}

// --- Report ---
function report (stabilityOk) {
  console.log('\n========== CROSS-RELAY TEST REPORT ==========')
  console.log(`Topic: ${b4a.toString(RELAY_DISCOVERY_TOPIC, 'hex')}`)
  console.log(`Total connections seen: ${allConnections.length}`)
  console.log('')

  for (const [id, { pkPrefix, label }] of Object.entries(KNOWN_RELAYS)) {
    const entry = [...discovered.values()].find(d => d.id === id)
    if (entry) {
      const disc = ((entry.time - startTime) / 1000).toFixed(2)
      const status = entry.disconnected ? 'DISCONNECTED' : 'CONNECTED'
      console.log(`  [FOUND]  ${label}`)
      console.log(`           PK prefix : ${pkPrefix}...`)
      console.log(`           Discovered: ${disc}s after start`)
      console.log(`           Status    : ${status}`)
    } else {
      console.log(`  [MISS]   ${label}  -- NOT discovered within ${DISCOVERY_TIMEOUT_MS / 1000}s`)
    }
  }

  // Unidentified peers
  const unidentified = allConnections.filter(c => !identifyRelay(c.pkHex))
  if (unidentified.length) {
    console.log(`\n  Unidentified peers: ${unidentified.length}`)
    for (const u of unidentified) {
      console.log(`    PK: ${u.pkHex.slice(0, 16)}...`)
    }
  }

  console.log('')
  const bothFound = allRelaysFound()
  console.log(`Both relays discovered : ${bothFound ? 'YES' : 'NO'}`)
  console.log(`Connection stability   : ${stabilityOk ? 'PASS (15s no disconnects)' : 'FAIL or SKIPPED'}`)
  console.log('==============================================')

  if (bothFound && stabilityOk) {
    console.log('\nRESULT: ALL CHECKS PASSED')
  } else {
    console.log('\nRESULT: SOME CHECKS FAILED')
  }
}

// --- Main ---
const swarm = new Hyperswarm()
console.log(`[${elapsed()}s] Hyperswarm created, joining discovery topic...`)

swarm.on('connection', (conn, info) => {
  const pkHex = info.publicKey ? b4a.toString(info.publicKey, 'hex') : ''
  const short = pkHex.slice(0, 16)
  console.log(`[${elapsed()}s] Connection from PK: ${short}...`)

  allConnections.push({ pkHex, time: Date.now(), conn })

  const match = identifyRelay(pkHex)
  if (match && !discovered.has(pkHex)) {
    discovered.set(pkHex, {
      id: match.id,
      label: match.label,
      time: Date.now(),
      conn,
      disconnected: false
    })
    console.log(`[${elapsed()}s]   -> Identified as ${match.label}`)

    if (allRelaysFound()) {
      console.log(`[${elapsed()}s] Both relays discovered! Starting ${STABILITY_WINDOW_MS / 1000}s stability window...`)
      clearTimeout(discoveryTimer)
      startStabilityCheck()
    }
  }

  conn.on('error', () => {}) // swallow to avoid crash

  conn.on('close', () => {
    console.log(`[${elapsed()}s] Disconnected: ${short}...`)
    const entry = discovered.get(pkHex)
    if (entry) entry.disconnected = true
  })
})

// Join the discovery topic (client-only so we find existing servers)
const discovery = swarm.join(RELAY_DISCOVERY_TOPIC, { server: false, client: true })
await discovery.flushed()
console.log(`[${elapsed()}s] Topic flushed. Own PK: ${b4a.toString(swarm.keyPair.publicKey, 'hex').slice(0, 16)}...`)
console.log(`[${elapsed()}s] Waiting up to ${DISCOVERY_TIMEOUT_MS / 1000}s for relay connections...\n`)

// Discovery timeout
discoveryTimer = setTimeout(async () => {
  if (!allRelaysFound()) {
    console.log(`\n[${elapsed()}s] Discovery timeout reached.`)
    report(false)
    await swarm.destroy()
    process.exit(1)
  }
}, DISCOVERY_TIMEOUT_MS)

function startStabilityCheck () {
  if (stabilityTimer) return
  stabilityTimer = setTimeout(async () => {
    // Check that no known relay disconnected during the window
    const allStable = [...discovered.values()].every(d => !d.disconnected)
    report(allStable)
    await swarm.destroy()
    process.exit(allStable ? 0 : 1)
  }, STABILITY_WINDOW_MS)
}