OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / storage-proof-dispatch.test.js

110 lines · javascript ref:
/**
 * Storage-proof LIVE-PATH smoke — through the REAL Router + ServiceRegistry.
 *
 * The unit tests call StorageProofService.prove() directly; this exercises the
 * actual relay-side dispatch path a client request takes:
 *   client.callService -> ServiceProtocol (JSON over Protomux) -> router.dispatch
 *     ('storage-proof.prove', params, context) -> provider.prove -> JSON result.
 *
 * It proves the parts the direct unit tests skip: (1) registerFromRegistry binds
 * provider.prove to the route, (2) the 'public' ROUTE_ACCESS_POLICIES entry
 * actually admits an ANONYMOUS caller (no role / not authenticated), (3) the
 * proof object survives the JSON wire encoding the ServiceProtocol uses, and
 * (4) the privacy gate fires through the real dispatch path. The Protomux socket
 * layer itself is shared with poker/subscribe (already live on the fleet).
 */
import test from 'brittle'
import Hypercore from 'hypercore'
import b4a from 'b4a'
import sodium from 'sodium-universal'
import os from 'os'
import path from 'path'
import { Router } from 'p2p-hiverelay/core/router/index.js'
import { ServiceRegistry } from 'p2p-hiverelay/core/services/index.js'
import { StorageProofService } from 'p2p-hiveservices/builtin/storage-proof-service.js'
import { verifyStorageProof } from 'p2p-hiverelay/core/protocol/proof-of-storage.js'

let _n = 0
const tmp = () => path.join(os.tmpdir(), 'hr-spd-' + process.pid + '-' + (_n++))
function relayKeyPair () {
  const publicKey = b4a.alloc(sodium.crypto_sign_PUBLICKEYBYTES)
  const secretKey = b4a.alloc(sodium.crypto_sign_SECRETKEYBYTES)
  sodium.crypto_sign_keypair(publicKey, secretKey)
  return { publicKey, secretKey }
}
const freshNonce = () => { const n = b4a.alloc(32); sodium.randombytes_buf(n); return n }

function mockNode (core, keyPair, opts = {}) {
  const keyHex = b4a.toString(core.key, 'hex')
  const entry = { drive: { core, db: { core }, closed: false, closing: false }, blind: opts.blind === true, privacyTier: 'public' }
  return {
    keyPair,
    appRegistry: {
      has: (k) => k === keyHex,
      get: (k) => (k === keyHex ? entry : null),
      _shouldRedactEntry: (e, o = {}) => e.blind === true
    }
  }
}

async function setup (opts = {}) {
  const core = new Hypercore(tmp())
  await core.ready()
  await core.append([b4a.from('m0'), b4a.from('m1'), b4a.from('m2'), b4a.from('m3')])
  const relay = relayKeyPair()
  const registry = new ServiceRegistry()
  registry.register(new StorageProofService())
  await registry.startAll({ node: mockNode(core, relay, opts), store: null, config: {} })
  const router = new Router()
  router.registerFromRegistry(registry)
  await router.start()
  return { core, relay, router }
}

test('dispatch: ANONYMOUS caller gets a proof via the real Router (public access) + JSON wire verifies', async (t) => {
  const { core, relay, router } = await setup()
  const nonce = freshNonce()
  // Anonymous context — no role, no remotePubkey, not authenticated. If the
  // route weren't 'public', router.dispatch would throw ACCESS_DENIED here.
  const result = await router.dispatch('storage-proof.prove',
    { coreKey: b4a.toString(core.key, 'hex'), index: 2, nonce: b4a.toString(nonce, 'hex') },
    {})

  // The real transport is JSON over Protomux (services/protocol.js) — round-trip
  // the result the same way the wire does, then verify it stands on its own.
  const onWire = JSON.parse(JSON.stringify(result))
  const verifier = new Hypercore(tmp(), core.key)
  await verifier.ready()
  const v = await verifyStorageProof({
    verifierCore: verifier,
    response: onWire,
    expect: { driveKey: core.key, index: 2, nonce, relayPubkey: relay.publicKey, minLength: core.length }
  })
  t.ok(v.valid, 'proof routed through the real Router, survived JSON, and verified')
  t.is(v.reason, null)
  await verifier.close(); await core.close()
  if (typeof router.stop === 'function') await router.stop()
})

test('dispatch: privacy gate fires through the real Router (blind => NOT_SEEDED)', async (t) => {
  const { core, router } = await setup({ blind: true })
  await t.exception(
    router.dispatch('storage-proof.prove',
      { coreKey: b4a.toString(core.key, 'hex'), index: 0, nonce: b4a.toString(freshNonce(), 'hex') }, {}),
    /NOT_SEEDED/
  )
  await core.close()
  if (typeof router.stop === 'function') await router.stop()
})

test('dispatch: a bad nonce is rejected through the real Router', async (t) => {
  const { core, router } = await setup()
  await t.exception(
    router.dispatch('storage-proof.prove',
      { coreKey: b4a.toString(core.key, 'hex'), index: 0, nonce: 'not-a-hex-nonce' }, {}),
    /BAD_NONCE/
  )
  await core.close()
  if (typeof router.stop === 'function') await router.stop()
})