import b4a from 'b4a'
import test from 'brittle'
import sodium from 'sodium-universal'
import {
FAMILY,
INBOX_APPEND_RESULT,
INBOX_FRAME_CLASS,
OPERATION,
RESULT_SIGNATURE_DOMAIN_ID,
blindInboxControlGlobalSnapshotV1,
blindInboxRetryReconstructionV1,
blake2b256,
chargedUnaryRetryV1,
decodeCanonical,
encodeCanonical,
inboxAppendAckV1,
inboxCreateCommitment,
inboxPhysicalTopic,
relayResultBindingV1,
resultSignaturePayload
} from '@hiverelay/blind-protocol'
import {
BLIND_INBOX_CONTROL_SNAPSHOT_STATUS,
createBlindInboxControlSnapshotSemanticAuthority,
createBlindInboxControlSnapshotSemanticVerifier,
reconstructBlindInboxControlSnapshot,
streamBlindInboxControlSnapshotEntries,
verifyBlindInboxControlSnapshotSemanticResult,
verifyBlindInboxControlSnapshotSemanticVerifier
} from '../inbox-control-snapshot.js'
import { verifyBlindLocalCheckpointSnapshotSemanticAuthority } from '../local-checkpoint-store.js'
import { deriveBlindVirtualBucket } from '../virtual-bucket.js'
const FINGERPRINT_DOMAIN = b4a.from('hiverelay.blind.inbox-store-request-fingerprint.v1', 'ascii')
const RESULT_IDENTITY_DOMAIN = b4a.from('hiverelay.blind.inbox-store-result-identity.v1', 'ascii')
const RETRY_SOURCE_DOMAIN = b4a.from('hiverelay.blind.inbox-retry-source.v1', 'ascii')
const ZERO32 = b4a.alloc(32)
const RELAY_SEED = b4a.alloc(sodium.crypto_sign_SEEDBYTES, 0x01)
const RELAY_PUBLIC_KEY = b4a.alloc(sodium.crypto_sign_PUBLICKEYBYTES)
const RELAY_SECRET_KEY = b4a.alloc(sodium.crypto_sign_SECRETKEYBYTES)
sodium.crypto_sign_seed_keypair(RELAY_PUBLIC_KEY, RELAY_SECRET_KEY, RELAY_SEED)
const STORE_ID = b4a.alloc(32, 0x02)
const DURABILITY_CONTINUITY = b4a.alloc(32, 0x03)
function bytes (fill) {
return b4a.alloc(32, fill)
}
function u16bytes (value) {
return b4a.from([value >>> 8, value])
}
function u32bytes (value) {
return b4a.from([value >>> 24, value >>> 16, value >>> 8, value])
}
function u64bytes (value) {
value = BigInt(value)
const output = b4a.alloc(8)
for (let index = 7; index >= 0; index--) {
output[index] = Number(value & 0xffn)
value >>= 8n
}
return output
}
function hashParts (domain, ...parts) {
return blake2b256(b4a.concat([domain, ...parts]))
}
function fingerprint (value) {
return hashParts(
FINGERPRINT_DOMAIN,
b4a.from([value.operation]),
value.spendTag,
value.requestCommitment,
value.physicalTopic,
b4a.from([value.frameClass]),
value.frameHash == null ? ZERO32 : value.frameHash,
b4a.from([value.requestedLeaseClass]),
u16bytes(value.profileId),
u32bytes(value.declaredBytes)
)
}
function resultIdentity (operation, physicalTopic, requestCommitment, resultRevision, resultHash, committedEpoch) {
return hashParts(
RESULT_IDENTITY_DOMAIN,
b4a.from([operation]),
physicalTopic,
requestCommitment,
u64bytes(resultRevision),
resultHash,
u32bytes(committedEpoch)
)
}
function retrySourceCommitment (physicalTopic, sourceRevision, reconstruction, pins) {
const pinBytes = pins.flatMap(pin => [u64bytes(pin.appendRevision), pin.frameHash])
return hashParts(
RETRY_SOURCE_DOMAIN,
physicalTopic,
u64bytes(sourceRevision),
u64bytes(reconstruction.firstAppendRevision),
u64bytes(reconstruction.lastAppendRevision),
b4a.from([reconstruction.entryCount]),
reconstruction.nextCursorHash,
...pinBytes
)
}
function virtualBucket (physicalTopic) {
return deriveBlindVirtualBucket(FAMILY.INBOX, physicalTopic)
}
function authority (overrides = {}) {
return createBlindInboxControlSnapshotSemanticAuthority(overrides)
}
function spend (seed, physicalTopic, operation, overrides = {}) {
const value = {
status: overrides.status || 'committed',
transactionId: bytes(0x10 + seed),
spendTag: bytes(0x20 + seed),
requestCommitment: bytes(0x30 + seed),
physicalTopic,
operation,
profileId: seed,
frameClass: overrides.frameClass || 0,
frameHash: overrides.frameHash || null,
requestedLeaseClass: overrides.requestedLeaseClass || 0,
declaredBytes: overrides.declaredBytes || 0,
deadlineUnixMillis: 100000n + BigInt(seed),
remainingAttempts: overrides.remainingAttempts == null ? 1 : overrides.remainingAttempts,
reservedEpoch: 100,
resultIdentity: overrides.resultIdentity || null,
resultRevision: overrides.resultRevision == null ? null : BigInt(overrides.resultRevision),
committedEpoch: overrides.committedEpoch == null ? null : overrides.committedEpoch,
resultLeaseClass: overrides.resultLeaseClass == null ? null : overrides.resultLeaseClass,
resultLeaseEpoch: overrides.resultLeaseEpoch == null ? null : overrides.resultLeaseEpoch,
resultCommitment: overrides.resultCommitment || null,
retryState: overrides.retryState || 0,
terminalReason: overrides.terminalReason || null,
terminalEpoch: overrides.terminalEpoch || null,
inFlight: false
}
value.requestFingerprint = fingerprint(value)
return value
}
function fixture (reverse = false) {
const relayPublicKey = b4a.from(RELAY_PUBLIC_KEY)
const allocationEpoch = 90
const createPublicKey = bytes(0x41)
const appendPublicKey = bytes(0x42)
const renewPublicKey = bytes(0x43)
const closePublicKey = bytes(0x44)
const physicalTopic = inboxPhysicalTopic({ allocationEpoch, createPublicKey })
const createCommitment = inboxCreateCommitment({
relayPublicKey,
physicalTopic,
allocationEpoch,
frameClassBits: 0x07,
appendAuthMode: 1,
appendPublicKey,
createPublicKey,
renewPublicKey,
closePublicKey,
retentionClass: 1,
leaseClass: 1
})
const createSpend = spend(1, physicalTopic, OPERATION.INBOX.CREATE, {
requestedLeaseClass: 1,
resultRevision: 0,
committedEpoch: 100,
resultLeaseClass: 1,
resultLeaseEpoch: 104
})
createSpend.resultIdentity = resultIdentity(OPERATION.INBOX.CREATE, physicalTopic,
createSpend.requestCommitment, 0n, createCommitment, 100)
const frameHash = bytes(0x51)
const appendSpend = spend(2, physicalTopic, OPERATION.INBOX.APPEND, {
frameClass: 1,
frameHash,
declaredBytes: INBOX_FRAME_CLASS[1],
resultRevision: 1,
committedEpoch: 100
})
appendSpend.resultIdentity = resultIdentity(OPERATION.INBOX.APPEND, physicalTopic,
appendSpend.requestCommitment, 1n, frameHash, 100)
const relayBinding = {
version: 1,
relayPublicKey,
storeId: STORE_ID,
descriptorSequence: 1n,
descriptorHash: bytes(0x05),
durabilityProfileId: 1,
durabilityContinuityHash: DURABILITY_CONTINUITY,
durabilityProfileHash: bytes(0x06),
restoreEvidenceHeadSequence: 0n,
restoreEvidenceHeadHash: b4a.alloc(32),
externalCommitWitness: null
}
appendSpend.resultBindingBytes = encodeCanonical(relayResultBindingV1, relayBinding)
appendSpend.clientNonce = bytes(0x53)
appendSpend.retentionClassAtAppend = 1
appendSpend.appendLeaseEpoch = 104
appendSpend.expiresAtEpoch = 104
const unsignedAck = encodeCanonical(inboxAppendAckV1, {
version: 1,
relayBinding,
topicCommitment: blake2b256(physicalTopic),
frameHash,
appendRevision: 1n,
storedAtEpoch: 100,
expiresAtEpoch: 104,
requestNonce: appendSpend.clientNonce,
requestCommitment: appendSpend.requestCommitment,
result: INBOX_APPEND_RESULT.STORED,
signature: b4a.alloc(64)
}).subarray(0, -64)
appendSpend.ackSignature = b4a.alloc(64)
sodium.crypto_sign_detached(appendSpend.ackSignature,
resultSignaturePayload(RESULT_SIGNATURE_DOMAIN_ID.INBOX_APPEND_ACK, unsignedAck), RELAY_SECRET_KEY)
appendSpend.resultCommitment = blake2b256(encodeCanonical(inboxAppendAckV1, {
version: 1,
relayBinding,
topicCommitment: blake2b256(physicalTopic),
frameHash,
appendRevision: 1n,
storedAtEpoch: 100,
expiresAtEpoch: 104,
requestNonce: appendSpend.clientNonce,
requestCommitment: appendSpend.requestCommitment,
result: INBOX_APPEND_RESULT.STORED,
signature: appendSpend.ackSignature
}))
createSpend.resultBindingBytes = b4a.from(appendSpend.resultBindingBytes)
createSpend.clientNonce = bytes(0x54)
const reservedAppend = spend(3, physicalTopic, OPERATION.INBOX.APPEND, {
status: 'reserved',
frameClass: 2,
frameHash: bytes(0x52),
declaredBytes: INBOX_FRAME_CLASS[2],
remainingAttempts: 2
})
const resultCommitment = bytes(0x61)
const readSpend = spend(4, physicalTopic, OPERATION.INBOX.READ, {
resultRevision: 1,
committedEpoch: 100,
resultCommitment,
retryState: 1
})
readSpend.resultIdentity = resultIdentity(OPERATION.INBOX.READ, physicalTopic,
readSpend.requestCommitment, 1n, resultCommitment, 100)
readSpend.resultBindingBytes = b4a.from(appendSpend.resultBindingBytes)
readSpend.clientNonce = bytes(0x55)
const spendRows = [createSpend, appendSpend, reservedAppend, readSpend]
if (reverse) spendRows.reverse()
const spends = new Map(spendRows.map(value => [b4a.toString(value.spendTag, 'hex'), value]))
const commitments = new Map(spendRows.map(value => [
b4a.toString(value.requestCommitment, 'hex'),
{
spendKey: b4a.toString(value.spendTag, 'hex'),
fingerprint: b4a.toString(value.requestFingerprint, 'hex')
}
]))
const inbox = {
physicalTopic,
metadataVirtualBucket: virtualBucket(physicalTopic),
allocationEpoch,
frameClassBits: 0x07,
appendAuthMode: 1,
appendPublicKey,
createPublicKey,
renewPublicKey,
closePublicKey,
retentionClass: 1,
// Renewed state; the immutable create commitment remains lease class 1.
leaseClass: 4,
leaseEpoch: 460,
stateRevision: 1n,
policyRevision: 0n,
appendRevision: 1n,
createCommitment,
objectState: 1,
policyState: 1,
tombstoneReason: null,
terminalEpoch: null,
createSpendTag: createSpend.spendTag,
createRequestCommitment: createSpend.requestCommitment,
resultIdentity: createSpend.resultIdentity,
createdEpoch: 100
}
const frame = {
physicalTopic,
appendRevision: 1n,
frameHash,
frameClass: 1,
frameVirtualBucket: virtualBucket(physicalTopic),
frameObjectId: bytes(0x71),
appendLeaseEpoch: 104,
storedAtEpoch: 100,
expiresAtEpoch: 104,
spendTag: appendSpend.spendTag,
requestCommitment: appendSpend.requestCommitment,
resultIdentity: appendSpend.resultIdentity
}
const nextCursor = b4a.from('cursor-v1', 'ascii')
const reconstruction = {
version: 1,
firstAppendRevision: 1n,
lastAppendRevision: 1n,
entryCount: 1,
nextCursorHash: blake2b256(nextCursor)
}
const pinEntries = [{ physicalTopic, appendRevision: 1n, frameHash }]
const pin = {
spendTag: readSpend.spendTag,
requestCommitment: readSpend.requestCommitment,
physicalTopic,
operation: OPERATION.INBOX.READ,
locatorCommitment: blake2b256(physicalTopic),
sourceRevision: 1n,
sourceCommitment: retrySourceCommitment(physicalTopic, 1n, reconstruction, pinEntries),
resultCommitment,
reconstruction,
retryExpiresMinute: 200000n,
retryState: 1,
entries: pinEntries,
pinnedEntries: [{
appendRevision: frame.appendRevision,
frameHash: frame.frameHash,
frameClass: frame.frameClass,
frameObjectId: frame.frameObjectId
}],
entriesCommitment: bytes(0x64),
nextCursor,
resultBindingBytes: readSpend.resultBindingBytes,
clientNonce: readSpend.clientNonce,
committedEpoch: readSpend.committedEpoch
}
return {
relayPublicKey,
storeId: STORE_ID,
durabilityContinuityHash: DURABILITY_CONTINUITY,
spends,
commitments,
requestResults: new Map(),
inboxes: new Map([[b4a.toString(physicalTopic, 'hex'), inbox]]),
frames: new Map([[`${b4a.toString(physicalTopic, 'hex')}:1`, frame]]),
retryPins: new Map([[b4a.toString(readSpend.spendTag, 'hex'), pin]]),
accounting: {
storedFrameBytes: INBOX_FRAME_CLASS[1],
stagingFrameBytes: INBOX_FRAME_CLASS[2],
controlBytes: 4 * 512 + 2 * 256,
tombstoneBytes: 512,
frameIndexBytes: 256,
reservedFrames: 1,
stagingByProfile: new Map([[reservedAppend.profileId, INBOX_FRAME_CLASS[2]]])
},
epochFloor: 100,
clockUnsafe: true,
readOnlyReason: null,
integrityEvidence: []
}
}
function terminalFixture () {
const state = fixture()
const reserved = [...state.spends.values()].find(value => value.status === 'reserved')
reserved.status = 'terminal'
reserved.remainingAttempts = 0
reserved.terminalReason = 2
reserved.terminalEpoch = 100
state.accounting.stagingFrameBytes = 0
state.accounting.reservedFrames = 0
state.accounting.stagingByProfile.clear()
return state
}
function expiredAppendFixture () {
const state = fixture()
const append = [...state.spends.values()].find(value => value.operation === OPERATION.INBOX.APPEND &&
value.status === 'committed')
const read = [...state.spends.values()].find(value => value.operation === OPERATION.INBOX.READ)
append.status = 'expired-append'
append.expiredEpoch = 104
state.frames.clear()
state.spends.delete(b4a.toString(read.spendTag, 'hex'))
state.commitments.delete(b4a.toString(read.requestCommitment, 'hex'))
state.retryPins.clear()
state.accounting.storedFrameBytes = 0
state.accounting.frameIndexBytes = 0
state.accounting.controlBytes = 3 * 512
state.epochFloor = 104
return state
}
function closedFixture () {
const state = terminalFixture()
const terminal = [...state.spends.values()].find(value => value.status === 'terminal')
state.spends.delete(b4a.toString(terminal.spendTag, 'hex'))
state.commitments.delete(b4a.toString(terminal.requestCommitment, 'hex'))
const inbox = state.inboxes.values().next().value
inbox.objectState = 2
inbox.tombstoneReason = 1
inbox.terminalEpoch = 100
inbox.stateRevision = 2n
const readSpend = [...state.spends.values()].find(value => value.operation === OPERATION.INBOX.READ)
readSpend.retryState = 2
state.retryPins.get(b4a.toString(readSpend.spendTag, 'hex')).retryState = 2
const closeRequest = bytes(0x7a)
const closeResult = {
operation: 'close',
transactionId: bytes(0x79),
requestCommitment: closeRequest,
physicalTopic: inbox.physicalTopic,
resultRevision: 2n,
committedEpoch: 100,
resultBindingBytes: b4a.from([...state.spends.values()]
.find(value => value.status === 'committed').resultBindingBytes),
clientNonce: bytes(0x7b),
resultLeaseClass: 0,
resultLeaseEpoch: inbox.leaseEpoch,
resultIdentity: resultIdentity(OPERATION.INBOX.CLOSE, inbox.physicalTopic,
closeRequest, 2n, inbox.createCommitment, 100)
}
state.requestResults.set(b4a.toString(closeRequest, 'hex'), closeResult)
state.accounting.controlBytes = 4 * 512 + 2 * 256
return state
}
function headers (state) {
const header = {
relayPublicKey: state.relayPublicKey,
storeId: STORE_ID,
durabilityContinuityHash: DURABILITY_CONTINUITY,
walSequence: 7n,
walHash: bytes(0x04)
}
return {
header,
checkpointHeader: {
relayPublicKey: header.relayPublicKey,
storeId: header.storeId,
durabilityContinuityHash: header.durabilityContinuityHash,
coveredWalSequence: header.walSequence,
coveredWalHash: header.walHash,
epochFloor: state.epochFloor
}
}
}
async function entriesFor (semanticAuthority, state) {
const output = []
for await (const entry of streamBlindInboxControlSnapshotEntries(semanticAuthority, state)) {
output.push({ entryKind: entry.entryKind, key: b4a.from(entry.key), value: b4a.from(entry.value) })
}
return output
}
async function reconstruct (semanticAuthority, state, entries) {
return reconstructBlindInboxControlSnapshot(semanticAuthority, {
...headers(state),
declaredEntryCount: entries.length,
entries
})
}
test('Inbox recovery snapshots are deterministic, bounded, exact, and preserve immutable allocation history', async t => {
const semanticAuthority = authority()
const state = fixture()
const entries = await entriesFor(semanticAuthority, state)
const reversedEntries = await entriesFor(semanticAuthority, fixture(true))
t.alike(reversedEntries, entries)
const retry = state.retryPins.values().next().value
const reconstructionBytes = encodeCanonical(blindInboxRetryReconstructionV1, retry.reconstruction)
t.is(reconstructionBytes.byteLength, 50)
t.is(encodeCanonical(chargedUnaryRetryV1, {
version: 1,
spendTag: retry.spendTag,
requestCommitment: retry.requestCommitment,
familyId: FAMILY.INBOX,
operationId: retry.operation,
locatorCommitment: retry.locatorCommitment,
sourceRevision: retry.sourceRevision,
sourceCommitment: retry.sourceCommitment,
resultCommitment: retry.resultCommitment,
reconstruction: reconstructionBytes,
retryExpiresMinute: retry.retryExpiresMinute,
retryState: retry.retryState
}).byteLength, 231)
const verifier = createBlindInboxControlSnapshotSemanticVerifier(semanticAuthority)
t.is(verifyBlindInboxControlSnapshotSemanticVerifier(verifier), verifier)
const result = await verifier({
...headers(state),
declaredEntryCount: entries.length,
entries
})
t.is(verifyBlindInboxControlSnapshotSemanticResult(result), result)
t.is(result.inboxComplete, true)
t.is(result.recoveryVerified, true)
t.is(result.publicationAuthorized, false)
t.is(result.productionComplete, false)
t.is(BLIND_INBOX_CONTROL_SNAPSHOT_STATUS.scalableCandidateEntryStreamingImplemented, false)
t.is(BLIND_INBOX_CONTROL_SNAPSHOT_STATUS.frameBodyAvailabilityAndHashVerificationImplemented, false)
const recovered = result.inboxState
const inbox = recovered.inboxes.values().next().value
t.is(inbox.allocationLeaseClass, 1)
t.is(inbox.leaseClass, 4)
t.is(recovered.frames.size, 1)
t.is(recovered.retryPins.size, 1)
t.is(recovered.accounting.controlBytes, 2560)
t.is(recovered.accounting.stagingByProfile.get(3), INBOX_FRAME_CLASS[2])
})
test('Inbox semantic result state, tuple, authority, and verifier branding are private and immutable', async t => {
t.ok(createBlindInboxControlSnapshotSemanticAuthority())
await t.exception.all(() => authority({ maximumCandidateEntries: 0 }), /maximumCandidateEntries is outside/)
const semanticAuthority = authority()
const state = fixture()
const entries = await entriesFor(semanticAuthority, state)
const result = await reconstruct(semanticAuthority, state, entries)
const expected = headers(state).header
result.relayPublicKey.fill(0)
const exposed = result.inboxState
exposed.inboxes.clear()
exposed.frames.clear()
exposed.retryPins.clear()
exposed.accounting.stagingByProfile.clear()
verifyBlindInboxControlSnapshotSemanticResult(result, { ...expected, entryCount: entries.length })
t.alike(result.relayPublicKey, expected.relayPublicKey)
t.is(result.inboxState.inboxes.size, 1)
t.is(result.inboxState.frames.size, 1)
t.is(result.inboxState.retryPins.size, 1)
await t.exception.all(() => verifyBlindInboxControlSnapshotSemanticVerifier(() => {}), /branded Inbox/)
await t.exception.all(() => verifyBlindInboxControlSnapshotSemanticResult({ ...result }), /branded Inbox/)
await t.exception(verifyBlindLocalCheckpointSnapshotSemanticAuthority(result, {}, '/private/tmp', {}),
/snapshot semantic authority is forged, expired, or unsupported/)
})
test('Inbox terminal spends and owner-close tombstones reconstruct without losing retained idempotency', async t => {
const semanticAuthority = authority()
const terminalState = terminalFixture()
const terminalEntries = await entriesFor(semanticAuthority, terminalState)
const terminalResult = await reconstruct(semanticAuthority, terminalState, terminalEntries)
t.is([...terminalResult.inboxState.spends.values()].filter(value => value.status === 'terminal').length, 1)
t.is(terminalResult.inboxState.accounting.stagingFrameBytes, 0)
t.is(terminalResult.inboxState.accounting.reservedFrames, 0)
const closedState = closedFixture()
const closedEntries = await entriesFor(semanticAuthority, closedState)
const closedResult = await reconstruct(semanticAuthority, closedState, closedEntries)
t.is(closedResult.inboxState.requestResults.size, 1)
const closedInbox = closedResult.inboxState.inboxes.values().next().value
t.is(closedInbox.objectState, 2)
t.is(closedInbox.tombstoneReason, 1)
t.is(closedResult.inboxState.retryPins.values().next().value.retryState, 2)
})
test('expired Inbox APPEND keeps a fixed anti-replay record and exact signed ACK without a frame', async t => {
const semanticAuthority = authority()
const state = expiredAppendFixture()
const entries = await entriesFor(semanticAuthority, state)
const result = await reconstruct(semanticAuthority, state, entries)
const expired = [...result.inboxState.spends.values()]
.find(value => value.status === 'expired-append')
t.ok(expired)
t.is(result.inboxState.frames.size, 0)
t.is(expired.expiredEpoch, 104)
t.is(expired.expiresAtEpoch, 104)
t.ok(expired.resultBindingBytes.byteLength > 0)
t.is(expired.ackSignature.byteLength, 64)
t.is(expired.resultCommitment.byteLength, 32)
t.is(result.inboxState.accounting.controlBytes, 1536)
const tampered = entries.map(entry => ({
...entry,
key: b4a.from(entry.key),
value: b4a.from(entry.value)
}))
const expiredIndex = tampered.findIndex(entry => entry.entryKind === 1 && entry.key[1] === 4)
tampered[expiredIndex].value[tampered[expiredIndex].value.byteLength - 40] ^= 1
await t.exception(reconstruct(semanticAuthority, state, tampered),
/expired APPEND|signature|commitment|binding|canonical/i)
})
test('Inbox reconstruction rejects unknown, duplicate, incomplete, substituted, and misaccounted control state', async t => {
const semanticAuthority = authority()
const state = fixture()
const entries = await entriesFor(semanticAuthority, state)
const unknown = entries.map(entry => ({ ...entry, key: b4a.from(entry.key), value: b4a.from(entry.value) }))
const inboxIndex = unknown.findIndex(entry => entry.entryKind === 4)
unknown[inboxIndex].key[1] = 0xff
await t.exception(reconstruct(semanticAuthority, state, unknown), /unknown Inbox control snapshot entry/)
const duplicate = [...entries]
duplicate.splice(1, 0, entries[0])
await t.exception(reconstruct(semanticAuthority, state, duplicate), /strictly sorted and duplicate-free/)
const incomplete = entries.filter(entry => !(entry.entryKind === 6 && entry.key.byteLength === 2))
await t.exception(reconstruct(semanticAuthority, state, incomplete), /incomplete without its global record/)
const substituted = entries.map(entry => ({ ...entry, key: b4a.from(entry.key), value: b4a.from(entry.value) }))
const reservationIndex = substituted.findIndex(entry => entry.entryKind === 2)
substituted[reservationIndex].key[substituted[reservationIndex].key.byteLength - 1] ^= 1
await t.exception(reconstruct(semanticAuthority, state, substituted), /key does not match spendTag/)
const misaccounted = entries.map(entry => ({ ...entry, key: b4a.from(entry.key), value: b4a.from(entry.value) }))
const globalIndex = misaccounted.findIndex(entry => entry.entryKind === 6 && entry.key.byteLength === 2)
const global = decodeCanonical(blindInboxControlGlobalSnapshotV1, misaccounted[globalIndex].value, { copyBytes: true })
global.storedFrameBytes++
misaccounted[globalIndex].value = encodeCanonical(blindInboxControlGlobalSnapshotV1, global)
await t.exception(reconstruct(semanticAuthority, state, misaccounted), /storedFrameBytes accounting does not reconstruct exactly/)
const nonInbox = entries.map(entry => ({ ...entry, key: b4a.from(entry.key), value: b4a.from(entry.value) }))
nonInbox[0].key[0] = FAMILY.CELL
await t.exception(reconstruct(semanticAuthority, state, nonInbox), /rejects non-Inbox snapshot entries/)
})
test('Inbox candidate and retry reconstruction reject in-flight, divergent, substituted, missing, and misbound state', async t => {
const semanticAuthority = authority()
const inFlight = fixture()
for (const value of inFlight.spends.values()) {
if (value.status === 'reserved') value.inFlight = true
}
await t.exception(entriesFor(semanticAuthority, inFlight), /in-flight Inbox reservations/)
const divergent = fixture()
divergent.commitments.values().next().value.fingerprint = '00'.repeat(32)
await t.exception(entriesFor(semanticAuthority, divergent), /commitment index does not match reconstructed spends/)
const wrongBucket = fixture()
wrongBucket.inboxes.values().next().value.metadataVirtualBucket ^= 1
await t.exception(entriesFor(semanticAuthority, wrongBucket), /metadata virtual bucket does not match/)
const badExpiry = fixture()
badExpiry.frames.values().next().value.expiresAtEpoch--
await t.exception(entriesFor(semanticAuthority, badExpiry), /frame expiry does not match/)
const missingFrame = fixture()
missingFrame.frames.clear()
missingFrame.accounting.storedFrameBytes = 0
missingFrame.accounting.frameIndexBytes = 0
await t.exception(entriesFor(semanticAuthority, missingFrame), /retry frame pin has no matching immutable frame reference/)
const substitutedPin = fixture()
substitutedPin.retryPins.values().next().value.entries[0].frameHash = bytes(0x7f)
await t.exception(entriesFor(semanticAuthority, substitutedPin), /retry frame pin has no matching immutable frame reference/)
const badSource = fixture()
badSource.retryPins.values().next().value.sourceCommitment = bytes(0x7e)
await t.exception(entriesFor(semanticAuthority, badSource), /retry source commitment does not match/)
const tooSmall = authority({ maximumCandidateEntries: 1 })
await t.exception(entriesFor(tooSmall, fixture()), /exceeds its configured entry bound/)
})