OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / witnesslog.test.js

207 lines · javascript ref:
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'brittle'
import sodium from 'sodium-universal'
import b4a from 'b4a'
import {
  WITNESSLOG_NAMESPACE,
  WitnessLogApp,
  canonicalWitnessRecord,
  createWitnessRecord,
  redactWitnessRecord,
  signWitnessRecord,
  targetMatchesWitnessRecord,
  verifyWitnessOutboxRecord,
  verifyWitnessRecord,
  witnessRecordMarker
} from '../../packages/services/builtin/witnesslog/index.js'

const OBSERVED_AT = '2026-07-02T12:00:00.000Z'
const EXPIRES_AT = '2026-07-02T12:30:00.000Z'
const NOW = Date.parse('2026-07-02T12:05:00.000Z')
const TARGET = 'a'.repeat(64)
const TARGET_HASH = 'b'.repeat(64)

test('witnesslog record: signs, verifies, canonicalizes, and redacts target keys', (t) => {
  const observer = keyPair(1)
  const base = witnessInput(observer)
  const created = createWitnessRecord(base)
  const signed = signWitnessRecord(base, observer.secretKey)

  t.is(created.id, signed.id)
  t.is(canonicalWitnessRecord(base), canonicalWitnessRecord(signed))
  t.ok(verifyWitnessRecord(signed, { now: NOW }))
  t.absent(verifyWitnessRecord({ ...signed, result: { ...signed.result, latencyMs: 999 } }, { now: NOW }))

  const redacted = redactWitnessRecord(signed)
  t.absent(redacted.target.key)
  t.is(redacted.target.keyHash, TARGET_HASH)
  t.ok(targetMatchesWitnessRecord(signed, TARGET))
  t.ok(targetMatchesWitnessRecord(signed, TARGET_HASH))

  const marker = witnessRecordMarker(signed)
  t.alike(marker, {
    id: signed.id,
    observedAt: OBSERVED_AT,
    expiresAt: EXPIRES_AT,
    observer: observer.publicKeyHex,
    relay: 'https://relay.example/',
    target: TARGET_HASH,
    targetKind: 'hypercore',
    ok: true,
    latencyMs: 42,
    httpStatus: 200,
    errorCode: null
  })
})

test('witnesslog record: timestamp policy rejects future, stale, and expired observations', (t) => {
  const observer = keyPair(2)
  const future = signWitnessRecord(witnessInput(observer, {
    observedAt: '2026-07-02T13:00:00.000Z',
    expiresAt: '2026-07-02T13:30:00.000Z'
  }), observer.secretKey)
  const stale = signWitnessRecord(witnessInput(observer, {
    observedAt: '2026-07-01T11:00:00.000Z',
    expiresAt: '2026-07-01T11:30:00.000Z'
  }), observer.secretKey)
  const expired = signWitnessRecord(witnessInput(observer, {
    observedAt: '2026-07-02T11:00:00.000Z',
    expiresAt: '2026-07-02T11:30:00.000Z'
  }), observer.secretKey)

  t.absent(verifyWitnessRecord(future, { now: NOW }))
  t.absent(verifyWitnessRecord(stale, { now: NOW, maxAgeMs: 30 * 60 * 1000 }))
  t.absent(verifyWitnessRecord(expired, { now: NOW }))
})

test('witnesslog app: appends signed observations, ranges by target, and emits markers', (t) => {
  const observer = keyPair(3)
  const app = new WitnessLogApp({ verify: { now: NOW } })
  const signed = signWitnessRecord(witnessInput(observer), observer.secretKey)
  const live = []
  const unsubscribe = app.subscribe({ target: TARGET_HASH }, marker => live.push(marker))

  t.alike(app.append(signed), { ok: true, key: 'availability!' + signed.id, id: signed.id })
  unsubscribe()

  const page = app.list({ target: TARGET_HASH })
  t.is(page.count, 1)
  t.is(page.total, 1)
  t.is(page.records[0].id, signed.id)
  t.absent(page.records[0].target.key)
  t.is(page.records[0].target.keyHash, TARGET_HASH)

  const markers = app.markers({ target: TARGET })
  t.is(markers.count, 1)
  t.is(markers.markers[0].id, signed.id)
  t.is(live.length, 1)
  t.is(live[0].topic, 'witness/' + TARGET_HASH)
})

test('witnesslog app: rejects foreign outbox writers before storage', (t) => {
  const observer = keyPair(4)
  const foreign = keyPair(5)
  const signed = signWitnessRecord(witnessInput(observer), observer.secretKey)
  const app = new WitnessLogApp({ verify: { now: NOW } })
  const bad = {
    appId: foreign.publicKeyHex,
    data: { ...signed, _ns: WITNESSLOG_NAMESPACE }
  }

  t.absent(verifyWitnessOutboxRecord(bad, { now: NOW }))
  const err = throws(() => app.sync.append(foreign.publicKeyHex, { type: 'availability', data: bad.data }))
  t.is(err.status, 400)
  t.is(app.list().count, 0)
})

test('witnesslog app: default file persistence restores signed observations', async (t) => {
  const dir = await mkdtemp(join(tmpdir(), 'witnesslog-'))
  t.teardown(async () => {
    await rm(dir, { recursive: true, force: true })
  })

  const observer = keyPair(6)
  const signed = signWitnessRecord(witnessInput(observer), observer.secretKey)
  const first = new WitnessLogApp({ verify: { now: NOW } })
  await first.start({ config: { storage: dir }, node: { id: 'relay-a' } })
  t.alike(first.append(signed), { ok: true, key: 'availability!' + signed.id, id: signed.id })
  await first.stop()

  const second = new WitnessLogApp({ verify: { now: NOW } })
  await second.start({ config: { storage: dir }, node: { id: 'relay-b' } })
  const page = second.list({ target: TARGET_HASH })
  t.is(page.count, 1)
  t.is(page.records[0].id, signed.id)
  t.absent(page.records[0].target.key)
  await second.stop()
})

function witnessInput (observer, fields = {}) {
  return {
    observedAt: fields.observedAt || OBSERVED_AT,
    expiresAt: fields.expiresAt || EXPIRES_AT,
    observer: {
      publicKey: observer.publicKeyHex,
      appId: 'probe-agent'
    },
    relay: {
      url: 'https://relay.example',
      operatorKey: 'c'.repeat(64)
    },
    target: {
      kind: 'hypercore',
      key: TARGET,
      keyHash: TARGET_HASH
    },
    probe: {
      nonce: 'nonce-1',
      method: 'gateway-head',
      range: { start: 0, end: 1024 }
    },
    result: {
      ok: true,
      httpStatus: 200,
      latencyMs: 42,
      bytes: 1024,
      headSeq: 7,
      digest: 'sha256:' + 'd'.repeat(64)
    }
  }
}

function keyPair (seedByte) {
  const publicKey = b4a.alloc(32)
  const secretKey = b4a.alloc(64)
  sodium.crypto_sign_seed_keypair(publicKey, secretKey, b4a.alloc(32, seedByte))
  return {
    publicKey,
    secretKey,
    publicKeyHex: b4a.toString(publicKey, 'hex')
  }
}

function throws (fn) {
  try {
    fn()
  } catch (err) {
    return err
  }
  throw new Error('expected function to throw')
}

test('witnesslog: redaction/markers never expose a raw key even for key-only records', t => {
  const observer = keyPair(30)
  const keyOnly = signWitnessRecord({ ...witnessInput(observer), target: { kind: 'hypercore', key: TARGET } }, observer.secretKey)

  const redacted = redactWitnessRecord(keyOnly)
  t.absent(redacted.target.key, 'raw key stripped on read')
  t.ok(redacted.target.keyHash, 'keyHash derived for identification')
  t.not(redacted.target.keyHash, TARGET)

  const marker = witnessRecordMarker(keyOnly)
  t.not(marker.target, TARGET, 'marker does not expose the raw key')
  t.ok(marker.target)
})