OverviewFilesCommitsIssuesDocsReleasesPeers

favela-peer / test / room-replay.test.js

103 lines · javascript ref:
// ROOM-CONTRACT §1.1 / §6 test 1 — a captured room op replayed by ANOTHER writer must do nothing.
//
// ⛔ v1 (room.js 0.0.62) authorises an op by the identity root signed INTO its payload, so a correctly
// signed op is valid every time it appears, from anyone: re-appending a captured `add-member` re-admits a
// member a moderator removed. v2 authorises by the writer core that CARRIED the op (Keet's model), so a
// replay from any other core is ignored by construction.

import test from 'brittle'
import Corestore from 'corestore'
import fs from '#node:fs'
import os from '#node:os'
import { join } from '#node:path'
import { Room, PEER, ADMIN } from '../lib/room.js'
import { identity } from './helpers.js'

const tmp = () => fs.mkdtempSync(join(os.tmpdir(), 'favela-replay-'))

async function who () {
  const id = await identity()
  return { root: id.rootHex, device: await id.device() }
}

const until = async (fn, ms = 10000) => {
  const end = Date.now() + ms
  while (Date.now() < end) { const v = await fn(); if (v) return v; await new Promise((resolve) => setTimeout(resolve, 40)) }
  return null
}

test('a captured op re-appended by another writer core is ignored (§1.1 replay)', async (t) => {
  const admin = await who()
  const peer = await who()
  const one = new Room(new Corestore(tmp()).namespace('room'), null)
  await one.ready()
  await one.found(admin.device)
  await one.update()

  // A second node in the room as a plain member, carrying its own core.
  const two = new Room(new Corestore(tmp()).namespace('room'), one.key)
  await two.ready()
  const s1 = one.base.replicate(true); const s2 = two.base.replicate(false)
  s1.pipe(s2).pipe(s1)
  t.teardown(async () => { s1.destroy(); s2.destroy(); await one.close(); await two.close() })
  await admitAsPeer(t, one, two, admin, peer)

  // A third identity the admin lets in, then removes — the state a replay would try to undo.
  const victim = await who()
  const captured = await letInThenRemove(one, admin, victim)
  t.is(await one.roleOf(victim.root), null, 'precondition: the member is removed')

  // The PEER's node re-appends the admin's captured op verbatim.
  await two.base.append(captured, { optimistic: true })
  await until(async () => { await one.update(); await two.update(); return false }, 1500)
  t.is(await one.roleOf(victim.root), null, 'the replayed op did not bring the removed member back')
  t.ok(await one.roleOf(admin.root) === ADMIN, 'and the admin is still the admin')
  if (typeof one.invites === 'function') {
    // v2: the captured op was the admin's ADMIN invitation. Replayed from the peer's core it is the peer's
    // op — and its id is already taken, so it creates nothing; no invitation is ever attributed to the peer.
    const invites = await one.invites()
    t.absent(invites.some((i) => i.by === peer.root), 'the replay created no invitation at all')
    t.ok(invites.every((i) => i.by === admin.root), 'every invitation is the admin\'s own')
  }
})

// v1 and v2 admit a member differently; each branch is the documented path for its version.
async function admitAsPeer (t, one, two, admin, peer) {
  if (typeof one.invite === 'function') {
    const { token } = await one.invite({ role: PEER }, admin.device)
    await one.update()
    await until(async () => { await two.update(); return (await two.members()).length >= 1 })
    await two.join(token, peer.device)
  } else {
    await one.addMember(peer.root, PEER, admin.device)
    await one.admitDevice(peer.device, admin.device, two.base.local.key.toString('hex'))
  }
  t.ok(await until(async () => { await one.update(); await two.update(); return (await one.roleOf(peer.root)) === PEER }), 'the peer is in')
}

// Returns the raw bytes of the op that ADMITTED the victim, as a replayer would have captured them.
// ⛔ The OP VALUE handed to append — not `base.local.get(n)`, which is an encoded oplog node (links,
// clock) and re-appending it wraps it again, so apply cannot even parse it and the "replay" proves nothing.
async function letInThenRemove (one, admin, victim) {
  const seen = []
  const append = one.base.append.bind(one.base)
  one.base.append = (value, opts) => { seen.push(value); return append(value, opts) }
  try {
    if (typeof one.invite === 'function') {
      // v2 has no admin-signed admission to replay: a member is admitted by its OWN join, from its OWN
      // core. The captured op is the admin's invite — replaying an admin op from a peer's core must not
      // act as the admin (it would at most be the peer's own invite, capped at the peer's role).
      await one.invite({ role: ADMIN }, admin.device)
      await one.update()
      await one.removeMember(victim.root, admin.device)
      await one.update()
      return seen[0]
    }
    await one.addMember(victim.root, PEER, admin.device)
    await one.update()
    await one.removeMember(victim.root, admin.device)
    await one.update()
    return seen[0]
  } finally { one.base.append = append }
}