// The receipt of a vote on the Ğ1 chain, checked in the browser: the same // canonical text and the same seal as the engine (crates/ld-engine/src/ballot.rs, // whose vectors were computed apart with Python's hashlib.blake2b). import { describe, expect, it } from 'vitest'; import { canonical, checkReceipt, seal } from '../../src/lib/receipt'; const SALT = '00112233445566778899aabbccddeeff'; const LINES = [ { proposalId: 'p2', revision: 1, choice: 'against' as const }, { proposalId: 'p1', revision: 3, choice: 'for' as const }, ]; describe('the sealed ballot', () => { it('reads as the engine writes it, the lines in the order of their versions', () => { expect(canonical('d0c', null, LINES, SALT)).toBe( 'ld1\ndoc d0c\nauthor -\nline p1 3 for\nline p2 1 against\nsalt 00112233445566778899aabbccddeeff\n', ); }); it('seals to the remark of the engine', () => { const s = seal('d0c', null, LINES, SALT); expect(s?.digest).toBe('20448d496744292981c8586856afb58a86e37da2470f764b4e6ed84eade71340'); expect(s?.comment).toBe('ld1:20448d496744292981c8586856afb58a86e37da2470f764b4e6ed84eade71340'); expect(s?.remarkHash).toBe('0xe765221a75716dde863a7b53df3a97acf6db38a2118edd525d54c8a2979b7bc3'); const bound = seal('d0c', 'g1NrqKgJW9FoFabaYW82zhS9tDWdQcL1DGpivaJdj7GW9M6iv', [{ proposalId: 'p1', revision: 3, choice: 'none' }], 'ffeeddccbbaa99887766554433221100'); expect(bound?.digest).toBe('6a6d8a90b5003ff3c4d050dbd36199e8bbf34c4cc11620618399226c9237c05e'); expect(bound?.remarkHash).toBe('0x846495fe62b9a5184a0d3090d4d26f621887c334890404169c00575ee74540e4'); }); it('what cannot be sealed', () => { expect(seal('d0c', null, [], SALT)).toBeNull(); expect(seal('d0c', null, [LINES[0]!, { ...LINES[0]!, revision: 2 }], SALT)).toBeNull(); expect(seal('d 0c', null, LINES, SALT)).toBeNull(); expect(seal('d0c', null, LINES, SALT.toUpperCase())).toBeNull(); expect(seal('d0c', null, LINES, '0011')).toBeNull(); expect(seal('d0c', null, [{ proposalId: 'p1', revision: 1.5, choice: 'for' }], SALT)).toBeNull(); }); }); describe('a receipt', () => { const receipt = { format: 'ld1', doc: 'd0c', author: null, lines: LINES.map((l) => ({ ...l, article: 'E1', title: 'Unicité du compte' })), salt: SALT, comment: 'ld1:20448d496744292981c8586856afb58a86e37da2470f764b4e6ed84eade71340', block: 3_400_000, }; it('holds when its lines and its salt give its comment', () => { expect(checkReceipt(JSON.stringify(receipt))).toEqual({ kind: 'ok', comment: receipt.comment, remarkHash: '0xe765221a75716dde863a7b53df3a97acf6db38a2118edd525d54c8a2979b7bc3', block: 3_400_000, lines: receipt.lines, }); }); it('does not hold once a choice is changed', () => { const changed = { ...receipt, lines: [{ ...receipt.lines[0]!, choice: 'for' }, receipt.lines[1]!] }; const check = checkReceipt(JSON.stringify(changed)); expect(check.kind).toBe('mismatch'); if (check.kind === 'mismatch') expect(check.computed).not.toBe(receipt.comment); }); it('what is not a receipt', () => { for (const text of ['', 'nope', '[]', '{}', JSON.stringify({ ...receipt, format: 'ld2' }), JSON.stringify({ ...receipt, lines: 'x' })]) { expect(checkReceipt(text).kind, text).toBe('unreadable'); } }); });