OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / blind-protocol-vectors.test.js

320 lines · javascript ref:
import fs from 'fs'
import test from 'brittle'
import b4a from 'b4a'
import {
  admissionParametersV1,
  admissionProfileV1,
  batchGetResultV1,
  batchGetSignaturePayloadV1,
  batchGetV1,
  blindBackupChunkManifestV1,
  blindBackupEncryptionProfileV1,
  blindBackupManifestV1,
  blindBackupRetentionTransitionV1,
  blindCleanRestoreEvidenceV1,
  blindCoreAckV1,
  blindDhtPointerV1,
  blindExternalJournalTopologyV1,
  blindExternalCommitWitnessV1,
  blindForwardCloseV1,
  blindForwardDataV1,
  blindForwardOpenResultV1,
  blindForwardOpenV1,
  blindForwardWindowV1,
  blindHealthChallengeV1,
  blindHealthResultV1,
  blindControlStateSnapshotV1,
  blindLocalCheckpointV1,
  blindOhttpKeyConfigV1,
  blindRestoreEvidenceBundleV1,
  blindRestoreEvidenceHeadV1,
  blindServiceDescriptorV1,
  blindStreamChunkPlainV1,
  blindWalHeaderV2,
  blake2b256,
  coreMirrorRequestV1,
  coreServeChallengeV1,
  coreServeResultV1,
  compileMasterSchemaCatalog,
  compileMasterSchemaCatalogForCategory,
  controlSnapshotHash,
  decodeCanonical,
  decodeSchemaCatalog,
  decodeDispatchFrame,
  decodeOuterEnvelope,
  decodeVectorManifest,
  domainRegistryEntryV1,
  durabilityContinuityBindingV1,
  encodeWireAbiRegistry,
  encodeCanonical,
  errorProfileEntryV1,
  getCellResultV1,
  hashAbi,
  hashSpec,
  hashVectorSet,
  inboxAppendV1,
  inboxReadResultV1,
  inboxReadSignaturePayloadV1,
  inboxReadV1,
  localCheckpointHash,
  operationProfileV1,
  putCellV1,
  relayResultBindingV1,
  relayIdentityTransitionV1,
  SCHEMA_CATEGORY,
  SCHEMA_NAMES_BY_CATEGORY,
  schemaCatalogEntryV1
} from '@hiverelay/blind-protocol'

const vectorUrl = name => new URL(`../../packages/blind-protocol/vectors/draft/${name}`, import.meta.url)
const readVector = name => fs.readFileSync(vectorUrl(name))
const masterSpecUrl = new URL('../../docs/protocol/BLIND-APP-AGNOSTIC-HIVERELAY-MASTER-SPEC.md', import.meta.url)
const masterSchemaCatalog = () => compileMasterSchemaCatalog(fs.readFileSync(masterSpecUrl, 'utf8'))

test('blind vectors: checked-in dispatch bytes are frozen', (t) => {
  const cellGet = readVector('dispatch/cell-get-request.bin')
  const forwardData = readVector('dispatch/forward-data.bin')
  t.is(
    b4a.toString(cellGet, 'hex'),
    '0000002d010102020000112233445566778899aabbccddeeff000000000000000000000000000000000000000401020304'
  )
  t.is(
    b4a.toString(forwardData, 'hex'),
    '0000002d0104050200000000000000000000000000000000000102030405060708000000000000000900000004a0a1a2a3'
  )
  t.is(decodeDispatchFrame(cellGet).familyId, 2)
  t.is(decodeDispatchFrame(forwardData).streamId, 0x0102030405060708n)
})

test('blind vectors: negative frames and padded outer fixture behave as declared', (t) => {
  t.exception(() => decodeDispatchFrame(readVector('invalid/dispatch-nonzero-flags.bin')), /flags are reserved/)
  t.exception(() => decodeDispatchFrame(readVector('invalid/dispatch-unknown-operation.bin')), /unknown family\/operation/)
  const outer = decodeOuterEnvelope(readVector('outer/cell-get-class-1.bin'))
  t.is(outer.outerClass, 1)
  t.is(outer.frame.familyId, 2)
})

test('blind vectors: checked-in authority is reproducible from the canonical registry', (t) => {
  const artifact = fs.readFileSync(new URL('../../packages/blind-protocol/hiverelay-blind-abi-v1.cenc', import.meta.url))
  const alias = fs.readFileSync(new URL('../../packages/blind-protocol/hiverelay-blind-abi-v1.draft.cenc', import.meta.url))
  t.ok(b4a.equals(alias, artifact))
  t.is(hashAbi(artifact).byteLength, 32)
  t.ok(encodeWireAbiRegistry().byteLength > 0)
})

test('blind vectors: final WIRE manifest is category-isolated, complete and byte-reproducible', t => {
  const packageUrl = new URL('../../packages/blind-protocol/', import.meta.url)
  const manifest = fs.readFileSync(new URL('vector-manifest-v1.cenc', packageUrl))
  const alias = fs.readFileSync(new URL('vectors/draft/vector-manifest-v1.draft.cenc', packageUrl))
  t.alike(alias, manifest)
  const entries = decodeVectorManifest(manifest)
  t.is(entries.length, 237)
  t.ok(entries.some(entry => entry.path === 'registry/release-operation-profile-v1.json'))
  t.ok(entries.some(entry => entry.path === 'registry/wire-schema-catalog.bin'))
  t.is(entries.filter(entry => entry.path.startsWith('registry/schemas/')).length, 73)
  t.is(entries.filter(entry => entry.path.startsWith('registry/operations/')).length, 22)
  t.is(entries.filter(entry => entry.path.startsWith('registry/domains/')).length, 40)
  t.is(entries.filter(entry => entry.path.startsWith('registry/errors/')).length, 20)
  t.is(entries.filter(entry => entry.path.startsWith('registry/admission-costs/')).length, 11)
  for (const entry of entries) {
    t.absent(entry.path.startsWith('store/'), `${entry.path} is not an internal-store vector`)
    t.absent([
      'registry/master-schema-catalog.bin',
      'registry/evidence-schema-catalog.bin',
      'registry/client-example-schema-catalog.bin',
      'registry/internal-store-schema-catalog.bin'
    ].includes(entry.path), `${entry.path} is not a non-WIRE catalog`)
    const bytes = fs.readFileSync(new URL(`vectors/${entry.path}`, packageUrl))
    t.is(entry.vectorLength, BigInt(bytes.byteLength), `${entry.path} length`)
    t.alike(entry.vectorHash, blake2b256(bytes), `${entry.path} hash`)
  }
  const abi = fs.readFileSync(new URL('hiverelay-blind-abi-v1.cenc', packageUrl))
  for (const category of [SCHEMA_CATEGORY.EVIDENCE, SCHEMA_CATEGORY.CLIENT_EXAMPLE,
    SCHEMA_CATEGORY.INTERNAL_STORE, SCHEMA_CATEGORY.PRIVATE_IPC]) {
    for (const schemaName of SCHEMA_NAMES_BY_CATEGORY[category]) {
      t.absent(abi.includes(b4a.from(schemaName, 'ascii')), `${schemaName} is absent from WIRE ABI bytes`)
    }
  }

  const authority = JSON.parse(fs.readFileSync(
    new URL('hiverelay-blind-wire-authority-v1.json', packageUrl), 'utf8'))
  const spec = fs.readFileSync(new URL('../../docs/protocol/HIVERELAY-BLIND-WIRE-V1.md', import.meta.url))
  const master = fs.readFileSync(masterSpecUrl)
  t.is(authority.specHash, b4a.toString(hashSpec(spec), 'hex'))
  t.absent(authority.specHash === b4a.toString(hashSpec(master), 'hex'),
    'public specHash does not import the full master/store/private prose')
  const changedMaster = b4a.concat([master, b4a.from('internal-store-only-edit')])
  t.is(authority.specHash, b4a.toString(hashSpec(spec), 'hex'))
  t.absent(b4a.equals(hashSpec(master), hashSpec(changedMaster)))
  t.is(authority.abiHash, b4a.toString(hashAbi(abi), 'hex'))
  t.is(authority.vectorSetHash, b4a.toString(hashVectorSet(manifest), 'hex'))
})

test('blind vectors: master catalog contains every canonical schema', t => {
  const catalog = masterSchemaCatalog()
  t.is(catalog.entries.length, 152)
  const complete = decodeSchemaCatalog(readVector('registry/master-schema-catalog.bin'), {
    minimum: 152,
    maximum: 152
  })
  t.is(complete.length, 152)
  for (const [name, count] of [
    ['wire', 73],
    ['evidence', 28],
    ['client-example', 6],
    ['internal-store', 38]
  ]) {
    t.is(decodeSchemaCatalog(readVector(`registry/${name}-schema-catalog.bin`), {
      minimum: count,
      maximum: count
    }).length, count)
  }
})

test('blind vectors: WIRE catalog remains complete after unrelated category edits', t => {
  let isolatedMaster = fs.readFileSync(masterSpecUrl, 'utf8')
  for (const name of ['BuildManifestV1', 'ReadCellCapV1', 'BlindStoreManifestV1', 'LocalDispatchV1']) {
    const declaration = `${name} {`
    t.ok(isolatedMaster.includes(declaration), `${name} declaration exists in the full master`)
    isolatedMaster = isolatedMaster.replace(declaration, `${name}Removed {`)
  }
  const wireCatalog = compileMasterSchemaCatalogForCategory(isolatedMaster, SCHEMA_CATEGORY.WIRE)
  t.is(wireCatalog.entries.length, 73)
  t.exception(() => compileMasterSchemaCatalog(isolatedMaster), /inventory does not match/)
})

test('blind vectors: version-2 WAL header is executable store-format authority', t => {
  const header = readVector('store/wal-header-v2.bin')
  t.is(header.byteLength, 192)
  const decoded = decodeCanonical(blindWalHeaderV2, header)
  t.is(decoded.walVersion, 2)
  t.is(decoded.totalLength, 241)
  t.is(decoded.payloadLength, 17)
  t.alike(encodeCanonical(blindWalHeaderV2, decoded), header)
})

test('blind vectors: local checkpoint binds the frozen canonical control snapshot', t => {
  const snapshotBytes = readVector('store/control-state-snapshot-v1.bin')
  const snapshot = decodeCanonical(blindControlStateSnapshotV1, snapshotBytes)
  const checkpointBytes = readVector('store/local-checkpoint-v1.bin')
  const checkpoint = decodeCanonical(blindLocalCheckpointV1, checkpointBytes)
  t.is(snapshot.entries.length, 3)
  t.is(checkpoint.checkpointRevision, 1n)
  t.is(checkpoint.coveredWalSequence, 9n)
  t.is(checkpoint.snapshotByteLength, BigInt(snapshotBytes.byteLength))
  t.alike(checkpoint.snapshotHash, controlSnapshotHash(snapshotBytes))
  t.is(b4a.toString(controlSnapshotHash(snapshotBytes), 'hex'),
    '4157fea639272105bbd1771686f3fd22a644f388e4addc8d6e1934c726cec218')
  t.alike(snapshot.relayPublicKey, checkpoint.relayPublicKey)
  t.alike(snapshot.storeId, checkpoint.storeId)
  t.alike(snapshot.durabilityContinuityHash, checkpoint.durabilityContinuityHash)
  t.is(snapshot.walSequence, checkpoint.coveredWalSequence)
  t.alike(snapshot.walHash, checkpoint.coveredWalHash)
  t.is(localCheckpointHash(checkpointBytes).byteLength, 32)
  t.alike(encodeCanonical(blindControlStateSnapshotV1, snapshot), snapshotBytes)
  t.alike(encodeCanonical(blindLocalCheckpointV1, checkpoint), checkpointBytes)
})

test('blind vectors: public durability and persistent-result binding fixtures decode', t => {
  for (const [name, codec] of [
    ['external-commit-witness', blindExternalCommitWitnessV1],
    ['relay-result-binding', relayResultBindingV1],
    ['batch-signature-payload', batchGetSignaturePayloadV1],
    ['inbox-signature-payload', inboxReadSignaturePayloadV1],
    ['continuity-binding', durabilityContinuityBindingV1],
    ['backup-encryption-profile', blindBackupEncryptionProfileV1],
    ['backup-chunk-manifest', blindBackupChunkManifestV1],
    ['backup-manifest', blindBackupManifestV1],
    ['clean-restore', blindCleanRestoreEvidenceV1],
    ['retention-transition', blindBackupRetentionTransitionV1],
    ['restore-head', blindRestoreEvidenceHeadV1],
    ['restore-bundle', blindRestoreEvidenceBundleV1]
  ]) {
    const bytes = readVector(`durability/${name}.bin`)
    const decoded = decodeCanonical(codec, bytes)
    t.ok(decoded)
    t.alike(encodeCanonical(codec, decoded), bytes)
  }
})

test('blind vectors: CELL/INBOX bodies and registry rows are executable', t => {
  t.is(decodeCanonical(putCellV1, readVector('cell/put-class-1.bin')).cellBlob.byteLength, 4096)
  t.is(decodeCanonical(batchGetResultV1, readVector('cell/batch-get-result.bin')).entries[1].status, 1)
  t.is(decodeCanonical(inboxAppendV1, readVector('inbox/append-class-1.bin')).frame.byteLength, 4096)
  t.is(decodeCanonical(inboxReadResultV1, readVector('inbox/read-result-empty.bin')).entries.length, 0)
  t.is(decodeCanonical(operationProfileV1, readVector('registry/cell-put-operation-profile.bin')).requestCommitmentDomainId, 1)
  t.is(decodeCanonical(domainRegistryEntryV1, readVector('registry/cell-put-domain.bin')).domainId, 1)
  t.is(decodeCanonical(errorProfileEntryV1,
    readVector('registry/renew-not-due-error-profile.bin')).retryAfterMode, 1)
  t.is(b4a.toString(readVector('commitment/cell-put.bin'), 'hex'), '5cd5e4bb62e986c97b2426c8727a7610a7253ec78e539d2041385d89aafd86ff')
  t.is(b4a.toString(readVector('commitment/inbox-read.bin'), 'hex'), '6c65ea21c8d71e6ee649bf47f951bb3c4b78d30ee9e6f41431cb1145c9a09712')
  t.is(b4a.toString(readVector('commitment/core-mirror.bin'), 'hex'), 'b1b78e44b1530ea6317925f80fd92e9c93ba1e454e042f88a73fe5d6eb649f98')
  t.is(b4a.toString(readVector('commitment/core-serve.bin'), 'hex'), '01e645341ecbfbf4903950d1bfcbd83d91c60f86bdebaf532080d4e1e39993ef')
  t.is(b4a.toString(readVector('commitment/forward-open.bin'), 'hex'), '9e84ef026c7867f410684adb0c66c440e8babc965cba804c3343a026d931efaa')
})

test('blind vectors: malformed CELL/INBOX and registry bytes remain negative', t => {
  t.exception(() => decodeCanonical(getCellResultV1, readVector('invalid/cell-get-unknown-class.bin')), /outside 1..5/)
  t.exception(() => decodeCanonical(batchGetV1, readVector('invalid/batch-get-duplicate-slots.bin')), /duplicate/)
  t.exception(() => decodeCanonical(batchGetResultV1, readVector('invalid/batch-get-unknown-entry-tag.bin')), /status must be 0 or 1/)
  t.exception(() => decodeCanonical(inboxReadV1, readVector('invalid/inbox-read-cursor-129.bin')), /outside 0..128/)
  t.exception(() => decodeCanonical(operationProfileV1, readVector('invalid/operation-profile-wrong-domain-purpose.bin')), /not a registered request domain/)
  t.exception(() => decodeCanonical(domainRegistryEntryV1, readVector('invalid/domain-entry-wrong-purpose.bin')), /not in the frozen registry/)
  t.exception(() => decodeCanonical(errorProfileEntryV1,
    readVector('invalid/error-profile-wrong-retryable.bin')), /not in the frozen registry/)
})

test('blind vectors: frozen public codecs remain executable, including reserved rows', t => {
  const vectors = [
    ['describe/admission-parameters.bin', admissionParametersV1],
    ['describe/admission-profile.bin', admissionProfileV1],
    ['describe/dht-pointer.bin', blindDhtPointerV1],
    ['describe/external-journal-topology.bin', blindExternalJournalTopologyV1],
    ['describe/health-challenge.bin', blindHealthChallengeV1],
    ['describe/health-result.bin', blindHealthResultV1],
    ['describe/identity-transition.bin', relayIdentityTransitionV1],
    ['describe/ohttp-key-config.bin', blindOhttpKeyConfigV1],
    ['describe/schema-catalog-entry.bin', schemaCatalogEntryV1],
    ['describe/service-descriptor.bin', blindServiceDescriptorV1],
    ['core/ack.bin', blindCoreAckV1],
    ['core/mirror-request.bin', coreMirrorRequestV1],
    ['core/serve-challenge.bin', coreServeChallengeV1],
    ['core/serve-result.bin', coreServeResultV1],
    ['forward/open.bin', blindForwardOpenV1],
    ['forward/open-result.bin', blindForwardOpenResultV1],
    ['forward/data-body.bin', blindForwardDataV1],
    ['forward/window.bin', blindForwardWindowV1],
    ['forward/close.bin', blindForwardCloseV1],
    ['forward/stream-chunk-class-1.bin', blindStreamChunkPlainV1]
  ]
  for (const [name, codec] of vectors) {
    const bytes = readVector(name)
    t.ok(decodeCanonical(codec, bytes), `${name} decodes`)
    for (const end of [...new Set([0, 1, Math.floor(bytes.byteLength / 2), bytes.byteLength - 1])]) {
      t.exception(() => decodeCanonical(codec, bytes.subarray(0, end)))
    }
    t.exception(() => decodeCanonical(codec, b4a.concat([bytes, b4a.from([0])])))
  }
  t.exception(() => decodeCanonical(blindStreamChunkPlainV1,
    readVector('invalid/stream-chunk-unknown-class.bin')), /outside 1\.\.3/)
})

test('blind vectors: all ten newly closed public operation rows are checked in', t => {
  const names = [
    'describe-get',
    'describe-challenge',
    'describe-admission-parameters',
    'core-mirror',
    'core-prove',
    'core-open-replication',
    'forward-open',
    'forward-data',
    'forward-window',
    'forward-close'
  ]
  for (const name of names) {
    const row = decodeCanonical(operationProfileV1, readVector(`registry/${name}-operation-profile.bin`))
    t.ok(row.familyId === 1 || row.familyId === 4 || row.familyId === 5, `${name} family`)
  }
})