OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / blind-client-composition-v3.test.js

254 lines · javascript ref:
import test from 'brittle'
import b4a from 'b4a'
import fs from 'node:fs'
import path from 'node:path'
import * as browser from '../../packages/blind-client/browser-artifacts/blind-client-control-v3.mjs'
import { decodeCanonical } from '../../packages/blind-protocol/codec.js'
import { blindForwardHttpsOriginForwardTurnRequestV1 } from '../../packages/blind-protocol/wire-v3.js'
import {
  CLIENT_COMPOSITION_SCHEMA_V3,
  FORWARD_HTTPS_OUTSTANDING_STATE_V3,
  assertForwardHttpsSessionV3,
  assertForwardHttpsVerifiedEndpointV3,
  decodeClientCompositionV3,
  decodeClientCompositionV3SchemaCatalog
} from '../../packages/blind-protocol/client-composition-v3.js'
import {
  CLIENT_COMPOSITION_AUTHORITY_V3,
  assertClientCompositionAuthorityV3
} from '../../packages/blind-protocol/client-composition-authority-generated-v3.js'
import {
  BlindClientBrowserCrashModelV3,
  decodeBlindClientBrowserArtifactManifestV3,
  verifyBlindClientBrowserArtifactV3
} from '../../packages/blind-client/browser-artifact-v3.js'
import {
  BLIND_CLIENT_CONTROL_V3_AUTHORITY,
  prepareForwardHttpsOriginPersistenceV3
} from '../../packages/blind-client/browser-forward-state-v3.js'

const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../..')
const read = relative => fs.readFileSync(path.join(root, relative))

function endpointFixture () {
  const value = JSON.parse(read('packages/blind-protocol/vectors-v3/client-composition/positive/forward-https-verified-endpoint-v3.json'))
  for (const field of [
    'wireV3AbiHash', 'verifiedEndpointHandleHash', 'sourceRelayPublicKey', 'sourceDescriptorHash',
    'targetCatalogEntryId', 'targetRelayPublicKey', 'targetDescriptorHash', 'signedDescriptorHash', 'signedHealthHash'
  ]) value[field] = b4a.from(value[field], 'hex')
  value.sourceDescriptorSequence = BigInt(value.sourceDescriptorSequence)
  value.targetDescriptorSequence = BigInt(value.targetDescriptorSequence)
  return value
}

function sessionFixture () {
  const endpoint = endpointFixture()
  return {
    version: 3,
    verifiedEndpoint: endpoint,
    stableSessionId: b4a.alloc(32, 0x91),
    capabilityPrefixHash: b4a.alloc(32, 0x92),
    clientSessionNonce: b4a.alloc(32, 0x93),
    nextSequence: 0n,
    previousTargetResultHash: b4a.alloc(32),
    terminal: 0,
    outstandingState: 0,
    outstandingOriginRequestCommitment: b4a.alloc(32),
    outstandingOriginRequest: b4a.alloc(0),
    lastDefinitiveTargetResult: b4a.alloc(0)
  }
}

function wireFixture () {
  const origin = read('packages/blind-protocol/vectors-v3/wire/positive/open-origin.bin')
  const request = decodeCanonical(blindForwardHttpsOriginForwardTurnRequestV1, origin, { copyBytes: true })
  const capability = request.parentCapability
  const endpoint = {
    version: 3,
    releaseProfileId: 2,
    routeKind: 7,
    wireV3AbiHash: b4a.from(BLIND_CLIENT_CONTROL_V3_AUTHORITY.wireV3AbiHash, 'hex'),
    verifiedEndpointHandleHash: b4a.alloc(32, 0xa1),
    sourceRelayPublicKey: capability.sourceRelayPublicKey,
    sourceDescriptorSequence: capability.sourceDescriptorSequence,
    sourceDescriptorHash: capability.sourceDescriptorHash,
    targetCatalogEntryId: capability.targetCatalogEntryId,
    targetRelayPublicKey: capability.targetRelayPublicKey,
    targetDescriptorSequence: capability.targetDescriptorSequence,
    targetDescriptorHash: capability.targetDescriptorHash,
    signedDescriptorHash: b4a.alloc(32, 0xa2),
    signedHealthHash: b4a.alloc(32, 0xa3),
    descriptorFresh: true,
    signedHealthFresh: true,
    credentialFreeHttps: true,
    cookies: false,
    authorization: false,
    referrer: false,
    redirect: false,
    exactRequestBytes: 65_536,
    exactResultBytes: 65_536,
    continuityBackend: 'INDEXEDDB_PERSISTENT'
  }
  return { origin, endpoint }
}

function publicSession (record) {
  return {
    version: 3,
    verifiedEndpoint: record.verifiedEndpoint,
    stableSessionId: record.stableSessionId,
    capabilityPrefixHash: record.capabilityPrefixHash,
    clientSessionNonce: record.clientSessionNonce,
    nextSequence: BigInt(record.nextSequence),
    previousTargetResultHash: record.previousTargetResultHash,
    terminal: record.terminal,
    outstandingState: record.outstandingState,
    outstandingOriginRequestCommitment: record.outstandingOriginRequestCommitment,
    outstandingOriginRequest: record.outstandingOriginRequest,
    lastDefinitiveTargetResult: record.lastDefinitiveTargetResult
  }
}

test('client composition v3 imports exact v2 format and WIRE v3 and allocates only IDs 9/10', t => {
  const formatBytes = read('packages/blind-protocol/hiverelay-blind-client-composition-format-v3.cenc')
  const catalogBytes = read('packages/blind-protocol/hiverelay-blind-client-composition-schema-catalog-v3.cenc')
  const format = decodeClientCompositionV3(formatBytes)
  const catalog = decodeClientCompositionV3SchemaCatalog(catalogBytes)
  t.alike(CLIENT_COMPOSITION_SCHEMA_V3, {
    ForwardHttpsVerifiedEndpointV3: 9,
    ForwardHttpsSessionV3: 10
  })
  t.is(b4a.toString(format.baseClientCompositionV2FormatHash, 'hex'), 'e289e6a1658db9f63c79ae13b50a055e16eccc997ef4c752bf1c94090b91dcc2')
  t.is(b4a.toString(format.wireV3AbiHash, 'hex'), CLIENT_COMPOSITION_AUTHORITY_V3.wireV3AbiHash)
  t.is(format.baseSchemaCount, 8)
  t.alike(format.additionalSchemas.map(value => value.schemaId), [9, 10])
  t.alike(catalog.map(value => value.schemaId), [9, 10])
  t.is(format.forwardReadinessOperationBits, 0)
  t.ok(assertClientCompositionAuthorityV3(CLIENT_COMPOSITION_AUTHORITY_V3))
  t.is(CLIENT_COMPOSITION_AUTHORITY_V3.browserRuntimeReady, false)
  t.is(CLIENT_COMPOSITION_AUTHORITY_V3.authorizesRelease, false)
  t.exception(() => decodeClientCompositionV3(b4a.concat([formatBytes, b4a.from([0])])), /canonical/)
})

test('composition v3 endpoint is opaque, two-relay, persistent, and credential-free', t => {
  const fixture = endpointFixture()
  const endpoint = assertForwardHttpsVerifiedEndpointV3(fixture, fixture.wireV3AbiHash)
  t.is(endpoint.continuityBackend, 'INDEXEDDB_PERSISTENT')
  t.is(endpoint.exactRequestBytes, 65_536)
  t.is(endpoint.exactResultBytes, 65_536)
  t.not(endpoint.sourceRelayPublicKey, endpoint.targetRelayPublicKey)
  for (const forbidden of ['url', 'host', 'ip', 'dialAddress', 'credentials']) t.absent(endpoint[forbidden])
  t.exception(() => assertForwardHttpsVerifiedEndpointV3({ ...fixture, host: 'arbitrary.invalid' }), /host is forbidden/)
  t.exception(() => assertForwardHttpsVerifiedEndpointV3({ ...fixture, cookies: true }), /policy is incomplete/)
  t.exception(() => assertForwardHttpsVerifiedEndpointV3({ ...fixture, continuityBackend: 'MEMORY' }), /policy is incomplete/)
  t.exception(() => assertForwardHttpsVerifiedEndpointV3({ ...fixture, targetRelayPublicKey: fixture.sourceRelayPublicKey }), /must differ/)
})

test('composition v3 session enforces exact IndexedDB outstanding and chain invariants', t => {
  const empty = sessionFixture()
  t.is(assertForwardHttpsSessionV3(empty).outstandingState, FORWARD_HTTPS_OUTSTANDING_STATE_V3.NONE)
  t.is(assertForwardHttpsSessionV3({ ...empty, terminal: 1 }).terminal, 1)
  const fixture = wireFixture()
  const record = prepareForwardHttpsOriginPersistenceV3(null, {
    sessionKey: 'composition-test',
    verifiedEndpoint: fixture.endpoint,
    requestBytes: fixture.origin
  }).record
  const outstanding = publicSession(record)
  t.is(assertForwardHttpsSessionV3(outstanding).outstandingOriginRequest.byteLength, 65_536)
  t.exception(() => assertForwardHttpsSessionV3({ ...outstanding, outstandingOriginRequest: b4a.alloc(65_535) }), /exact 65536/)
  t.exception(() => assertForwardHttpsSessionV3({ ...empty, previousTargetResultHash: b4a.alloc(32, 1) }), /zero iff/)
  t.exception(() => assertForwardHttpsSessionV3({ ...empty, outstandingState: 0, outstandingOriginRequestCommitment: b4a.alloc(32, 1) }), /NONE/)
  t.exception(() => assertForwardHttpsSessionV3({ ...outstanding, terminal: 1 }), /terminal session/)
  t.exception(() => assertForwardHttpsSessionV3({ ...outstanding, capabilityPrefixHash: b4a.alloc(32, 9) }), /session state/)
})

test('browser v3 manifest has exact HRBCBV03 214-byte layout and pins complete closure', t => {
  const artifact = read('packages/blind-client/browser-artifacts/blind-client-control-v3.mjs')
  const manifestBytes = read('packages/blind-client/browser-artifacts/blind-client-control-v3.manifest.cenc')
  const closure = read('packages/blind-client/browser-artifacts/blind-client-control-v3.source-closure.json')
  const authority = JSON.parse(read('packages/blind-client/browser-artifacts/blind-client-control-v3.authority.json'))
  const manifest = decodeBlindClientBrowserArtifactManifestV3(manifestBytes)
  t.is(manifestBytes.byteLength, 214)
  t.is(b4a.toString(manifestBytes.subarray(0, 8), 'ascii'), 'HRBCBV03')
  t.is(manifestBytes[8], 0)
  t.is(manifestBytes[9], 3)
  t.is(manifest.exactRequestBytes, 65_536)
  t.is(manifest.exactResultBytes, 65_536)
  t.is(manifest.forwardReadinessOperationBits, 0)
  t.ok(verifyBlindClientBrowserArtifactV3(artifact, manifestBytes, closure, {
    wireV3AbiHash: b4a.from(authority.wireV3AbiHash, 'hex'),
    clientCompositionV3FormatHash: b4a.from(authority.clientCompositionV3FormatHash, 'hex'),
    baseBrowserV2ArtifactHash: b4a.from(authority.baseBrowserV2ArtifactHash, 'hex'),
    baseBrowserV2ManifestHash: b4a.from(authority.baseBrowserV2ManifestHash, 'hex')
  }))
  const sourceClosure = JSON.parse(closure)
  const entries = sourceClosure.entries
  t.is(sourceClosure.schema, 'HiveRelayBlindClientBrowserSourceClosureV3')
  t.is(sourceClosure.toolchain, 'esbuild@0.28.1')
  t.alike(sourceClosure.externalArtifacts, ['./blind-client-control-v2.mjs'])
  t.ok(sourceClosure.metafileInputs.length > 0)
  for (const required of [
    'package.json',
    'packages/blind-client/package.json',
    'packages/blind-client/browser-artifact-v3.js',
    'packages/blind-client/browser-forward-state-v3.js',
    'packages/blind-client/generate-browser-artifact-v3.mjs',
    'packages/blind-client/browser-artifacts/blind-client-control-v2.mjs',
    'packages/blind-protocol/hiverelay-blind-abi-v3.cenc',
    'packages/blind-protocol/hiverelay-blind-wire-authority-v3.json',
    'packages/blind-protocol/hiverelay-blind-client-composition-format-v3.cenc',
    'packages/blind-protocol/hiverelay-blind-client-composition-authority-v3.json'
  ]) t.ok(entries.some(entry => entry.path === required), `closure includes ${required}`)
  t.is(authority.runtimeReady, false)
  t.is(authority.realBrowserEvidenceAccepted, false)
  t.is(authority.authorizesRelease, false)
  t.is(authority.forwardDescriptorOperationBits, 0)
  t.is(authority.forwardAdvertisedOperationBits, 0)
  t.is(authority.forwardReadinessOperationBits, 0)
  t.is(authority.toolchain, 'esbuild@0.28.1')
  t.alike(authority.externalArtifacts, ['./blind-client-control-v2.mjs'])
})

test('browser crash model persists before fetch, retries exact bytes, advances atomically, and retains source results', t => {
  const fixture = wireFixture()
  const input = { sessionKey: 'model', verifiedEndpoint: fixture.endpoint, requestBytes: fixture.origin }
  const model = new BlindClientBrowserCrashModelV3()
  t.exception(() => model.beginFetch(), /before exact request persistence/)
  model.persistBeforeFetch(input)
  const sent = model.beginFetch()
  t.alike(sent, fixture.origin)
  sent[0] ^= 1
  t.alike(model.restartExactRetry(), fixture.origin)
  const sourceResult = read('packages/blind-protocol/vectors-v3/wire/positive/open-source-pre-forward-error.bin')
  t.is(model.receiveResult(sourceResult).advanced, false)
  t.is(model.nextSequence, 0n)
  t.alike(model.restartExactRetry(), fixture.origin)
  const targetResult = read('packages/blind-protocol/vectors-v3/wire/positive/open-target-result.bin')
  t.is(model.receiveResult(targetResult).advanced, true)
  t.is(model.nextSequence, 1n)
  t.alike(model.lastDefinitiveTargetResult, targetResult)
  t.absent(model.outstanding)
  t.absent(model.receiveSourceResult)
  t.absent(model.receiveTargetResult)
  const inspected = model.inspectRecord()
  inspected.stableSessionId[0] ^= 1
  model.record = { forged: true }
  t.is(model.nextSequence, 1n)
  t.alike(model.inspectRecord().lastDefinitiveTargetResult, targetResult)
})

test('generated browser v3 module exposes IndexedDB transaction controls while fail-closed', t => {
  t.is(browser.BLIND_CLIENT_CONTROL_V3_AUTHORITY.runtimeReady, false)
  t.is(browser.BLIND_CLIENT_CONTROL_V3_AUTHORITY.realBrowserEvidenceAccepted, false)
  t.is(browser.BLIND_CLIENT_CONTROL_V3_AUTHORITY.authorizesRelease, false)
  for (const name of [
    'openForwardHttpsIndexedDbV3',
    'persistForwardHttpsOriginBeforeFetchV3',
    'loadForwardHttpsSessionV3',
    'fetchPersistedForwardHttpsOriginV3',
    'retryPersistedForwardHttpsOriginV3',
    'commitVerifiedForwardHttpsResultV3'
  ]) t.is(typeof browser[name], 'function')
})