OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / integration / blind-custody-intent-e2e.test.js

140 lines · javascript ref:
/**
 * FULL custody-path dispersal over live relays — the exact flow the fleet run
 * executes, with NO stub authorization.
 *
 * A publisher builds a real signed custody intent (createCustodyIntent) binding
 * shareAssignments (relay -> shareIndex) + shareManifest (shareIndex -> shard),
 * "publishes" it to each relay, and disperse() PUTs each shard. Each relay
 * authorizes the PUT through the PRODUCTION resolver
 * (RelayNode._resolveShardCustodyAssignment reading its seeding registry) — so
 * this proves intent-create -> publish -> custody-authorized PUT -> recover end
 * to end against the real custody machinery. Swap the in-process relays for the
 * fleet's URLs + real publishCustodyIntent and it's the same code.
 */
import test from 'brittle'
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import Corestore from 'corestore'
import b4a from 'b4a'
import sodium from 'sodium-universal'
import { RelayAPI } from 'p2p-hiverelay/core/relay-node/api.js'
import { RelayNode } from 'p2p-hiverelay/core/relay-node/index.js'
import { ShardStoreService, signShardPin, normalizeShardAddress } from '../../packages/services/builtin/shard-store/index.js'
import { disperse, recover } from '../../packages/client/blind-custody.js'
// The CORE signer supports the v2 PVSS custody fields (shareScheme/shareManifest/
// shareAssignments). The client mirror (packages/client/custody.js) does NOT yet
// (pending task: mirror shareManifest into the client signer) — so a node-run
// dealer uses core; a Bare/client-only dealer is blocked until that lands.
import { createCustodyIntent } from '../../packages/core/core/custody-signing.js'

function keyPair (seed) {
  const publicKey = b4a.alloc(32)
  const secretKey = b4a.alloc(64)
  sodium.crypto_sign_seed_keypair(publicKey, secretKey, b4a.alloc(32, seed))
  return { publicKey, secretKey, hex: b4a.toString(publicKey, 'hex') }
}
async function tmpStore (t) {
  const dir = await mkdtemp(join(tmpdir(), 'custody-intent-'))
  t.teardown(() => rm(dir, { recursive: true, force: true }))
  const store = new Corestore(dir)
  t.teardown(() => store.close())
  return store
}
function shardRegistry (p) {
  return { services: new Map([['shard-store', { name: 'shard-store', status: 'running', capabilities: ['put', 'get', 'has', 'prove'], provider: p }]]) }
}
function mockNode (registry) {
  return { running: true, config: { plugins: [], trustProxy: true }, metrics: { getSummary () { return {} } }, seededApps: new Map(), appRegistry: { apps: new Map(), catalog () { return [] }, catalogForBroadcast () { return [] } }, getStats () { return {} }, getHealthStatus () { return { healthy: true } }, serviceRegistry: registry, async stop () {}, async start () {}, on () {}, emit () {} }
}

const H = (c) => c.repeat(64)
const nonceFor = (i) => i.toString(16).padStart(32, '0')

test('blind-custody dispersal via a REAL signed custody intent + production resolver', async (t) => {
  // Shared "published custody intents" — stands in for each relay's seeding
  // registry. On the fleet this is populated by client.publishCustodyIntent().
  const published = new Map() // intentId -> signed intent
  const registryLike = { getCustodyIntent: (id) => published.get(id) || null }

  // n live relays; each authorizes via the ACTUAL production resolver.
  const relays = []
  for (let i = 1; i <= 4; i++) {
    const store = await tmpStore(t)
    const kp = keyPair(i)
    const relay = { kp, pubkey: kp.hex }
    relay.svc = new ShardStoreService({
      putAuth: ['custody'],
      resolveCustodyAssignment: (intentId, relayPubkey) =>
        RelayNode.prototype._resolveShardCustodyAssignment.call({ seedingRegistry: registryLike }, intentId, relayPubkey)
    })
    await relay.svc.start({ store, node: { keyPair: kp } })
    const api = new RelayAPI(mockNode(shardRegistry(relay.svc)), { apiPort: 0, apiHost: '127.0.0.1' })
    await api.start()
    relay.baseUrl = 'http://127.0.0.1:' + api.server.address().port
    t.teardown(async () => { await api.stop(); await relay.svc.stop() })
    relays.push(relay)
  }

  const publisher = keyPair(200)
  let intent = null

  // Build + sign the real custody intent from the dispersal plan, then "publish"
  // it to the relay (fleet: await client.publishCustodyIntent(relay.baseUrl, intent, {apiKey})).
  const publishIntent = async (_relay, fields) => {
    if (!intent) {
      intent = createCustodyIntent({
        version: 2, // unlocks the PVSS share-field allowlist (custody-signing.js:1031)
        blindContentId: H('b'),
        ciphertextRoot: fields.commitmentRoot,
        contentVersion: 1,
        requiredReplicas: fields.count,
        shareScheme: 'pvss-secp256k1-v1',
        shareThreshold: fields.threshold,
        commitmentRoot: fields.commitmentRoot,
        shareBundleKey: H('d'),
        shareAssignments: fields.shareAssignments,
        shareManifest: fields.shareManifest
      }, publisher)
      published.set(intent.intentId, intent)
    }
  }
  const signPin = ({ hash, shareIndex }) => signShardPin({
    reason: 'custody',
    hash: normalizeShardAddress(hash),
    custodyIntentId: intent.intentId,
    shareIndex,
    retainUntil: Date.now() + 3600000,
    nonce: nonceFor(shareIndex)
  }, publisher)

  // 3-of-4 dispersal across the live relays, authorized by the real intent.
  const out = await disperse(undefined, { relays, threshold: 3, signPin, publishIntent })
  t.ok(intent && /^[0-9a-f]{64}$/.test(intent.intentId), 'a real signed custody intent was built + published')
  t.is(out.shareManifest.length, 4)
  t.alike(out.shareManifest, intent.shareManifest, 'the intent binds exactly what was dispersed')

  // Reader reconstructs from any 3 of 4 relays over HTTP.
  const rec = await recover({ relays: [relays[0], relays[1], relays[3]], shareManifest: out.shareManifest, threshold: 3 })
  t.is(rec.ok, true)
  t.is(rec.key, out.key, 'reader reconstructs the exact secret through the real custody path')

  // 2 relays is below threshold.
  const two = await recover({ relays: [relays[0], relays[1]], shareManifest: out.shareManifest, threshold: 3 })
  t.is(two.ok, false)

  // A PUT whose intent was never published is refused by the production resolver.
  const orphanPin = signShardPin({
    reason: 'custody',
    hash: normalizeShardAddress(out.shareManifest[0].shard),
    custodyIntentId: H('f'),
    shareIndex: 1,
    retainUntil: Date.now() + 3600000,
    nonce: nonceFor(9)
  }, publisher)
  await t.exception(
    relays[0].svc.put({ ciphertext: b4a.toString(b4a.from('x'), 'base64'), pin: orphanPin }),
    /UNAUTHORIZED_PIN/
  )
})