Below is a tiny encoded proof: a long string of bits in which every honest claim is repeated as a redundant, self-consistent pattern. A verifier never reads the whole thing — it picks a couple of random positions, checks one simple local rule, and accepts or rejects.
<p class="hint">This is an <b>encoded proof</b>: each honest claim is repeated across a whole block of bits, so the bits inside a block must all agree. The verifier reads only <b>2 random bits from one block</b> and rejects if they disagree. Honest proofs always pass; flip a few bits to cheat, then run the verifier and watch it catch you.</p>
<div id="proof" class="proof"></div>
<div class="status" id="status">Honest proof loaded. Pick an action.</div>
<div class="btns">
<button id="round" type="button">Run 1 check (read 2 bits)</button>
<button id="many" type="button">Run 20 checks</button>
<button id="reset" type="button" class="ghost">Reset to honest proof</button>
</div>
<p class="note" id="note"></p>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.proof { display: flex; flex-wrap: wrap; gap: 4px; margin: .4rem 0; }
.blk { display: flex; gap: 3px; padding: 4px; border: 1px dashed #cdd9e3; border-radius: 8px; }
.bit { width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
font: 700 15px ui-monospace, monospace; border-radius: 6px; cursor: pointer;
background: #e8eef3; color: #1d3557; border: 1px solid #cdd9e3; user-select: none; transition: all .1s; }
.bit:hover { background: #d6e0ea; }
.bit.flipped { background: #ffd9a8; border-color: #e08a1e; }
.bit.read { outline: 3px solid #1d3557; outline-offset: 1px; }
.bit.caught { background: #e63946; border-color: #c92f3c; color: #fff; }
.status { font-size: 1rem; font-weight: 600; margin: .6rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
.note { font-size: .82rem; color: #555; margin: .6rem 0 0; min-height: 1.2em; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// A PCP-style toy. The "encoded proof" is split into BLOCKS; in an honest proof
// every bit inside a block is identical (a repetition encoding of one logical bit).
// The verifier’s LOCAL RULE: pick one random block, read 2 random distinct bits from
// it, accept iff they are equal. It never reads the whole proof.
// - Honest proof: every pair inside every block agrees -> always accepts.
/* - Tampered proof: any cheated block has disagreeing pairs, so each round has a
real chance of catching it. Repeat rounds -> detection probability -> 1. */
const BLOCKS = 4, PER = 4; // 4 blocks of 4 bits = 16-bit proof
const HONEST = [1, 0, 1, 1]; // the true logical bit of each block
let bits = []; // current proof bits (flattened)
const idx = (b, j) => b * PER + j;
function loadHonest() {
bits = [];
for (let b = 0; b < BLOCKS; b++) for (let j = 0; j < PER; j++) bits.push(HONEST[b]);
}
function render(read, caught) {
const root = document.getElementById('proof'); root.innerHTML = '';
for (let b = 0; b < BLOCKS; b++) {
const blk = document.createElement('div'); blk.className = 'blk';
for (let j = 0; j < PER; j++) {
const i = idx(b, j);
const d = document.createElement('div');
let cls = 'bit';
if (bits[i] !== HONEST[b]) cls += ' flipped';
if (read && read.includes(i)) cls += caught ? ' caught' : ' read';
d.className = cls; d.textContent = bits[i];
d.title = fmt(`block %b%%v%, position %j%%v%`, { b: b, v: 1, j: j });
d.addEventListener('click', () => { bits[i] = bits[i] ? 0 : 1; setStatus(`Bit flipped — proof tampered. Now run the verifier.`, ''); render(); note(''); });
blk.appendChild(d);
}
root.appendChild(blk);
}
}
function setStatus(t, cls){ const s=document.getElementById('status'); s.textContent=t; s.className='status '+cls; }
function note(t){ document.getElementById('note').textContent = t; }
const isHonest = () => bits.every((v, i) => v === HONEST[(i / PER) | 0]);
// One verifier round: random block, two distinct random positions, compare.
function oneRound() {
const b = Math.floor(Math.random() * BLOCKS);
let p = Math.floor(Math.random() * PER), q = Math.floor(Math.random() * PER);
while (q === p) q = Math.floor(Math.random() * PER);
const i = idx(b, p), k = idx(b, q);
const accept = bits[i] === bits[k];
return { read: [i, k], accept };
}
function round() {
const r = oneRound();
render(r.read, !r.accept);
if (r.accept) setStatus(`✓ The 2 bits agreed — verifier ACCEPTS this round.`, 'ok');
else setStatus(`✗ The 2 bits disagreed — verifier REJECTS. Cheat caught!`, 'bad');
note(isHonest() ? `Proof is honest, so every round will accept.`
: `Proof is tampered: each round has a real chance of catching it.`);
}
function many() {
let rejects = 0, lastReject = null;
for (let t = 0; t < 20; t++) { const r = oneRound(); if (!r.accept) { rejects++; lastReject = r; } }
if (rejects > 0) {
render(lastReject.read, true);
setStatus(fmt(`✗ Caught in %rejects% of 20 rounds — verifier REJECTS.`, { rejects: rejects }), 'bad');
note(`Even reading only 2 bits per round, a cheat is exposed with overwhelming probability over many rounds.`);
} else {
render(null, false);
if (isHonest()) { setStatus(`✓ Accepted all 20 rounds — honest proof always passes.`, 'ok'); note(`An honest proof never fails a check.`); }
else { setStatus(`✓ Slipped past all 20 rounds this time — run again.`, 'ok'); note(`A tiny cheat can occasionally hide; more rounds drive detection toward certainty.`); }
}
}
document.getElementById('round').addEventListener('click', round);
document.getElementById('many').addEventListener('click', many);
document.getElementById('reset').addEventListener('click', () => { loadHonest(); render(); setStatus(`Honest proof loaded. Pick an action.`, ''); note(''); });
loadHonest(); render();
<p class="hint">This is an <b>encoded proof</b>: each honest claim is repeated across a whole block of bits, so the bits inside a block must all agree. The verifier reads only <b>2 random bits from one block</b> and rejects if they disagree. Honest proofs always pass; flip a few bits to cheat, then run the verifier and watch it catch you.</p>
<div id="proof" class="proof"></div>
<div class="status" id="status">Honest proof loaded. Pick an action.</div>
<div class="btns">
<button id="round" type="button">Run 1 check (read 2 bits)</button>
<button id="many" type="button">Run 20 checks</button>
<button id="reset" type="button" class="ghost">Reset to honest proof</button>
</div>
<p class="note" id="note"></p>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.proof { display: flex; flex-wrap: wrap; gap: 4px; margin: .4rem 0; }
.blk { display: flex; gap: 3px; padding: 4px; border: 1px dashed #cdd9e3; border-radius: 8px; }
.bit { width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
font: 700 15px ui-monospace, monospace; border-radius: 6px; cursor: pointer;
background: #e8eef3; color: #1d3557; border: 1px solid #cdd9e3; user-select: none; transition: all .1s; }
.bit:hover { background: #d6e0ea; }
.bit.flipped { background: #ffd9a8; border-color: #e08a1e; }
.bit.read { outline: 3px solid #1d3557; outline-offset: 1px; }
.bit.caught { background: #e63946; border-color: #c92f3c; color: #fff; }
.status { font-size: 1rem; font-weight: 600; margin: .6rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
.note { font-size: .82rem; color: #555; margin: .6rem 0 0; min-height: 1.2em; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// A PCP-style toy. The "encoded proof" is split into BLOCKS; in an honest proof
// every bit inside a block is identical (a repetition encoding of one logical bit).
// The verifier’s LOCAL RULE: pick one random block, read 2 random distinct bits from
// it, accept iff they are equal. It never reads the whole proof.
// - Honest proof: every pair inside every block agrees -> always accepts.
/* - Tampered proof: any cheated block has disagreeing pairs, so each round has a
real chance of catching it. Repeat rounds -> detection probability -> 1. */
const BLOCKS = 4, PER = 4; // 4 blocks of 4 bits = 16-bit proof
const HONEST = [1, 0, 1, 1]; // the true logical bit of each block
let bits = []; // current proof bits (flattened)
const idx = (b, j) => b * PER + j;
function loadHonest() {
bits = [];
for (let b = 0; b < BLOCKS; b++) for (let j = 0; j < PER; j++) bits.push(HONEST[b]);
}
function render(read, caught) {
const root = document.getElementById('proof'); root.innerHTML = '';
for (let b = 0; b < BLOCKS; b++) {
const blk = document.createElement('div'); blk.className = 'blk';
for (let j = 0; j < PER; j++) {
const i = idx(b, j);
const d = document.createElement('div');
let cls = 'bit';
if (bits[i] !== HONEST[b]) cls += ' flipped';
if (read && read.includes(i)) cls += caught ? ' caught' : ' read';
d.className = cls; d.textContent = bits[i];
d.title = fmt(`block %b%%v%, position %j%%v%`, { b: b, v: 1, j: j });
d.addEventListener('click', () => { bits[i] = bits[i] ? 0 : 1; setStatus(`Bit flipped — proof tampered. Now run the verifier.`, ''); render(); note(''); });
blk.appendChild(d);
}
root.appendChild(blk);
}
}
function setStatus(t, cls){ const s=document.getElementById('status'); s.textContent=t; s.className='status '+cls; }
function note(t){ document.getElementById('note').textContent = t; }
const isHonest = () => bits.every((v, i) => v === HONEST[(i / PER) | 0]);
// One verifier round: random block, two distinct random positions, compare.
function oneRound() {
const b = Math.floor(Math.random() * BLOCKS);
let p = Math.floor(Math.random() * PER), q = Math.floor(Math.random() * PER);
while (q === p) q = Math.floor(Math.random() * PER);
const i = idx(b, p), k = idx(b, q);
const accept = bits[i] === bits[k];
return { read: [i, k], accept };
}
function round() {
const r = oneRound();
render(r.read, !r.accept);
if (r.accept) setStatus(`✓ The 2 bits agreed — verifier ACCEPTS this round.`, 'ok');
else setStatus(`✗ The 2 bits disagreed — verifier REJECTS. Cheat caught!`, 'bad');
note(isHonest() ? `Proof is honest, so every round will accept.`
: `Proof is tampered: each round has a real chance of catching it.`);
}
function many() {
let rejects = 0, lastReject = null;
for (let t = 0; t < 20; t++) { const r = oneRound(); if (!r.accept) { rejects++; lastReject = r; } }
if (rejects > 0) {
render(lastReject.read, true);
setStatus(fmt(`✗ Caught in %rejects% of 20 rounds — verifier REJECTS.`, { rejects: rejects }), 'bad');
note(`Even reading only 2 bits per round, a cheat is exposed with overwhelming probability over many rounds.`);
} else {
render(null, false);
if (isHonest()) { setStatus(`✓ Accepted all 20 rounds — honest proof always passes.`, 'ok'); note(`An honest proof never fails a check.`); }
else { setStatus(`✓ Slipped past all 20 rounds this time — run again.`, 'ok'); note(`A tiny cheat can occasionally hide; more rounds drive detection toward certainty.`); }
}
}
document.getElementById('round').addEventListener('click', round);
document.getElementById('many').addEventListener('click', many);
document.getElementById('reset').addEventListener('click', () => { loadHonest(); render(); setStatus(`Honest proof loaded. Pick an action.`, ''); note(''); });
loadHonest(); render();
Try it honestly first: an untampered proof passes every random check. Now flip a few bits to "cheat" and run the verifier again and again. Even reading only 2 bits per round, the probability of slipping past climbs down fast: each round has a real chance of landing on a broken rule, so after a handful of rounds a cheat is exposed with overwhelming probability — without ever reading the full proof.
Comments
Loading comments...