OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-proto-peer / test / restore.test.js

71 lines · javascript ref:
// Restart continuity: after a daemon restart a device re-opens the shared state with head=null and
// _lastRecords=[]. Without restore(), the next sign diffs against an EMPTY baseline → re-emits the
// FULL record set as creates (the ops=[9,..,12] firehose bug that broke relay/AppView MST continuity).
// restore() re-seeds head + baseline from the shared __commit__ log so the firehose stays INCREMENTAL.
// Deterministic (network:false).

import '../lib/bare-globals.js'
import test from 'brittle'
import crypto from 'hypercore-crypto'
import fs from 'bare-fs'
import b4a from 'b4a'
import { StateBaseV3 } from 'atproto-room/lib/state-base.js'
import { SwarmDevice } from '../lib/swarm-device.js'
import { makeAttest, makeVerifyAttest, attestDeviceChain, deviceKeetKeyPair, rootIdentityPublicKey } from 'atproto-room/lib/membership.js'
import { deriveAtprotoKey } from 'atproto-identity'

const SIGN_SRC = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
const KEET_ROOT = 'legal winner thank year wave sausage worth useful legal winner thank yellow'
const DEV = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong'
const DID = 'did:plc:restore22222222222222222'
const POST = 'app.bsky.feed.post'
const signKey = deriveAtprotoKey(SIGN_SRC)
const tmp = () => { const d = '/tmp/hpp-restore-' + Math.floor(Math.random() * 1e9); fs.mkdirSync(d, { recursive: true }); return d }

async function mkDevice (dir) {
  const rootPub = await rootIdentityPublicKey(KEET_ROOT)
  const dev = await deviceKeetKeyPair(DEV)
  const proof = await attestDeviceChain({ rootMnemonic: KEET_ROOT, devicePublicKey: dev.publicKey })
  const members = [{ key: b4a.toString(dev.publicKey, 'hex'), role: 1 }]
  const A = new StateBaseV3(dir, crypto.hash(b4a.from(DID)), {
    roomSeed: crypto.hash(b4a.from('restore-seed')), encryptionKey: crypto.hash(b4a.from('restore-enc')), network: false,
    selfKey: dev.publicKey, attest: makeAttest(dev, proof), verifyAttest: makeVerifyAttest(rootPub), getMembers: async () => members
  })
  await A.ready(); await A.projectMembers(members)
  const device = new SwarmDevice(A, { did: DID, privateKey: signKey.privateKey, self: b4a.toString(dev.publicKey, 'hex'), firehose: true })
  return { A, device }
}

test('restart restore: the firehose stays INCREMENTAL (no full-set re-author)', async (t) => {
  const dir = tmp()
  // 1. boot, write 2 records over 2 signs → 2 incremental #commit frames
  const one = await mkDevice(dir)
  await one.device.claimLease()
  await one.device.putRecord(POST, '3aaawu7vak2001', { $type: POST, text: 'a', createdAt: '2026-07-05T00:00:00.000Z' })
  await one.device.sign()
  await one.device.putRecord(POST, '3bbbwu7vak2002', { $type: POST, text: 'b', createdAt: '2026-07-05T00:00:01.000Z' })
  const h2 = await one.device.sign()
  const log1 = await one.device.firehoseLog()
  t.is(log1.length, 2, 'two #commit frames')
  t.is(log1[1].ops.length, 1, 'second frame is incremental (1 op, not the full set)')
  await one.A.close()

  // 2. "restart": a FRESH SwarmDevice on the SAME state — head=null, _lastRecords=[]. bootSwarmDevice
  // runs restore() at boot BEFORE the control surface accepts any writes, so mirror that ordering here.
  const two = await mkDevice(dir)
  t.teardown(async () => { try { await two.A.close() } catch {} })
  t.absent(two.device.head, 'fresh instance starts with no in-memory head')
  const restored = await two.device.restore()
  t.ok(restored && restored.commitCid.equals(h2.commitCid), 'restore() re-seeds head to the last signed commit')
  await two.device.claimLease()

  // 3. add ONE record + sign — must be INCREMENTAL (1 op), not re-author all 3
  await two.device.putRecord(POST, '3cccwu7vak2003', { $type: POST, text: 'c', createdAt: '2026-07-05T00:00:02.000Z' })
  const h3 = await two.device.sign()
  t.ok(h3, 'restarted device signed a continuing commit')
  const log2 = await two.device.firehoseLog()
  t.is(log2.length, 3, 'exactly ONE new frame appended (no full-set re-author)')
  t.is(log2[2].ops.length, 1, 'the post-restart frame is INCREMENTAL (1 op, not 3)')
  t.is(log2[2].prev, h2.commitCid.toString(), 'the new frame chains from the RESTORED head (prev = last signed commit)')
})