OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / witness-quorum-policy.test.js

181 lines · javascript ref:
import test from 'brittle'
import { readFile } from 'fs/promises'
import {
  buildWitnessQuorumPolicy,
  evaluateWitnessQuorum,
  validateWitnessQuorumPolicy
} from 'p2p-hiverelay/core/protocol/witness-quorum-policy.js'

const VECTOR_URL = new URL(
  '../fixtures/relaykernel-profile/witness-quorum-policy-v1-5-of-7-diverse.json',
  import.meta.url
)

async function loadVector () {
  return JSON.parse(await readFile(VECTOR_URL, 'utf8'))
}

function hex (byte) {
  return byte.repeat(32)
}

function witness (n, overrides = {}) {
  return {
    witnessPubkey: String(n).padStart(2, '0').repeat(32),
    operator: 'operator-' + n,
    region: ['us-east', 'eu-west', 'ap-south', 'sa-east', 'me-central', 'af-south', 'ca-central'][n - 1] || 'moon',
    ...overrides
  }
}

function baseInput (overrides = {}) {
  return {
    intentId: hex('aa'),
    subjectRelayPubkey: hex('bb'),
    publisherPubkey: hex('cc'),
    selectedBy: 'publisher',
    witnessCount: 7,
    requiredWitnesses: 5,
    minOperators: 5,
    minRegions: 5,
    witnesses: [1, 2, 3, 4, 5, 6, 7].map(n => witness(n)),
    ...overrides
  }
}

function entryFor (policy, witnessPubkey, overrides = {}) {
  return {
    type: 'custody-expiry-witness',
    intentId: policy.intentId,
    relayPubkey: policy.subjectRelayPubkey,
    witnessPubkey,
    catalogPresent: false,
    gatewayServing: false,
    activeSwarmObserved: false,
    ...overrides
  }
}

test('witness quorum policy vector pins publisher-selected 5-of-7 diversity', async (t) => {
  const vector = await loadVector()
  const policy = buildWitnessQuorumPolicy(vector.input)
  const verdict = validateWitnessQuorumPolicy(policy)
  const quorum = evaluateWitnessQuorum(
    policy,
    policy.witnesses.slice(0, 5).map(w => entryFor(policy, w.witnessPubkey))
  )

  t.alike(verdict, vector.verdict, 'policy verdict matches fixture')
  t.alike(quorum, vector.quorum, 'quorum verdict matches fixture')
  t.is(policy.summary.operators.length, 7, 'all witnesses are operator-diverse')
  t.is(policy.summary.regions.length, 7, 'all witnesses are region-diverse')
})

test('witness quorum policy requires publisher-selected witness set', (t) => {
  const policy = buildWitnessQuorumPolicy(baseInput({ selectedBy: 'vault' }))
  const verdict = validateWitnessQuorumPolicy(policy)

  t.absent(verdict.valid)
  t.ok(verdict.errors.includes('witness set must be publisher-selected'))
})

test('witness quorum policy rejects subject self-witness and duplicate witnesses', (t) => {
  const policy = buildWitnessQuorumPolicy(baseInput({
    witnesses: [
      witness(1, { witnessPubkey: hex('bb') }),
      witness(2),
      witness(2),
      witness(4),
      witness(5),
      witness(6),
      witness(7)
    ]
  }))
  const verdict = validateWitnessQuorumPolicy(policy)

  t.absent(verdict.valid)
  t.ok(verdict.errors.includes('subject relay cannot witness itself'))
  t.ok(verdict.errors.some(error => error.startsWith('duplicate witnessPubkey:')))
})

test('witness quorum policy rejects weak operator or region diversity', (t) => {
  const lowOperator = buildWitnessQuorumPolicy(baseInput({
    witnesses: [1, 2, 3, 4, 5, 6, 7].map(n => witness(n, { operator: 'same-operator' }))
  }))
  const lowOperatorVerdict = validateWitnessQuorumPolicy(lowOperator)
  t.absent(lowOperatorVerdict.valid)
  t.ok(lowOperatorVerdict.errors.includes('insufficient witness operator diversity'))

  const lowRegion = buildWitnessQuorumPolicy(baseInput({
    witnesses: [1, 2, 3, 4, 5, 6, 7].map(n => witness(n, { region: 'same-region' }))
  }))
  const lowRegionVerdict = validateWitnessQuorumPolicy(lowRegion)
  t.absent(lowRegionVerdict.valid)
  t.ok(lowRegionVerdict.errors.includes('insufficient witness region diversity'))
})

test('witness quorum policy fails closed on malformed witness policy shape', (t) => {
  const policy = buildWitnessQuorumPolicy(baseInput())
  policy.witnesses[0] = null
  policy.minOperators = -1
  policy.minRegions = -1
  const verdict = validateWitnessQuorumPolicy(policy)

  t.absent(verdict.valid)
  t.ok(verdict.errors.includes('witness object required'))
  t.ok(verdict.errors.includes('minOperators must be a non-negative integer'))
  t.ok(verdict.errors.includes('minRegions must be a non-negative integer'))
})

test('witness quorum policy requires T3 witness role by default', (t) => {
  const badTierProfile = {
    kind: 'hivemesh-tier-profile',
    version: 1,
    tier: 't3',
    role: 'witness',
    keyPrefix: 'rk1',
    capabilities: ['tombstone-signing', 'non-serving-observation', 'gateway'],
    network: { gateway: true, circuit: false, publicDht: false, publicDirectory: false, httpSurfaces: [] },
    storage: { storesContent: false, storesCiphertext: false, storesPlaintext: false },
    compatibility: { blindsparkHttpGateway: { present: false, surfaces: [] } }
  }
  const policy = buildWitnessQuorumPolicy(baseInput({
    witnesses: [witness(1, { tierProfile: badTierProfile }), ...[2, 3, 4, 5, 6, 7].map(n => witness(n))]
  }))
  const verdict = validateWitnessQuorumPolicy(policy)

  t.absent(verdict.valid)
  t.ok(verdict.errors.some(error => error.startsWith('invalid T3 witness profile for')))
})

test('evaluateWitnessQuorum counts only selected, matching, non-serving witnesses', (t) => {
  const policy = buildWitnessQuorumPolicy(baseInput())
  const entries = [
    entryFor(policy, policy.witnesses[0].witnessPubkey),
    entryFor(policy, policy.witnesses[1].witnessPubkey),
    entryFor(policy, policy.witnesses[2].witnessPubkey),
    entryFor(policy, policy.witnesses[3].witnessPubkey),
    entryFor(policy, policy.witnesses[3].witnessPubkey),
    entryFor(policy, hex('99')),
    entryFor(policy, policy.witnesses[4].witnessPubkey, { gatewayServing: true }),
    entryFor(policy, policy.witnesses[5].witnessPubkey, { intentId: hex('dd') })
  ]
  const result = evaluateWitnessQuorum(policy, entries)

  t.absent(result.valid, 'quorum not reached after rejecting bad witnesses')
  t.is(result.count, 4)
  t.is(result.required, 5)
  t.ok(result.rejected.find(row => row.reason === 'duplicate witness'))
  t.ok(result.rejected.find(row => row.reason === 'witness not publisher-selected'))
  t.ok(result.rejected.find(row => row.reason === 'witness observed active serving'))
  t.ok(result.rejected.find(row => row.reason === 'intentId mismatch'))
})

test('evaluateWitnessQuorum fails closed when policy is invalid', (t) => {
  const policy = buildWitnessQuorumPolicy(baseInput({ selectedBy: 'relay' }))
  const result = evaluateWitnessQuorum(policy, [])

  t.absent(result.valid)
  t.is(result.reason, 'invalid-policy')
})