// The consensus page's central result as a file a program can read. // // Raft's rule is that a leader may count replicas toward committing an entry only // if the entry is from the leader's own term. The page removes that clause, searches // every reachable state of three servers through four terms, and finds the shortest // run in which two servers disagree about a committed entry. This writes that run, // the bounds, the state counts under each rule and the engine's file hash to JSON, // so a reader can replay it without opening the page. // // node scripts/consensus-counterexample.mjs write tests/fixtures/consensus-counterexample.json // node scripts/consensus-counterexample.mjs --check regenerate and compare, write nothing import { readFileSync, writeFileSync } from 'node:fs'; import { createHash } from 'node:crypto'; import { fileURLToPath } from 'node:url'; import { dirname, join } from 'node:path'; const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); const SRC = readFileSync(join(ROOT, 'site/js/consensus.js'), 'utf8'); const window = {}; new Function('window', SRC)(window); const C = window.Consensus; const test = readFileSync(join(ROOT, 'tests/consensus.test.mjs'), 'utf8'); const box = test.match(/const BOX = (\{[^}]*\})/); if (!box) throw new Error('tests/consensus.test.mjs no longer defines BOX: the bounds below would be a guess'); const BOX = new Function(`return ${box[1]}`)(); const raft = C.check({ ...BOX, rule: 'raft' }); const majority = C.check({ ...BOX, rule: 'majority' }); const lower = C.check({ ...BOX, maxTerm: 3, rule: 'majority' }); const out = { what: 'Raft with the current-term clause removed (rule "majority"), searched exhaustively inside the bounds given. The trace is the shortest run that breaks a safety property.', engine: 'site/js/consensus.js', engine_sha256: createHash('sha256').update(SRC).digest('hex'), bounds: BOX, states: { raft: raft.states, majority: majority.states, majority_with_terms_to_3: lower.states }, raft_fails: !!raft.failed, majority_fails: !!majority.failed, majority_with_terms_to_3_fails: !!lower.failed, trace_length: majority.trace ? majority.trace.length : null, trace: majority.trace || null, failure: majority.failed || null, }; const text = JSON.stringify(out, null, 2) + '\n'; const path = join(ROOT, 'tests/fixtures/consensus-counterexample.json'); if (process.argv.includes('--check')) { const have = readFileSync(path, 'utf8'); if (have !== text) { console.error('consensus-counterexample.json is stale: rerun without --check'); process.exit(1); } console.log('consensus-counterexample.json is current'); } else { writeFileSync(path, text); console.log(`wrote ${path}: trace of ${out.trace_length} steps, ${out.states.majority} states searched`); }