import fs from 'node:fs'
import test from 'brittle'
import b4a from 'b4a'
import {
CLIENT_COMPOSITION_AUTHORITY_STATUS,
CLIENT_COMPOSITION_AUTHORITY_V1,
CLIENT_COMPOSITION_FORMAT_LAYOUT_V1,
CLIENT_COMPOSITION_SCHEMA_NAMES_V1,
CLIENT_COMPOSITION_VECTOR_EXPECTATIONS_V1,
assertClientCompositionAuthorityReady,
compileMasterSchemaCatalogForCategory,
decodeClientCompositionFormatAuthorityV1,
decodeSchemaCatalog,
decodeVectorManifest,
encodeClientCompositionFormatAuthorityV1,
encodeSchemaCatalog,
hashAbi,
hashClientCompositionFormat,
hashClientCompositionVectorSet,
hashSpec,
hashVectorSet,
isVerifiedClientCompositionAuthorityV1,
SCHEMA_CATEGORY,
verifyClientCompositionAuthorityV1,
verifyClientCompositionFormatAuthorityV1,
verifyClientCompositionVectorSetV1
} from '../index.js'
const packageUrl = new URL('../', import.meta.url)
const rootUrl = new URL('../../../', import.meta.url)
const artifact = name => fs.readFileSync(new URL(name, packageUrl))
const spec = () => fs.readFileSync(new URL(
'docs/protocol/HIVERELAY-BLIND-CLIENT-COMPOSITION-V1.md', rootUrl))
const master = () => fs.readFileSync(new URL(
'docs/protocol/BLIND-APP-AGNOSTIC-HIVERELAY-MASTER-SPEC.md', rootUrl), 'utf8')
const metadata = () => JSON.parse(artifact('hiverelay-blind-client-composition-authority-v1.json'))
function vectorMap () {
return new Map(CLIENT_COMPOSITION_VECTOR_EXPECTATIONS_V1.map(expectation => [
expectation.path,
artifact(`vectors/client-composition/${expectation.path}`)
]))
}
function authorityInput (overrides = {}) {
const value = metadata()
return {
formatAuthorityBytes: artifact('hiverelay-blind-client-composition-format-v1.cenc'),
specBytes: spec(),
schemaCatalogBytes: artifact('hiverelay-blind-client-composition-schema-catalog-v1.cenc'),
vectorManifestBytes: artifact('hiverelay-blind-client-composition-vector-manifest-v1.cenc'),
vectors: vectorMap(),
expectedFormatHash: b4a.from(value.formatHash, 'hex'),
expectedVectorSetHash: b4a.from(value.vectorSetHash, 'hex'),
...overrides
}
}
test('final client-composition artifacts reproduce and verify as one isolated authority', t => {
const input = authorityInput()
const value = metadata()
const compiled = compileMasterSchemaCatalogForCategory(master(), SCHEMA_CATEGORY.CLIENT_EXAMPLE)
const compiledCatalog = encodeSchemaCatalog(compiled.entries, { minimum: 6, maximum: 6 })
const decoded = decodeClientCompositionFormatAuthorityV1(input.formatAuthorityBytes)
t.is(CLIENT_COMPOSITION_FORMAT_LAYOUT_V1.magic, 'HRBCCF01')
t.is(decoded.authorityVersion, 1)
t.is(decoded.formatMajor, 1)
t.is(decoded.formatMinor, 0)
t.alike(decoded.specBytes, input.specBytes)
t.alike(decoded.schemaCatalogBytes, input.schemaCatalogBytes)
t.alike(compiledCatalog, input.schemaCatalogBytes)
t.alike(encodeClientCompositionFormatAuthorityV1(input.specBytes, input.schemaCatalogBytes),
input.formatAuthorityBytes)
t.is(b4a.toString(hashClientCompositionFormat(input.formatAuthorityBytes), 'hex'), value.formatHash)
t.is(b4a.toString(hashClientCompositionVectorSet(input.vectorManifestBytes), 'hex'), value.vectorSetHash)
const verified = verifyClientCompositionAuthorityV1(input)
t.ok(isVerifiedClientCompositionAuthorityV1(verified))
t.is(verified.profile, 'client-composition-authority-v1')
t.is(verified.vectorCount, 18)
t.alike(verified.schemaNames, CLIENT_COMPOSITION_SCHEMA_NAMES_V1)
t.ok(CLIENT_COMPOSITION_AUTHORITY_STATUS.releaseReady)
t.alike(CLIENT_COMPOSITION_AUTHORITY_STATUS.releaseBlockers, [])
t.alike(assertClientCompositionAuthorityReady(), CLIENT_COMPOSITION_AUTHORITY_STATUS)
t.is(CLIENT_COMPOSITION_AUTHORITY_V1.formatHash, value.formatHash)
t.is(CLIENT_COMPOSITION_AUTHORITY_V1.vectorSetHash, value.vectorSetHash)
t.ok(Object.isFrozen(CLIENT_COMPOSITION_AUTHORITY_V1))
t.ok(Object.isFrozen(CLIENT_COMPOSITION_AUTHORITY_V1.schemaNames))
t.ok(Object.isFrozen(CLIENT_COMPOSITION_AUTHORITY_STATUS))
})
test('client-composition API and final metadata are explicitly package-exported', t => {
const packageManifest = JSON.parse(artifact('package.json'))
t.is(packageManifest.exports['./client-composition-authority'],
'./client-composition-authority-entry.js')
t.is(packageManifest.exports['./client-composition-runtime-vectors'],
'./client-composition-runtime-vectors.js')
t.ok(packageManifest.files.includes('hiverelay-blind-client-composition-authority-v1.json'))
for (const name of [
'hiverelay-blind-client-composition-format-v1.cenc',
'hiverelay-blind-client-composition-schema-catalog-v1.cenc',
'hiverelay-blind-client-composition-vector-manifest-v1.cenc'
]) t.ok(packageManifest.files.includes('*.cenc') && fs.existsSync(new URL(name, packageUrl)), name)
})
test('client-composition vector manifest has a closed path set and executable semantics', t => {
const input = authorityInput()
const entries = decodeVectorManifest(input.vectorManifestBytes)
t.is(entries.length, CLIENT_COMPOSITION_VECTOR_EXPECTATIONS_V1.length)
t.alike(entries.map(entry => entry.path),
CLIENT_COMPOSITION_VECTOR_EXPECTATIONS_V1.map(entry => entry.path))
t.is(entries.filter(entry => entry.path.startsWith('positive/cell-blob-v1-class-')).length, 5)
const verified = verifyClientCompositionVectorSetV1(input.vectorManifestBytes, input.vectors, {
schemaCatalogBytes: input.schemaCatalogBytes,
expectedVectorSetHash: input.expectedVectorSetHash
})
t.is(verified.vectorCount, 18)
const tampered = vectorMap()
const path = 'positive/read-cell-cap-v1.bin'
tampered.get(path)[0] ^= 1
t.exception(() => verifyClientCompositionVectorSetV1(input.vectorManifestBytes, tampered, {
schemaCatalogBytes: input.schemaCatalogBytes
}), /does not match its manifest row/)
const extra = vectorMap()
extra.set('positive/extra.bin', b4a.from([1]))
t.exception(() => verifyClientCompositionVectorSetV1(input.vectorManifestBytes, extra, {
schemaCatalogBytes: input.schemaCatalogBytes
}), /missing or extra/)
t.exception(() => verifyClientCompositionVectorSetV1(
b4a.concat([input.vectorManifestBytes, b4a.from([0])]), input.vectors, {
schemaCatalogBytes: input.schemaCatalogBytes
}), /trailing bytes/)
const accessor = {}
for (const [key, bytes] of input.vectors) {
if (key !== path) Object.defineProperty(accessor, key, { value: bytes, enumerable: true })
}
Object.defineProperty(accessor, path, { get () { return input.vectors.get(path) }, enumerable: true })
t.exception(() => verifyClientCompositionVectorSetV1(input.vectorManifestBytes, accessor, {
schemaCatalogBytes: input.schemaCatalogBytes
}), /must not use accessors/)
})
test('client-composition authority rejects stale pins, framing drift and foreign catalogs', t => {
const input = authorityInput()
const unpinned = { ...input }
delete unpinned.expectedVectorSetHash
t.exception(() => verifyClientCompositionAuthorityV1(unpinned), /requires both expected hash pins/)
const trailing = b4a.concat([input.formatAuthorityBytes, b4a.from([0])])
t.exception(() => decodeClientCompositionFormatAuthorityV1(trailing), /trailing bytes/)
const badMagic = b4a.from(input.formatAuthorityBytes)
badMagic[0] ^= 1
t.exception(() => decodeClientCompositionFormatAuthorityV1(badMagic), /invalid magic/)
const hugeLength = b4a.from(input.formatAuthorityBytes)
hugeLength.fill(0xff, 14, 22)
t.exception(() => decodeClientCompositionFormatAuthorityV1(hugeLength), /invalid component length/)
const staleSpec = b4a.concat([input.specBytes.subarray(0, input.specBytes.byteLength - 1), b4a.from('x\n')])
t.exception(() => verifyClientCompositionFormatAuthorityV1(input.formatAuthorityBytes, {
specBytes: staleSpec
}), /expected specification/)
const wrongHash = b4a.from(input.expectedFormatHash)
wrongHash[0] ^= 1
t.exception(() => verifyClientCompositionFormatAuthorityV1(input.formatAuthorityBytes, {
expectedFormatHash: wrongHash
}), /expected format hash/)
const foreignCatalog = artifact('hiverelay-blind-store-schema-catalog-v1.draft.cenc')
t.exception(() => encodeClientCompositionFormatAuthorityV1(input.specBytes, foreignCatalog),
/schema catalog/)
})
test('client-composition artifacts cannot perturb WIRE or import relay persistence', t => {
const wire = JSON.parse(artifact('hiverelay-blind-wire-authority-v1.json'))
t.is(wire.specHash, 'c9ddd235c3963461174e3de13c25a4c995b53ff320be822d8304f870766b6592')
t.is(wire.abiHash, '199ba15d94d4d112cfac520a67055ce15ec870f0f6f7bd9adaaf47d552334567')
t.is(wire.vectorSetHash, 'fa54012cd0d7e4e620878c67e61f435ecb31ddec05a6283917987cc84279ee05')
t.is(b4a.toString(hashSpec(fs.readFileSync(new URL(
'docs/protocol/HIVERELAY-BLIND-WIRE-V1.md', rootUrl))), 'hex'), wire.specHash)
t.is(b4a.toString(hashAbi(artifact('hiverelay-blind-abi-v1.cenc')), 'hex'), wire.abiHash)
t.is(b4a.toString(hashVectorSet(artifact('vector-manifest-v1.cenc')), 'hex'), wire.vectorSetHash)
const input = authorityInput()
const decodedCatalog = decodeSchemaCatalog(input.schemaCatalogBytes, { minimum: 6, maximum: 6 })
t.ok(decodedCatalog.every(entry => entry.category === SCHEMA_CATEGORY.CLIENT_EXAMPLE))
t.alike(decodedCatalog.map(entry => b4a.toString(entry.schemaName, 'ascii')),
CLIENT_COMPOSITION_SCHEMA_NAMES_V1)
const forbiddenName = b4a.from('BlindStoreManifestV1', 'ascii')
const storeCatalog = artifact('hiverelay-blind-store-schema-catalog-v1.draft.cenc')
for (const [name, bytes] of [
['format authority', input.formatAuthorityBytes],
['schema catalog', input.schemaCatalogBytes],
['vector manifest', input.vectorManifestBytes],
['metadata', artifact('hiverelay-blind-client-composition-authority-v1.json')]
]) {
t.absent(bytes.includes(forbiddenName), `${name} excludes relay persistence schema names`)
t.absent(bytes.includes(storeCatalog), `${name} excludes relay persistence catalog bytes`)
}
for (const [vectorPath, bytes] of input.vectors) {
t.absent(vectorPath.startsWith('store/'), `${vectorPath} is not a persistence path`)
t.absent(bytes.includes(forbiddenName), `${vectorPath} excludes relay persistence schema names`)
}
})
test('authority verification copies mutable inputs and rejects shared vector memory', t => {
const input = authorityInput()
const verified = verifyClientCompositionAuthorityV1(input)
const formatHash = b4a.from(verified.formatHash)
input.formatAuthorityBytes.fill(0)
input.expectedFormatHash.fill(0)
t.alike(verified.formatHash, formatHash)
if (typeof SharedArrayBuffer !== 'undefined') {
const sharedVectors = vectorMap()
const path = 'positive/read-cell-cap-v1.bin'
const source = sharedVectors.get(path)
const shared = new Uint8Array(new SharedArrayBuffer(source.byteLength))
shared.set(source)
sharedVectors.set(path, shared)
t.exception(() => verifyClientCompositionVectorSetV1(
authorityInput().vectorManifestBytes, sharedVectors, {
schemaCatalogBytes: authorityInput().schemaCatalogBytes
}), /cannot use shared memory/)
} else {
t.pass('SharedArrayBuffer is unavailable in this runtime')
}
})