OverviewFilesCommitsIssuesDocsReleasesPeers

keet-daemon / test / probe-state-v3-admission.mjs

70 lines · javascript ref:
// P1 v3.0b verify: multi-writer admission (D1b), isolated + deterministic — same
// scope as the v2 probe (no-reject + durable optimistic + in-apply verify;
// cross-node landing needs the swarm e2e, not here). Raw-ed25519 proofs.
import { StateBaseV3 } from '../lib/state-base-v3.js'
import crypto from 'hypercore-crypto'
import b4a from 'b4a'
import fs from 'bare-fs'
import path from 'bare-path'
import os from 'bare-os'

const ROOT = path.join(os.tmpdir(), 'sbv3-admit-' + Bare.pid)
try { fs.rmSync(ROOT, { recursive: true, force: true }) } catch {}
const roomKey = crypto.randomBytes(32)
const roomSeed = crypto.randomBytes(32)
const kpA = crypto.keyPair() // admin → elected creator
const kpB = crypto.keyPair() // peer member (must be admitted optimistically)
const kpC = crypto.keyPair() // outsider (NOT a member → must be rejected)
const hex = (b) => b4a.toString(b, 'hex')
const members = [
  { key: hex(kpA.publicKey), role: 1 },
  { key: hex(kpB.publicKey), role: 3 }
]
const baseOpts = (kp) => ({ roomSeed, encryptionKey: roomSeed, network: false, selfKeyPair: kp, selfKey: kp.publicKey, getMembers: async () => members })

let pass = 0; let fail = 0
const ok = (c, m) => { if (c) { pass++; console.log('  ok  ' + m) } else { fail++; console.log('  FAIL ' + m) } }

async function main () {
  // A = creator (lex election: only admin) → writable; projects membership.
  const A = new StateBaseV3(path.join(ROOT, 'A'), roomKey, baseOpts(kpA))
  await A.ready()
  ok(A.writable, 'A (admin) elected creator → writable')
  await A.projectMembers(members)
  ok((await A.getProjectedRole(hex(kpB.publicKey))) === 3, 'membership projected: B is a peer member')

  // B = member, NOT writable → set() must be optimistic, no throw, durable.
  const B = new StateBaseV3(path.join(ROOT, 'B'), roomKey, baseOpts(kpB))
  await B.ready()
  ok(!B.writable, 'B (peer) opens as member, not writable')
  let threw = null
  try { await B.set('world:players:b', { level: 'town' }) } catch (e) { threw = e.message }
  ok(threw === null, '1. B.set() did NOT throw "not writable" (optimistic)')
  ok(B.base.local.length >= 1, '2. optimistic write is DURABLE in B oplog (local.length=' + B.base.local.length + ')')

  // 3. in-apply verify: A accepts B's proof (B ∈ members); rejects outsider C.
  const admitB = await B._buildAdmissionProof()
  ok(!!admitB, 'B built an admission proof')
  const okB = await A._verifyAdmissionInApply(B.base.local.key, admitB, A.base.view)
  ok(okB === true, '3a. A verifies B admission (valid proof + B is a member) → ACCEPT')

  // outsider C: valid signature but NOT in members → reject
  const cLocal = crypto.randomBytes(32)
  const sigC = crypto.sign(b4a.concat([cLocal, A.base.key]), kpC.secretKey)
  const admitC = JSON.stringify({ identity: hex(kpC.publicKey), proof: hex(sigC) })
  const okC = await A._verifyAdmissionInApply(cLocal, admitC, A.base.view)
  ok(okC === false, '3b. A rejects outsider C (valid sig but NOT a member) → REJECT')

  // tampered proof (wrong sig) → reject
  const badSig = crypto.randomBytes(64)
  const admitBad = JSON.stringify({ identity: hex(kpB.publicKey), proof: hex(badSig) })
  const okBad = await A._verifyAdmissionInApply(B.base.local.key, admitBad, A.base.view)
  ok(okBad === false, '3c. A rejects a tampered/invalid proof → REJECT')

  await A.close(); await B.close()
  try { fs.rmSync(ROOT, { recursive: true, force: true }) } catch {}
  console.log('\n[probe] pass=' + pass + ' fail=' + fail)
  Bare.exit(fail ? 1 : 0)
}
main().catch((e) => { console.error('[probe] THREW:', e && e.stack ? e.stack : e); Bare.exit(2) })