OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / packages / blind-protocol / test / external-profile-decoder.test.js

249 lines · javascript ref:
import test from 'brittle'
import b4a from 'b4a'
import * as externalDecoderAuthority from '../external-profile-decoder.js'
import {
  blindCoreReadCapV1,
  readCellCapV1
} from '../client-composition-external-codecs.js'
import { encodeCanonical } from '../codec.js'
import {
  blindCoreAckV1,
  blindReceiptV1,
  inboxAppendAckV1,
  inboxReceiptV1
} from '../schemas.js'

const { decodeBlindExternalProfileValueV1 } = externalDecoderAuthority
const bytes = (length, value) => b4a.alloc(length, value)

function throwsBadEncoding (t, operation, message) {
  try {
    operation()
    t.fail(message)
  } catch (error) {
    t.is(error.code, 'BAD_ENCODING', message)
  }
}

function relayBinding (seed) {
  return {
    version: 1,
    relayPublicKey: bytes(32, seed),
    storeId: bytes(32, seed + 1),
    descriptorSequence: 1n,
    descriptorHash: bytes(32, seed + 2),
    durabilityProfileId: 1,
    durabilityContinuityHash: bytes(32, seed + 3),
    durabilityProfileHash: bytes(32, seed + 4),
    restoreEvidenceHeadSequence: 0n,
    restoreEvidenceHeadHash: bytes(32, 0),
    externalCommitWitness: null
  }
}

function fixtureBytes () {
  const coreAck = {
    version: 1,
    relayBinding: relayBinding(0x11),
    corePublicKey: bytes(32, 0x16),
    fork: 2n,
    length: 16n,
    signedHeadHash: bytes(32, 0x17),
    observedAtEpoch: 100,
    leaseEpoch: 104,
    result: 2,
    requestNonce: bytes(32, 0x18),
    requestCommitment: bytes(32, 0x19),
    signature: bytes(64, 0x1a)
  }
  const coreReadCap = {
    version: 1,
    corePublicKey: bytes(32, 0x21),
    blockEncryptionKey: bytes(32, 0x22),
    witnessedFork: 3n,
    witnessedLength: 9n,
    witnessedSignedHead: bytes(8, 0x23)
  }
  const cellReceipt = {
    version: 1,
    protocol: b4a.from('hiverelay-blind-cell-v1', 'ascii'),
    relayBinding: relayBinding(0x31),
    slotCommitment: bytes(32, 0x36),
    cellBlobHash: bytes(32, 0x37),
    allocationCommitment: bytes(32, 0x38),
    requestCommitment: bytes(32, 0x39),
    sizeClass: 1,
    allocationEpoch: 10,
    leaseClass: 2,
    leaseEpoch: 11,
    stateRevision: 12n,
    receiptEpoch: 13,
    requestNonce: bytes(32, 0x3a),
    result: 1,
    signature: bytes(64, 0x3b)
  }
  const appendAck = {
    version: 1,
    relayBinding: relayBinding(0x41),
    topicCommitment: bytes(32, 0x46),
    frameHash: bytes(32, 0x47),
    appendRevision: 12n,
    storedAtEpoch: 13,
    expiresAtEpoch: 14,
    requestNonce: bytes(32, 0x48),
    requestCommitment: bytes(32, 0x49),
    result: 1,
    signature: bytes(64, 0x4a)
  }
  const inboxReceipt = {
    version: 1,
    relayBinding: relayBinding(0x51),
    topicCommitment: bytes(32, 0x56),
    stateRevision: 10n,
    leaseClass: 2,
    leaseEpoch: 11,
    requestNonce: bytes(32, 0x57),
    requestCommitment: bytes(32, 0x58),
    result: 1,
    signature: bytes(64, 0x59)
  }
  const readCellCap = {
    version: 1,
    relayPublicKey: bytes(32, 0x61),
    storageSlot: bytes(32, 0x62),
    cellKey: bytes(32, 0x63),
    sizeClass: 1,
    expectedCellBlobHash: bytes(32, 0x64)
  }
  return new Map([
    ['BlindCoreAckV1', encodeCanonical(blindCoreAckV1, coreAck)],
    ['BlindCoreReadCapV1', encodeCanonical(blindCoreReadCapV1, coreReadCap)],
    ['BlindReceiptV1', encodeCanonical(blindReceiptV1, cellReceipt)],
    ['InboxAppendAckV1', encodeCanonical(inboxAppendAckV1, appendAck)],
    ['InboxReceiptV1', encodeCanonical(inboxReceiptV1, inboxReceipt)],
    ['ReadCellCapV1', encodeCanonical(readCellCapV1, readCellCap)]
  ])
}

test('external-profile decoder exposes one closed six-type executable authority', t => {
  t.alike(Object.keys(externalDecoderAuthority), ['decodeBlindExternalProfileValueV1'])
  const fixtures = fixtureBytes()
  t.alike([...fixtures.keys()], [
    'BlindCoreAckV1',
    'BlindCoreReadCapV1',
    'BlindReceiptV1',
    'InboxAppendAckV1',
    'InboxReceiptV1',
    'ReadCellCapV1'
  ])
  for (const [name, encoded] of fixtures) {
    const decoded = decodeBlindExternalProfileValueV1(name, encoded)
    t.ok(Object.isFrozen(decoded), `${name} result container is frozen`)
    if (decoded.relayBinding != null) {
      t.ok(Object.isFrozen(decoded.relayBinding), `${name} nested result container is frozen`)
    }
  }

  for (const name of [
    '', 'BlindHealthResultV1', 'WriteCellCapV1', 'CellBlobV1',
    'BlindStoreManifestV1', '__proto__'
  ]) {
    throwsBadEncoding(t, () => decodeBlindExternalProfileValueV1(
      name, fixtures.get('ReadCellCapV1')), `unknown ${name || '<empty>'} is rejected`)
  }
  throwsBadEncoding(t, () => decodeBlindExternalProfileValueV1(
    { name: 'ReadCellCapV1', codec: readCellCapV1 }, fixtures.get('ReadCellCapV1')),
  'raw schema injection is rejected')
  throwsBadEncoding(t, () => decodeBlindExternalProfileValueV1(
    'ReadCellCapV1', fixtures.get('ReadCellCapV1'), () => true),
  'callback injection is rejected')

  const originalMapGet = Map.prototype.get
  const canonicalReadCap = fixtures.get('ReadCellCapV1')
  let mapSelectionCalls = 0
  try {
    Reflect.set(Map.prototype, 'get', function () {
      mapSelectionCalls++
      return { codec: readCellCapV1, minimum: 0, maximum: Number.MAX_SAFE_INTEGER }
    })
    t.is(decodeBlindExternalProfileValueV1(
      'ReadCellCapV1', canonicalReadCap).sizeClass, 1,
    'global Map prototype mutation cannot replace the closed selector')
  } finally {
    Reflect.set(Map.prototype, 'get', originalMapGet)
  }
  t.is(mapSelectionCalls, 0, 'closed selection does not dispatch through a mutable registry primitive')
})

test('external-profile decoder rejects truncation, trailing, oversize, noncanonical and wrong-type bytes', t => {
  const fixtures = fixtureBytes()
  const maximums = new Map([
    ['BlindCoreAckV1', 16384],
    ['BlindCoreReadCapV1', 8192],
    ['BlindReceiptV1', 16384],
    ['InboxAppendAckV1', 16384],
    ['InboxReceiptV1', 16384],
    ['ReadCellCapV1', 131]
  ])
  for (const [name, encoded] of fixtures) {
    throwsBadEncoding(t, () => decodeBlindExternalProfileValueV1(
      name, encoded.subarray(0, encoded.byteLength - 1)), `${name} truncation is rejected`)
    throwsBadEncoding(t, () => decodeBlindExternalProfileValueV1(
      name, b4a.concat([encoded, b4a.from([0])])), `${name} trailing byte is rejected`)
    throwsBadEncoding(t, () => decodeBlindExternalProfileValueV1(
      name, bytes(maximums.get(name) + 1, 1)), `${name} oversize input is rejected before decode`)
  }

  const coreCap = fixtures.get('BlindCoreReadCapV1')
  const compactLengthOffset = 1 + 32 + 32 + 8 + 8
  t.is(coreCap[compactLengthOffset], 8, 'fixture uses the canonical single-byte compact length')
  const noncanonical = b4a.concat([
    coreCap.subarray(0, compactLengthOffset),
    b4a.from([0xfd, 8, 0]),
    coreCap.subarray(compactLengthOffset + 1)
  ])
  throwsBadEncoding(t, () => decodeBlindExternalProfileValueV1(
    'BlindCoreReadCapV1', noncanonical), 'overlong compact length is rejected')

  for (const [actualName, encoded] of fixtures) {
    for (const expectedName of fixtures.keys()) {
      if (actualName === expectedName) continue
      throwsBadEncoding(t, () => decodeBlindExternalProfileValueV1(
        expectedName, encoded), `${actualName} bytes cannot decode as ${expectedName}`)
    }
  }
})

test('external-profile decoded values have no input or cross-call byte aliases', t => {
  const fixtures = fixtureBytes()
  const source = fixtures.get('BlindReceiptV1')
  const input = b4a.from(source)
  const decoded = decodeBlindExternalProfileValueV1('BlindReceiptV1', input)
  const relayKey = b4a.from(decoded.relayBinding.relayPublicKey)
  input.fill(0)
  t.ok(b4a.equals(decoded.relayBinding.relayPublicKey, relayKey),
    'mutating the caller input cannot mutate the decoded result')

  decoded.relayBinding.relayPublicKey.fill(0)
  const second = decodeBlindExternalProfileValueV1('BlindReceiptV1', source)
  t.ok(b4a.equals(second.relayBinding.relayPublicKey, relayKey),
    'mutating one returned byte copy cannot poison the authority or another call')

  const cap = fixtures.get('ReadCellCapV1')
  const offset = 7
  const view = new Uint8Array(cap.buffer, cap.byteOffset + offset, cap.byteLength - offset)
  const padded = b4a.alloc(cap.byteLength + offset, 0xee)
  padded.set(cap, offset)
  const dataView = new DataView(padded.buffer, padded.byteOffset + offset, cap.byteLength)
  t.is(decodeBlindExternalProfileValueV1('ReadCellCapV1', dataView).sizeClass, 1,
    'an offset ArrayBuffer view is copied and decoded exactly')
  t.is(view.byteLength, cap.byteLength - offset, 'fixture subview remains independent')

  if (typeof SharedArrayBuffer !== 'undefined') {
    const shared = new Uint8Array(new SharedArrayBuffer(cap.byteLength))
    shared.set(cap)
    throwsBadEncoding(t, () => decodeBlindExternalProfileValueV1(
      'ReadCellCapV1', shared), 'shared-memory input is rejected')
  }
})