OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / packages / blind-protocol / client-composition-runtime-vectors.js

157 lines · javascript ref:
import b4a from 'b4a'
import {
  CELL_BLOB_V1_BY_SIZE_CLASS,
  blindCoreReadCapV1,
  opaqueChainCheckpointV1,
  opaqueChainFrameV1,
  readCellCapV1,
  writeCellCapV1
} from './client-internal-schemas.js'
import { encodeCanonical } from './codec.js'
import {
  blake2b256,
  encodeVectorManifest,
  hashClientCompositionFormat,
  hashClientCompositionVectorSet
} from './hashes.js'

const bytes = (length, value) => b4a.alloc(length, value)
const hex = value => b4a.toString(value, 'hex')

function readCap (seed, sizeClass = 1, expected = true) {
  return {
    version: 1,
    relayPublicKey: bytes(32, seed),
    storageSlot: bytes(32, seed + 1),
    cellKey: bytes(32, seed + 2),
    sizeClass,
    expectedCellBlobHash: expected ? bytes(32, seed + 3) : null
  }
}

function fixtureValues () {
  const firstCap = readCap(0x21)
  const secondCap = readCap(0x31, 2, false)
  const checkpoint = {
    version: 1,
    coveredFrontier: [
      { chainId: bytes(32, 0x11), sequence: 7n, frameHash: bytes(32, 0x12) },
      { chainId: bytes(32, 0x13), sequence: 8n, frameHash: bytes(32, 0x14) }
    ],
    opaqueStateCommitment: bytes(32, 0x15),
    snapshotPayloadHash: bytes(32, 0x16),
    snapshotReadCaps: [firstCap, secondCap]
  }
  const genesisFrame = {
    version: 1,
    chainId: bytes(32, 0x41),
    sequence: 0n,
    previousFrameHash: null,
    transportVerifyKey: bytes(32, 0x42),
    opaquePayloads: [bytes(0, 0)],
    nextReadCellCaps: [firstCap, secondCap],
    checkpoint: null,
    transportSignature: bytes(64, 0x43)
  }
  const successorFrame = {
    version: 1,
    chainId: genesisFrame.chainId,
    sequence: 1n,
    previousFrameHash: bytes(32, 0x44),
    transportVerifyKey: genesisFrame.transportVerifyKey,
    opaquePayloads: [],
    nextReadCellCaps: [],
    checkpoint,
    transportSignature: bytes(64, 0x45)
  }
  const writeCap = {
    readCap: firstCap,
    allocationEpoch: 0x01020304,
    createPrivateKey: bytes(32, 0x51),
    renewPrivateKey: bytes(32, 0x52),
    dropPrivateKey: bytes(32, 0x53)
  }
  const coreCap = {
    version: 1,
    corePublicKey: bytes(32, 0x61),
    blockEncryptionKey: bytes(32, 0x62),
    witnessedFork: 3n,
    witnessedLength: 9n,
    witnessedSignedHead: b4a.from('0102030405060708', 'hex')
  }
  return { firstCap, checkpoint, genesisFrame, successorFrame, writeCap, coreCap }
}

function positiveVectors () {
  const values = fixtureValues()
  const vectors = new Map([
    ['positive/blind-core-read-cap-v1.bin', encodeCanonical(blindCoreReadCapV1, values.coreCap)],
    ['positive/opaque-chain-checkpoint-v1.bin', encodeCanonical(opaqueChainCheckpointV1, values.checkpoint)],
    ['positive/opaque-chain-frame-v1-genesis.bin', encodeCanonical(opaqueChainFrameV1, values.genesisFrame)],
    ['positive/opaque-chain-frame-v1-successor.bin', encodeCanonical(opaqueChainFrameV1, values.successorFrame)],
    ['positive/read-cell-cap-v1.bin', encodeCanonical(readCellCapV1, values.firstCap)],
    ['positive/write-cell-cap-v1.bin', encodeCanonical(writeCellCapV1, values.writeCap)]
  ])
  for (let sizeClass = 1; sizeClass <= 5; sizeClass++) {
    const length = [0, 4096, 16384, 65536, 262144, 1048576][sizeClass]
    vectors.set(`positive/cell-blob-v1-class-${sizeClass}.bin`, encodeCanonical(
      CELL_BLOB_V1_BY_SIZE_CLASS[sizeClass],
      {
        formatVersion: 1,
        nonce: bytes(12, 0x70 + sizeClass),
        sealed: bytes(length - 13, 0x80 + sizeClass)
      }
    ))
  }
  return vectors
}

export function buildClientCompositionFixtureVectorsV1 () {
  const vectors = positiveVectors()

  const zeroCoreKey = b4a.from(vectors.get('positive/blind-core-read-cap-v1.bin'))
  zeroCoreKey.fill(0, 1, 33)
  vectors.set('negative/blind-core-read-cap-zero-core-public-key.bin', zeroCoreKey)

  const classOne = vectors.get('positive/cell-blob-v1-class-1.bin')
  vectors.set('negative/cell-blob-v1-class-1-truncated.bin', b4a.from(classOne.subarray(0, classOne.byteLength - 1)))

  const unsortedCheckpoint = b4a.from(vectors.get('positive/opaque-chain-checkpoint-v1.bin'))
  if (unsortedCheckpoint[1] !== 2) throw new Error('checkpoint fixture array prefix drifted')
  unsortedCheckpoint.fill(0x20, 2, 34)
  unsortedCheckpoint.fill(0x10, 74, 106)
  vectors.set('negative/opaque-chain-checkpoint-unsorted-frontier.bin', unsortedCheckpoint)

  const sequenceWithoutPredecessor = b4a.from(vectors.get('positive/opaque-chain-frame-v1-genesis.bin'))
  sequenceWithoutPredecessor[40] = 1
  vectors.set('negative/opaque-chain-frame-sequence-one-without-predecessor.bin', sequenceWithoutPredecessor)

  const zeroRelayKey = b4a.from(vectors.get('positive/read-cell-cap-v1.bin'))
  zeroRelayKey.fill(0, 1, 33)
  vectors.set('negative/read-cell-cap-zero-relay-public-key.bin', zeroRelayKey)

  const duplicatePrivateKeys = b4a.from(vectors.get('positive/write-cell-cap-v1.bin'))
  const privateKeyOffset = duplicatePrivateKeys.byteLength - 96
  b4a.copy(duplicatePrivateKeys, duplicatePrivateKeys, privateKeyOffset + 32, privateKeyOffset, privateKeyOffset + 32)
  vectors.set('negative/write-cell-cap-duplicate-private-keys.bin', duplicatePrivateKeys)

  return new Map([...vectors].map(([path, value]) => [path, b4a.from(value)]))
}

export function computeClientCompositionRuntimeVectors () {
  const vectors = buildClientCompositionFixtureVectorsV1()
  const manifest = encodeVectorManifest([...vectors].map(([path, value]) => ({ path, bytes: value })))
  const vectorDigests = {}
  for (const [path, value] of [...vectors].sort(([left], [right]) => left.localeCompare(right))) {
    vectorDigests[path] = Object.freeze({
      byteLength: value.byteLength,
      hash: hex(blake2b256(value))
    })
  }
  return Object.freeze({
    vectorDigests: Object.freeze(vectorDigests),
    sampleFormatHash: hex(hashClientCompositionFormat(b4a.from('client-composition-runtime-format-v1', 'ascii'))),
    fixtureVectorSetHash: hex(hashClientCompositionVectorSet(manifest))
  })
}