Something went wrong. Try again.
A Social Deduction / Deception Game
Something went wrong. Try again.
3.1 kB · 72 lines
TypeScript
at main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273import { describe, it, expect } from 'vitest'import { Secp256k1Keypair } from '@atproto/crypto'import { inlineSign, verifyInline, type SignatureEntry } from './attestation.js'
const REPO = 'did:plc:exampleexampleexampleex'const ISSUED = '2026-06-08T00:00:00.000Z'
function record() { return { $type: 'town.deception.game.result', game: 'at://did:plc:host/town.deception.game/abc', role: 'worker', status: 'alive', outcome: 'workers', config: { resourceCount: 4, roundDurationSec: 600, targets: [{ resourceId: 'wood', target: 3 }] }, actions: [{ serverTime: 1000, type: 'actionStarted', station: 'res-wood', detail: 'collect' }], createdAt: ISSUED, }}
describe('inline attestation (badge.blue style)', () => { it('signs a record and verifies against the binding repo', async () => { const signer = await Secp256k1Keypair.create({ exportable: true }) const entry = await inlineSign(record(), REPO, signer, ISSUED) const full = { ...record(), signatures: [entry] }
expect(entry.key).toBe(signer.did()) expect(entry.cid).toMatch(/^bafy/) expect(await verifyInline(full, REPO, entry)).toBe(true) })
it('fails verification in a different repo (replay binding)', async () => { const signer = await Secp256k1Keypair.create({ exportable: true }) const entry = await inlineSign(record(), REPO, signer, ISSUED) const full = { ...record(), signatures: [entry] }
expect(await verifyInline(full, 'did:plc:someoneelseeeeeeeeeeeeee', entry)).toBe(false) })
it('fails verification when the record is tampered with', async () => { const signer = await Secp256k1Keypair.create({ exportable: true }) const entry = await inlineSign(record(), REPO, signer, ISSUED)
const tampered = { ...record(), outcome: 'saboteur', signatures: [entry] } expect(await verifyInline(tampered, REPO, entry)).toBe(false) })
it('fails verification when the signature bytes are swapped', async () => { const signer = await Secp256k1Keypair.create({ exportable: true }) const other = await Secp256k1Keypair.create({ exportable: true }) const entry = await inlineSign(record(), REPO, signer, ISSUED) const forged: SignatureEntry = { ...entry, signature: { $bytes: Buffer.from(await other.sign(new Uint8Array([1, 2, 3]))).toString('base64') }, } const full = { ...record(), signatures: [forged] } expect(await verifyInline(full, REPO, forged)).toBe(false) })
it('is stable regardless of how many signatures are attached', async () => { // The signing payload strips the `signatures` array, so adding a second signature must not // invalidate the first. const a = await Secp256k1Keypair.create({ exportable: true }) const b = await Secp256k1Keypair.create({ exportable: true }) const e1 = await inlineSign(record(), REPO, a, ISSUED) const e2 = await inlineSign(record(), REPO, b, ISSUED) const full = { ...record(), signatures: [e1, e2] }
expect(await verifyInline(full, REPO, e1)).toBe(true) expect(await verifyInline(full, REPO, e2)).toBe(true) })})