// 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) })