OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / packages / blind-protocol / test / durability-client-internal-codecs.test.js

617 lines · javascript ref:
import test from 'brittle'
import b4a from 'b4a'
import {
  CELL_BLOB_V1_BY_SIZE_CLASS,
  HASH_DOMAIN,
  batchGetSignaturePayloadV1,
  blindBackupChunkManifestV1,
  blindBackupEncryptionProfileV1,
  blindBackupManifestV1,
  blindBackupRetentionTransitionV1,
  blindCellAtomicCommittedPutSpendSnapshotV1,
  blindCleanRestoreEvidenceV1,
  blindControlStateSnapshotV1,
  blindCoreReadCapV1,
  blindExternalAckFloorV1,
  blindExternalCommitWitnessV1,
  blindExternalControlCheckpointV1,
  blindLocalCheckpointV1,
  blindPutAtomicCommittedStoreV1,
  blindRestoreEvidenceBundleV1,
  blindRestoreEvidenceHeadV1,
  blindStoreManifestV1,
  blindWalHeaderV2,
  cellRecordV1,
  chargedUnaryRetryV1,
  controlSnapshotHash,
  decodeCanonical,
  domainLengthHash,
  durabilityContinuityBindingV1,
  encodeCanonical,
  inboxReadSignaturePayloadV1,
  localCheckpointHash,
  opaqueChainCheckpointV1,
  opaqueChainFrameV1,
  readCellCapV1,
  relayResultBindingV1,
  writeCellCapV1
} from '../index.js'

const bytes = (length, value) => b4a.alloc(length, value)
const ascii = value => b4a.from(value, 'ascii')

test('version-2 WAL header freezes continuity-bound 192-byte recovery authority', t => {
  const header = {
    magic: ascii('HRWL'),
    walVersion: 2,
    recordType: 9,
    totalLength: 224 + 17,
    walSequence: 1n,
    transactionId: bytes(32, 0x31),
    virtualBucket: 0x1234,
    mapGeneration: 1n,
    writerFenceTokenHash: bytes(32, 0x32),
    payloadLength: 17,
    previousWalHash: bytes(32, 0),
    durabilityContinuityHash: bytes(32, 0x33),
    payloadHash: bytes(32, 0x34)
  }
  const canonical = encodeCanonical(blindWalHeaderV2, header)
  t.is(canonical.byteLength, 192)
  const decoded = decodeCanonical(blindWalHeaderV2, canonical)
  t.is(decoded.walVersion, 2)
  t.alike(decoded.durabilityContinuityHash, header.durabilityContinuityHash)
  t.exception(() => encodeCanonical(blindWalHeaderV2, {
    ...header,
    walSequence: 2n
  }), /predecessor hash zero state/)
  t.exception(() => encodeCanonical(blindWalHeaderV2, {
    ...header,
    totalLength: header.totalLength + 1
  }), /total and payload lengths disagree/)
})

test('atomic Cell PUT store and checkpoint codecs require result authority without reservation fields', t => {
  const committed = {
    version: 1,
    spendTag: bytes(32, 0x35),
    requestCommitment: bytes(32, 0x36),
    requestFingerprint: bytes(32, 0x37),
    storageSlot: bytes(32, 0x38),
    allocationEpoch: 1,
    sizeClass: 1,
    leaseClass: 1,
    declaredBlobHash: bytes(32, 0x39),
    createPublicKey: bytes(32, 0x3a),
    renewPublicKey: bytes(32, 0x3b),
    dropPublicKey: bytes(32, 0x3c),
    allocationCommitment: bytes(32, 0x3d),
    profileId: 1,
    preparedAdmissionBytes: bytes(1, 0x3e),
    resultBindingBytes: bytes(1, 0x3f),
    declaredBytes: 4096,
    blobObjectId: bytes(32, 0x40),
    leaseEpoch: 5,
    stateRevision: 0n,
    policyRevision: 0n,
    resultIdentity: bytes(32, 0x41),
    committedEpoch: 1
  }
  const encoded = encodeCanonical(blindPutAtomicCommittedStoreV1, committed)
  t.alike(decodeCanonical(blindPutAtomicCommittedStoreV1, encoded).resultIdentity,
    committed.resultIdentity)
  t.exception(() => encodeCanonical(blindPutAtomicCommittedStoreV1, {
    ...committed,
    resultBindingBytes: null
  }), /resultBindingBytes/)

  const checkpoint = decodeCanonical(blindCellAtomicCommittedPutSpendSnapshotV1,
    encodeCanonical(blindCellAtomicCommittedPutSpendSnapshotV1, {
      ...committed,
      transactionId: bytes(32, 0x42),
      resultCell: {
        storageSlot: committed.storageSlot,
        allocationEpoch: committed.allocationEpoch,
        sizeClass: committed.sizeClass,
        leaseClass: committed.leaseClass,
        leaseEpoch: committed.leaseEpoch,
        stateRevision: committed.stateRevision,
        policyRevision: committed.policyRevision,
        cellBlobHash: committed.declaredBlobHash,
        allocationCommitment: committed.allocationCommitment,
        objectState: 1,
        policyState: 1
      }
    }))
  t.absent(checkpoint.deadlineUnixMillis)
  t.absent(checkpoint.remainingAttempts)
  t.absent(checkpoint.reservedEpoch)
})

test('local checkpoint header binds one exact snapshot and covered WAL anchor', t => {
  const checkpoint = {
    magic: ascii('HRBCKP01'),
    checkpointVersion: 1,
    relayPublicKey: bytes(32, 0x41),
    storeId: bytes(32, 0x42),
    durabilityProfileId: 1,
    durabilityContinuityHash: bytes(32, 0x43),
    durabilityProfileHash: bytes(32, 0x44),
    formatMajor: 1,
    formatMinor: 1,
    storeFormatHash: bytes(32, 0x45),
    specHash: bytes(32, 0x46),
    abiHash: bytes(32, 0x47),
    mapGeneration: 1n,
    bucketMapHash: bytes(32, 0x48),
    writerEpoch: 1n,
    writerFenceTokenHash: bytes(32, 0x49),
    checkpointRevision: 1n,
    previousCheckpointHash: null,
    coveredWalSequence: 9n,
    coveredWalHash: bytes(32, 0x4a),
    epochFloor: 10,
    descriptorSequenceFloor: 2n,
    descriptorHashFloor: bytes(32, 0x4b),
    snapshotByteLength: 512n,
    snapshotHash: bytes(32, 0x4c)
  }
  const canonical = encodeCanonical(blindLocalCheckpointV1, checkpoint)
  t.alike(encodeCanonical(blindLocalCheckpointV1,
    decodeCanonical(blindLocalCheckpointV1, canonical)), canonical)
  t.is(localCheckpointHash(canonical).byteLength, 32)
  t.exception(() => encodeCanonical(blindLocalCheckpointV1, {
    ...checkpoint,
    checkpointRevision: 2n
  }), /predecessor presence/)
  t.exception(() => encodeCanonical(blindLocalCheckpointV1, {
    ...checkpoint,
    snapshotByteLength: 0n
  }), /must be nonzero/)
})

function externalWitness () {
  return {
    version: 1,
    relayPublicKey: bytes(32, 1),
    storeId: bytes(32, 2),
    externalJournalId: bytes(32, 3),
    durabilityContinuityHash: bytes(32, 4),
    durabilityProfileHash: bytes(32, 5),
    restoreEvidenceHeadSequence: 1n,
    restoreEvidenceHeadHash: bytes(32, 6),
    familyId: 2,
    operationId: 1,
    requestCommitment: bytes(32, 7),
    resultCommitment: bytes(32, 8),
    commitWalSequence: 9n,
    commitWalHash: bytes(32, 9),
    coveringFloorRevision: 10n,
    coveringFloorHash: bytes(32, 10),
    coveringFloorWalSequence: 9n,
    coveringFloorWalHash: bytes(32, 11),
    writerEpoch: 12n,
    writerFenceTokenHash: bytes(32, 12),
    externalLeaseRevision: 13n,
    witnessedUnixMillis: 14n,
    witnessPublicKey: bytes(32, 13),
    signature: bytes(64, 14)
  }
}

function resultBinding () {
  const witness = externalWitness()
  return {
    version: 1,
    relayPublicKey: witness.relayPublicKey,
    storeId: witness.storeId,
    descriptorSequence: 15n,
    descriptorHash: bytes(32, 15),
    durabilityProfileId: 2,
    durabilityContinuityHash: witness.durabilityContinuityHash,
    durabilityProfileHash: witness.durabilityProfileHash,
    restoreEvidenceHeadSequence: witness.restoreEvidenceHeadSequence,
    restoreEvidenceHeadHash: witness.restoreEvidenceHeadHash,
    externalCommitWitness: witness
  }
}

function restoreHead () {
  const issued = 21600000n * 10n
  return {
    version: 1,
    relayPublicKey: bytes(32, 21),
    storeId: bytes(32, 22),
    externalJournalId: bytes(32, 23),
    durabilityContinuityHash: bytes(32, 24),
    restoreEvidenceFeedId: bytes(32, 25),
    evidenceSequence: 1n,
    previousEvidenceHeadHash: null,
    currentBackupManifestHash: bytes(32, 26),
    currentRetentionTransitionHash: bytes(32, 27),
    currentCoveredWalSequence: 28n,
    currentExternalFloorRevision: 29n,
    currentChunkObjectCount: 30n,
    currentAvailabilityAuditHash: bytes(32, 31),
    currentSupportExpiresUnixMillis: issued + 100000n,
    drillBackupManifestHash: bytes(32, 32),
    drillCleanRestoreEvidenceHash: bytes(32, 33),
    drillRetentionTransitionHash: bytes(32, 34),
    restoreDrillCompletedUnixMillis: issued - 1000n,
    drillSupportExpiresUnixMillis: issued + 100000n,
    issuedExternalUnixMillis: issued,
    expiresExternalUnixMillis: issued + 200000n,
    issuedEpoch: 10,
    witnessPublicKey: bytes(32, 35),
    signature: bytes(64, 36)
  }
}

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

test('result bindings carry external commit continuity and signature payloads', t => {
  const witness = externalWitness()
  t.is(decodeCanonical(blindExternalCommitWitnessV1,
    encodeCanonical(blindExternalCommitWitnessV1, witness)).commitWalSequence, 9n)
  const binding = resultBinding()
  t.is(decodeCanonical(relayResultBindingV1,
    encodeCanonical(relayResultBindingV1, binding)).durabilityProfileId, 2)
  t.exception(() => encodeCanonical(relayResultBindingV1, {
    ...binding,
    restoreEvidenceHeadHash: bytes(32, 99)
  }), /does not match relay binding/)
  const batch = {
    version: 1,
    relayBinding: binding,
    requestNonce: bytes(32, 51),
    requestCommitment: bytes(32, 52),
    entriesCommitment: bytes(32, 53)
  }
  t.alike(decodeCanonical(batchGetSignaturePayloadV1,
    encodeCanonical(batchGetSignaturePayloadV1, batch)).entriesCommitment, batch.entriesCommitment)
  const inbox = {
    version: 1,
    relayBinding: binding,
    requestNonce: bytes(32, 54),
    requestCommitment: bytes(32, 55),
    snapshotRevision: 56n,
    entriesCommitment: bytes(32, 57),
    nextCursor: bytes(8, 58)
  }
  t.is(decodeCanonical(inboxReadSignaturePayloadV1,
    encodeCanonical(inboxReadSignaturePayloadV1, inbox)).snapshotRevision, 56n)
})

test('public backup and restore codecs round trip their frozen continuity chain', t => {
  const continuity = {
    version: 1,
    profileId: 2,
    externalJournalId: bytes(32, 61),
    externalWitnessPublicKey: bytes(32, 62),
    externalJournalReplicationClass: 1,
    externalJournalFailureGroupId: bytes(32, 63),
    restoreEvidenceFeedId: bytes(32, 64)
  }
  t.is(decodeCanonical(durabilityContinuityBindingV1,
    encodeCanonical(durabilityContinuityBindingV1, continuity)).profileId, 2)
  const encryptionProfile = {
    version: 1,
    algorithmId: 1,
    keyDerivationId: 1,
    recoveryKeyId: bytes(32, 65),
    keyEpoch: 1
  }
  t.is(decodeCanonical(blindBackupEncryptionProfileV1,
    encodeCanonical(blindBackupEncryptionProfileV1, encryptionProfile)).keyEpoch, 1)
  const chunkManifest = {
    version: 1,
    backupId: bytes(32, 66),
    encryptionProfile,
    encryptionManifestHash: bytes(32, 67),
    entries: [{
      path: ascii('store/manifest'),
      fileOffset: 0n,
      plaintextByteLength: 1024,
      ciphertextByteLength: 1040,
      chunkObjectId: bytes(32, 68),
      chunkSalt: bytes(32, 69),
      nonce: bytes(24, 70),
      ciphertextHash: bytes(32, 71)
    }],
    totalPlaintextByteLength: 1024n,
    totalCiphertextByteLength: 1040n
  }
  t.is(decodeCanonical(blindBackupChunkManifestV1,
    encodeCanonical(blindBackupChunkManifestV1, chunkManifest)).entries.length, 1)
  t.exception(() => encodeCanonical(blindBackupChunkManifestV1, {
    ...chunkManifest,
    totalCiphertextByteLength: 1041n
  }), /do not equal/)
  const manifest = {
    version: 1,
    relayPublicKey: bytes(32, 72),
    storeId: bytes(32, 73),
    externalJournalId: bytes(32, 74),
    durabilityContinuityHash: bytes(32, 75),
    backupId: chunkManifest.backupId,
    backupFailureGroupId: bytes(32, 76),
    storeManifestRevision: 1n,
    storeManifestHash: bytes(32, 77),
    storeFormatHash: bytes(32, 78),
    coverageCutoffExternalUnixMillis: 100n,
    coveredWalSequence: 2n,
    coveredWalHash: bytes(32, 79),
    externalFloorRevision: 3n,
    externalFloorHash: bytes(32, 80),
    externalCheckpointRevision: 4n,
    externalCheckpointHash: bytes(32, 81),
    baseFloorRevision: 3n,
    baseFloorHash: bytes(32, 82),
    controlSnapshotHash: bytes(32, 83),
    backupEncryptionProfileHash: bytes(32, 84),
    encryptionManifestHash: chunkManifest.encryptionManifestHash,
    chunkManifestByteLength: 1000n,
    chunkManifestHash: bytes(32, 85),
    totalPlaintextByteLength: chunkManifest.totalPlaintextByteLength,
    totalCiphertextByteLength: chunkManifest.totalCiphertextByteLength,
    restoreVerifierPublicKey: bytes(32, 86),
    cleanRestoreEvidenceHash: bytes(32, 87),
    createdExternalUnixMillis: 200n,
    restoreSupportExpiresUnixMillis: 300n,
    witnessPublicKey: bytes(32, 88),
    signature: bytes(64, 89)
  }
  t.is(decodeCanonical(blindBackupManifestV1,
    encodeCanonical(blindBackupManifestV1, manifest)).backupFailureGroupId.byteLength, 32)
  const clean = {
    version: 1,
    backupId: manifest.backupId,
    backupCandidateCommitment: bytes(32, 90),
    restoredStoreManifestHash: bytes(32, 91),
    verifiedWalSequence: 2n,
    verifiedWalHash: manifest.coveredWalHash,
    verifiedExternalFloorRevision: 3n,
    verifiedExternalFloorHash: manifest.externalFloorHash,
    verifiedCheckpointRevision: 4n,
    verifiedCheckpointHash: manifest.externalCheckpointHash,
    scrubbedObjectCount: 5n,
    scrubFailureCount: 0,
    startedExternalUnixMillis: 201n,
    completedExternalUnixMillis: 202n,
    verifierPublicKey: manifest.restoreVerifierPublicKey,
    signature: bytes(64, 92)
  }
  t.is(decodeCanonical(blindCleanRestoreEvidenceV1,
    encodeCanonical(blindCleanRestoreEvidenceV1, clean)).scrubFailureCount, 0)
  const retention = {
    version: 1,
    relayPublicKey: manifest.relayPublicKey,
    storeId: manifest.storeId,
    externalJournalId: manifest.externalJournalId,
    durabilityContinuityHash: manifest.durabilityContinuityHash,
    backupId: manifest.backupId,
    backupManifestHash: bytes(32, 93),
    transitionRevision: 1n,
    previousTransitionHash: null,
    operation: 1,
    supportExpiresUnixMillis: 300n,
    replacementBackupId: null,
    effectiveExternalTimeFloorMillis: 203n,
    witnessPublicKey: manifest.witnessPublicKey,
    signature: bytes(64, 94)
  }
  t.is(decodeCanonical(blindBackupRetentionTransitionV1,
    encodeCanonical(blindBackupRetentionTransitionV1, retention)).operation, 1)
  const head = restoreHead()
  t.is(decodeCanonical(blindRestoreEvidenceHeadV1,
    encodeCanonical(blindRestoreEvidenceHeadV1, head)).evidenceSequence, 1n)
  const bundle = {
    version: 1,
    heads: [head],
    currentBackupManifestBytes: bytes(1, 1),
    currentRetentionTransitionBytes: bytes(1, 2),
    drillBackupManifestBytes: bytes(1, 3),
    drillCleanRestoreEvidenceBytes: bytes(1, 4),
    drillRetentionTransitionBytes: bytes(1, 5)
  }
  t.is(decodeCanonical(blindRestoreEvidenceBundleV1,
    encodeCanonical(blindRestoreEvidenceBundleV1, bundle)).heads.length, 1)
})

test('client example codecs remain contextual and never become relay wire imports', t => {
  const cap = readCap()
  t.is(decodeCanonical(readCellCapV1, encodeCanonical(readCellCapV1, cap)).sizeClass, 1)
  const write = {
    readCap: cap,
    allocationEpoch: 10,
    createPrivateKey: bytes(32, 45),
    renewPrivateKey: bytes(32, 46),
    dropPrivateKey: bytes(32, 47)
  }
  t.alike(decodeCanonical(writeCellCapV1,
    encodeCanonical(writeCellCapV1, write)).readCap.storageSlot, cap.storageSlot)
  const cellBlob = { formatVersion: 1, nonce: bytes(12, 48), sealed: bytes(4096 - 13, 49) }
  t.is(encodeCanonical(CELL_BLOB_V1_BY_SIZE_CLASS[1], cellBlob).byteLength, 4096)
  const checkpoint = {
    version: 1,
    coveredFrontier: [{ chainId: bytes(32, 50), sequence: 1n, frameHash: bytes(32, 51) }],
    opaqueStateCommitment: bytes(32, 52),
    snapshotPayloadHash: bytes(32, 53),
    snapshotReadCaps: [cap]
  }
  t.is(decodeCanonical(opaqueChainCheckpointV1,
    encodeCanonical(opaqueChainCheckpointV1, checkpoint)).coveredFrontier.length, 1)
  const frame = {
    version: 1,
    chainId: bytes(32, 54),
    sequence: 0n,
    previousFrameHash: null,
    transportVerifyKey: bytes(32, 55),
    opaquePayloads: [bytes(1, 56)],
    nextReadCellCaps: [cap],
    checkpoint,
    transportSignature: bytes(64, 57)
  }
  t.is(decodeCanonical(opaqueChainFrameV1,
    encodeCanonical(opaqueChainFrameV1, frame)).sequence, 0n)
  const coreCap = {
    version: 1,
    corePublicKey: bytes(32, 58),
    blockEncryptionKey: bytes(32, 59),
    witnessedFork: 0n,
    witnessedLength: 1n,
    witnessedSignedHead: bytes(1, 60)
  }
  t.is(decodeCanonical(blindCoreReadCapV1,
    encodeCanonical(blindCoreReadCapV1, coreCap)).witnessedLength, 1n)
})

test('internal store codecs bind migration, floors, checkpoints and retry caps', t => {
  const record = {
    version: 1,
    slot: bytes(32, 101),
    allocationEpoch: 1,
    sizeClass: 1,
    leaseClass: 1,
    leaseEpoch: 2,
    stateRevision: 3n,
    policyRevision: 4n,
    cellBlobHash: bytes(32, 102),
    cellBlob: bytes(4096, 103),
    createPublicKey: bytes(32, 104),
    renewPublicKey: bytes(32, 105),
    dropPublicKey: bytes(32, 106),
    allocationCommitment: bytes(32, 107)
  }
  t.is(decodeCanonical(cellRecordV1, encodeCanonical(cellRecordV1, record)).stateRevision, 3n)
  const retry = {
    version: 1,
    spendTag: bytes(32, 108),
    requestCommitment: bytes(32, 109),
    familyId: 2,
    operationId: 2,
    locatorCommitment: bytes(32, 110),
    sourceRevision: 3n,
    sourceCommitment: bytes(32, 111),
    resultCommitment: bytes(32, 112),
    reconstruction: bytes(4, 113),
    retryExpiresMinute: 10n,
    retryState: 1
  }
  t.ok(encodeCanonical(chargedUnaryRetryV1, retry).byteLength <= 256)
  const snapshot = {
    version: 1,
    relayPublicKey: bytes(32, 114),
    storeId: bytes(32, 115),
    durabilityContinuityHash: bytes(32, 116),
    walSequence: 1n,
    walHash: bytes(32, 117),
    entries: []
  }
  const snapshotBytes = encodeCanonical(blindControlStateSnapshotV1, snapshot)
  t.is(decodeCanonical(blindControlStateSnapshotV1, snapshotBytes).entries.length, 0)
  t.is(HASH_DOMAIN.CONTROL_SNAPSHOT, 'hiverelay.blind.control-snapshot.v1')
  t.alike(controlSnapshotHash(snapshotBytes),
    domainLengthHash('hiverelay.blind.control-snapshot.v1', snapshotBytes))
  const floor = {
    version: 1,
    relayPublicKey: snapshot.relayPublicKey,
    storeId: snapshot.storeId,
    externalJournalId: bytes(32, 118),
    durabilityContinuityHash: snapshot.durabilityContinuityHash,
    floorRevision: 1n,
    previousFloorHash: null,
    writerEpoch: 2n,
    writerFenceTokenHash: bytes(32, 119),
    externalLeaseRevision: 3n,
    walSequence: snapshot.walSequence,
    walHash: snapshot.walHash,
    descriptorSequence: 4n,
    descriptorHash: bytes(32, 120),
    witnessedUnixMillis: 5n,
    witnessPublicKey: bytes(32, 121),
    signature: bytes(64, 122)
  }
  t.is(decodeCanonical(blindExternalAckFloorV1,
    encodeCanonical(blindExternalAckFloorV1, floor)).floorRevision, 1n)
  const checkpoint = {
    version: 1,
    relayPublicKey: floor.relayPublicKey,
    storeId: floor.storeId,
    externalJournalId: floor.externalJournalId,
    durabilityContinuityHash: floor.durabilityContinuityHash,
    checkpointRevision: 1n,
    previousCheckpointHash: null,
    baseFloorRevision: floor.floorRevision,
    baseFloorHash: bytes(32, 123),
    writerEpoch: floor.writerEpoch,
    writerFenceTokenHash: floor.writerFenceTokenHash,
    externalLeaseRevision: floor.externalLeaseRevision,
    walSequence: floor.walSequence,
    walHash: floor.walHash,
    descriptorSequence: floor.descriptorSequence,
    descriptorHash: floor.descriptorHash,
    snapshotByteLength: 100n,
    snapshotHash: bytes(32, 124),
    oldestRetainedFloorRevision: 1n,
    createdUnixMillis: 6n,
    witnessPublicKey: floor.witnessPublicKey,
    signature: bytes(64, 125)
  }
  t.is(decodeCanonical(blindExternalControlCheckpointV1,
    encodeCanonical(blindExternalControlCheckpointV1, checkpoint)).checkpointRevision, 1n)
  const manifest = {
    magic: ascii('HRBLIND1'),
    manifestVersion: 1,
    storeId: snapshot.storeId,
    relayPublicKey: snapshot.relayPublicKey,
    durabilityProfileId: 1,
    durabilityContinuityHash: snapshot.durabilityContinuityHash,
    durabilityProfileHash: bytes(32, 126),
    formatMajor: 1,
    formatMinor: 1,
    storeFormatHash: bytes(32, 127),
    specHash: bytes(32, 128),
    abiHash: bytes(32, 129),
    mapGeneration: 1n,
    bucketMapHash: bytes(32, 130),
    checkpointWalSequence: 1n,
    checkpointHash: bytes(32, 131),
    epochFloor: 1,
    writerEpoch: 1n,
    writerFenceTokenHash: bytes(32, 132),
    externalLeaseRevision: 0n,
    externalJournalId: bytes(32, 0),
    externalWitnessPublicKey: bytes(32, 0),
    restoreEvidenceFeedId: bytes(32, 0),
    lastAckWalSequence: 0n,
    lastAckWalHash: bytes(32, 0),
    externalCheckpointRevision: 0n,
    externalCheckpointHash: bytes(32, 0),
    descriptorSequenceFloor: 1n,
    descriptorHashFloor: bytes(32, 133),
    migrationState: 0,
    sourceFormatMajor: 0,
    targetFormatMajor: 0,
    migrationCursorHash: bytes(32, 0),
    previousManifestHash: null,
    manifestRevision: 0n,
    mac: bytes(32, 134)
  }
  t.is(decodeCanonical(blindStoreManifestV1,
    encodeCanonical(blindStoreManifestV1, manifest)).migrationState, 0)
  t.exception(() => encodeCanonical(blindStoreManifestV1, {
    ...manifest,
    externalJournalId: bytes(32, 1)
  }), /zero external continuity state/)
})