// Igneum reference apps, the browser driver for /light and /receipt (8 October 2026). Fetches data from the read // service behind rpc.devnet.igneum.network/light and verifies every byte in this tab with core.js. Libraries, pinned: // BLAKE2b and keccak from @noble/hashes 2.4.0, BLS12-381 from @noble/curves 2.4.0 (pure JavaScript, served by jsdelivr // as ES modules). Known-failed first: before the genuine result is shown, a copy of the same proof with one byte // altered is run through the same verifier and must read as refused. import { blake2b } from 'https://cdn.jsdelivr.net/npm/@noble/hashes@2.4.0/blake2.js/+esm'; import { keccak_256 } from 'https://cdn.jsdelivr.net/npm/@noble/hashes@2.4.0/sha3.js/+esm'; import { bls12_381 } from 'https://cdn.jsdelivr.net/npm/@noble/curves@2.4.0/bls12-381.js/+esm'; import { verifyBalance, verifyReceipt, formatIgn } from './core.js'; export const LIBRARIES = { '@noble/hashes': '2.4.0', '@noble/curves': '2.4.0' }; const deps = { blake2b, bls: bls12_381, keccak: keccak_256 }; const q = new URLSearchParams(location.search); export const API = (q.get('api') || 'https://rpc.devnet.igneum.network/light').replace(/\/$/, ''); const $ = s => document.querySelector(s); const esc = s => String(s).replace(/&/g, '&').replace(//g, '>'); const short = h => { const s = String(h).replace(/^0x/, ''); return s.slice(0, 8) + '…' + s.slice(-6); }; const clone = x => JSON.parse(JSON.stringify(x)); const flipHex = (s, at) => { const h = s.replace(/^0x/, ''); const i = Math.min(at, h.length - 1); const d = (parseInt(h[i], 16) ^ 1).toString(16); return (s.startsWith('0x') ? '0x' : '') + h.slice(0, i) + d + h.slice(i + 1); }; async function getJson(url) { const r = await fetch(url, { cache: 'no-store' }); let j = null; try { j = await r.json(); } catch { j = null; } if (!j) throw new Error(`${url.replace(API, '')} answered ${r.status} with no JSON`); if (!j.ok) throw new Error(j.error || `${url.replace(API, '')} answered ${r.status}`); return j; } function setStatus(text, kind = '') { const el = $('[data-status]'); if (!el) return; el.textContent = text; el.dataset.kind = kind; } function stepsHtml(result) { return '
${esc(res.balance_wei)} wei at chain block ${res.block}, under checkpoint ${res.checkpoint} (locked ${esc(ago(Number(proof.headers[proof.headers.length - 1].timestamp)))}). Verified here in ${res.ms} ms, ${res.headers} headers checked.
${esc(res.reason)}
Data served by ${esc(API)} (a read service in front of a Devnet 3 node). Nothing it answered was taken on trust: the certificate, every header hash and parent link, the coinbase inclusion, the segment record's signature and the account proof were recomputed in this tab. What is trusted: that the aggregator's statement is the true execution result (every node checks it natively before paying the record; the SP1 proof behind it is verified by nodes, not in this tab yet), and the voter list with weights, which came from the node (spec 10.1).
`; setStatus(res.verified ? 'verified' : 'refused', res.verified ? 'ok' : 'bad'); return res; } // ---- /receipt ----------------------------------------------------------------------------------------------------- export async function runReceipt(tx) { const out = $('[data-result]'); out.innerHTML = ''; setStatus('fetching the latest certified checkpoint…', 'busy'); const cp = await getJson(`${API}/checkpoint`); setStatus(`checkpoint ${cp.index} fetched; fetching the inclusion proof…`, 'busy'); const r = await getJson(`${API}/receipt?tx=${tx}&checkpoint=${cp.hash}&index=${cp.index}`); // the service may answer with an earlier certificate (the first checkpoint above the block: the smallest proof); it is verified like any other const receipt = { format: 'igneum-receipt-v1', issued: new Date().toISOString(), ...r, checkpoint: { ...r.checkpoint, certificate: r.checkpoint.certificate || cp } }; delete receipt.ok; delete receipt.now; setStatus('verifying in this tab…', 'busy'); await new Promise(resolve => setTimeout(resolve, 20)); const bad = clone(receipt); bad.raw_tx_hex = flipHex(bad.raw_tx_hex, 40); const neg = verifyReceipt(bad, deps); const res = verifyReceipt(receipt, deps); window.__igneumReceipt = { receipt, neg, res }; const t = res.tx || {}; const when = new Date(Number(res.block_time || 0)).toISOString().replace('T', ' ').slice(0, 19) + ' UTC'; const top = res.verified ? `${esc(res.reason)}
The file carries the raw transaction, the including block's header and merkle path, every header up to the certified checkpoint, the certificate and the voter table. Anyone re-verifies it offline with the one-file verifier: node verify-receipt.js receipt.json (verify-receipt.js, plain JavaScript, no npm, no network). A tampered file fails there the same way the copy above failed here.
${esc(String(err.message || err))}