OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / integration / storage-proof-e2e.test.js

119 lines · javascript ref:
/**
 * Storage-proof END-TO-END over a real (testnet) DHT + Protomux.
 *
 * The genuine "live proveSeeded smoke", three real nodes:
 *   - publisher: authors + announces a Hyperdrive,
 *   - relay: storage-proof enabled, seeds (replicates) that drive,
 *   - client: a real HiveRelayClient that discovers the relay via the DHT and
 *     runs client.proveSeeded — which calls the relay's storage-proof service
 *     over the actual JSON-over-Protomux service RPC (anonymous / 'public'
 *     route) and verifies every signed proof against the drive key.
 *
 * Exercises the full path the local unit tests stub: DHT discovery, the service
 * channel, the public access policy, and proof verification end to end.
 */
import test from 'brittle'
import createTestnet from '@hyperswarm/testnet'
import Hyperswarm from 'hyperswarm'
import Corestore from 'corestore'
import Hyperdrive from 'hyperdrive'
import { RelayNode } from 'p2p-hiverelay/core/relay-node/index.js'
import { HiveRelayClient } from 'p2p-hiverelay-client'
import b4a from 'b4a'
import path from 'path'
import { tmpdir } from 'os'
import { randomBytes } from 'crypto'
import { mkdirSync } from 'fs'

const TEST_MAX_STORAGE_BYTES = 64 * 1024 * 1024

const tmpStorage = () => {
  const storage = path.join(tmpdir(), 'hr-pos-e2e-' + randomBytes(8).toString('hex'))
  mkdirSync(storage, { recursive: true })
  return storage
}
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

async function within (promise, timeoutMs, label) {
  let timer = null
  try {
    return await Promise.race([
      promise,
      new Promise((_resolve, reject) => {
        timer = setTimeout(() => reject(new Error(label)), timeoutMs)
      })
    ])
  } finally {
    if (timer) clearTimeout(timer)
  }
}

test('e2e: client.proveSeeded verifies a relay holds a seeded drive over the swarm', { timeout: 120000 }, async (t) => {
  const testnet = await createTestnet(3)

  // ── Publisher: authors + announces a multi-block drive ──
  const publisher = new RelayNode({
    storage: tmpStorage(), bootstrapNodes: testnet.bootstrap, enableAPI: false, enableMetrics: false, enableServices: false
  })
  await publisher.start()
  const drive = new Hyperdrive(publisher.store)
  await drive.ready()
  for (let i = 0; i < 6; i++) await drive.put('/f' + i, b4a.from('content-' + i + '-'.padEnd(40, 'x')))
  const driveKey = b4a.toString(drive.key, 'hex')
  publisher.swarm.join(drive.discoveryKey, { server: true, client: true })
  await publisher.swarm.flush()

  // ── Relay: storage-proof enabled, seeds (replicates) the drive ──
  const relay = new RelayNode({
    storage: tmpStorage(),
    bootstrapNodes: testnet.bootstrap,
    enableAPI: false,
    enableMetrics: false,
    enableServices: true,
    plugins: ['storage-proof']
  })
  await relay.start()
  await relay.seedApp(driveKey, { maxStorage: TEST_MAX_STORAGE_BYTES })
  const relayDrive = relay.seededApps.get(driveKey).drive
  await relayDrive.update({ wait: true })
  // Download the full metadata core so any sampled block index is held locally.
  await relayDrive.core.download({ start: 0, end: relayDrive.core.length }).done()
  t.ok(relayDrive.core.length > 0, 'relay replicated the drive metadata core')

  // ── Client: discover the relay + proveSeeded over the wire ──
  const clientSwarm = new Hyperswarm({ bootstrap: testnet.bootstrap })
  const client = new HiveRelayClient({ swarm: clientSwarm, store: new Corestore(tmpStorage()) })

  t.teardown(async () => {
    try { await client.destroy() } catch {}
    try { await clientSwarm.destroy() } catch {}
    try { await drive.close() } catch {}
    try { await relay.stop() } catch {}
    try { await publisher.stop() } catch {}
    try { await testnet.destroy() } catch {}
  })

  await client.start(); await clientSwarm.flush()

  const relayHex = b4a.toString(relay.swarm.keyPair.publicKey, 'hex')
  let found = false
  for (let i = 0; i < 60 && !found; i++) {
    if (client.relays.has(relayHex)) { found = true; break }
    await sleep(500); await clientSwarm.flush()
  }
  t.ok(found, 'client discovered the storage-proof relay via DHT')

  // The real thing — callService over Protomux, anonymous (public route), signed
  // proofs verified against the drive key. Fail fast if it hangs so the failure
  // is legible rather than eating the global timeout.
  const result = await within(
    client.proveSeeded(driveKey, { relay: relayHex, samples: 4 }),
    60_000,
    'PROVE_HANG_60s'
  )
  t.ok(result.ok, 'proveSeeded ok=true — relay produced valid signed proofs over the wire')
  t.is(result.passed, result.total, 'every sampled block verified')
  t.ok(result.total >= 1, 'at least one block sampled')
  t.ok(result.head > 0, 'head reflects the replicated metadata core')
})