OverviewFilesCommitsIssuesDocsReleasesPeers

keet-daemon / test / probe-state-optimistic-admission.js

86 lines · javascript ref:
// probe-state-optimistic-admission.js
//
// D1b — what optimistic admission ACTUALLY guarantees (verified in isolation):
//   (1) NO-REJECT: a not-yet-admitted member's set() does not throw "Not
//       writable"; the write is appended OPTIMISTICALLY.
//   (2) DURABLE: that write lands in the member's own oplog and survives a
//       reopen (not lost — consistent with "durability != online indexer").
//   (3) GATE LOGIC: the in-apply verifier (_verifyAdmissionInApply) acks a
//       MEMBER with a valid proof, and rejects a NON-member or a bad proof.
//
// Cross-node "the write LANDS on the creator after admission" is a separate
// concern (it needs _admitWriters core-conveyance + an online acker) and is
// covered by the D1c swarm/admit e2e — NOT here.

import os from 'os'
import path from 'path'
import fs from 'fs'
import crypto from 'hypercore-crypto'
import { StateBase } from '../lib/state-base.js'

const ROOT = fs.mkdtempSync(path.join(os.tmpdir(), 'optadmit-'))
let failures = 0
const check = (label, cond) => { console.log((cond ? 'PASS ' : 'FAIL ') + label); if (!cond) failures++ }
const exit = (code) => (globalThis.Bare?.exit ?? globalThis.process?.exit ?? (() => {}))(code)

async function main () {
  const roomKey = crypto.hash(Buffer.from('opt-admission-test'))
  const kpA = crypto.keyPair() // creator/admin
  const kpB = crypto.keyPair() // joining member (its set() must not hard-reject)
  const X = crypto.keyPair()   // a projected MEMBER identity
  const Y = crypto.keyPair()   // a NON-member identity

  // Creator A: writable, projects membership (A + X are members; Y is NOT).
  const A = new StateBase(path.join(ROOT, 'A'), roomKey, { network: false, selfKeyPair: kpA })
  await A.ready(); await A.ensureFounder()
  await A.projectMembers([
    { key: kpA.publicKey.toString('hex'), role: 1 },
    { key: X.publicKey.toString('hex'), role: 3 }
  ])
  check('0. A writable + membership projected (X is a member)', A.writable && (await A.getProjectedRole(X.publicKey.toString('hex'))) === 3)

  // ── (1) + (2): NO-REJECT + DURABLE ──────────────────────────────
  // B shares A's base (adopt key), is NOT yet admitted.
  const B = new StateBase(path.join(ROOT, 'B'), roomKey, { network: false, selfKeyPair: kpB })
  await B.ready(); await B._openBase(A.stateBaseKey)
  check('1a. B opened shared base, NOT writable', !!B.base && B.base.writable === false)

  let threw = null
  try { await B.set('world:terrain:edit:1', { pos: [1, 2, 3] }) } catch (e) { threw = e.message }
  check('1b. B.set() did NOT throw "Not writable"', threw === null)
  check('2a. optimistic write is DURABLE in B\'s oplog (local.length === 1)', B.base.local.length === 1)
  // (durability across reopen is the same persisted-oplog property proven in
  // probe-durability-no-indexer.js / probe-state-membership-projection.js; we
  // skip a reopen here to avoid the known close() teardown hang.)

  // ── (3): GATE LOGIC — _verifyAdmissionInApply on the creator (a writer) ──
  const baseKey = A.base.key
  const localKey = crypto.randomBytes ? crypto.randomBytes(32) : crypto.keyPair().publicKey
  const signData = Buffer.concat([localKey, baseKey])
  const view = A.base.view

  // member X, valid proof → ACK (true)
  const memberAdmit = { identity: X.publicKey.toString('hex'), proof: crypto.sign(signData, X.secretKey).toString('hex') }
  check('3a. MEMBER with valid proof → admitted (true)', (await A._verifyAdmissionInApply(localKey, memberAdmit, view)) === true)

  // non-member Y, valid proof but not in membership → REJECT (false)
  const nonMemberAdmit = { identity: Y.publicKey.toString('hex'), proof: crypto.sign(signData, Y.secretKey).toString('hex') }
  check('3b. NON-member (valid proof, not in __members__) → rejected (false)', (await A._verifyAdmissionInApply(localKey, nonMemberAdmit, view)) === false)

  // member X identity but proof actually from Y (forged) → REJECT (false)
  const forgedAdmit = { identity: X.publicKey.toString('hex'), proof: crypto.sign(signData, Y.secretKey).toString('hex') }
  check('3c. forged proof (X identity, Y signature) → rejected (false)', (await A._verifyAdmissionInApply(localKey, forgedAdmit, view)) === false)

  // proof bound to a DIFFERENT base.key (replay) → REJECT (false)
  const replaySignData = Buffer.concat([localKey, crypto.randomBytes ? crypto.randomBytes(32) : crypto.keyPair().publicKey])
  const replayAdmit = { identity: X.publicKey.toString('hex'), proof: crypto.sign(replaySignData, X.secretKey).toString('hex') }
  check('3d. proof bound to another base.key (replay) → rejected (false)', (await A._verifyAdmissionInApply(localKey, replayAdmit, view)) === false)

  // Skip explicit close() (known teardown hang); the process exit reclaims it.
  try { fs.rmSync(ROOT, { recursive: true, force: true }) } catch {}
  console.log('\n' + (failures === 0 ? 'ALL PASS' : failures + ' FAILURE(S)'))
  exit(failures === 0 ? 0 : 1)
}

main().catch((e) => { console.error('THREW:', e); exit(2) })