Merge commit 'fb6f84e3e' into site-2.0-wipe

# Conflicts:
#	site/lc/app.js
#	tools/reference-apps/receipt/verify-receipt.src.mjs
This commit is contained in:
igneum-labs 2026-10-08 15:43:09 +00:00
commit 22bb0ff70e
7 changed files with 23 additions and 31 deletions

View file

@ -59,7 +59,7 @@ export function createHandler({ env = process.env, sql, rpc } = {}) {
const key = env.FAUCET_KEY, rpcUrl = env.FAUCET_RPC;
const chainId = Number(env.FAUCET_CHAIN_ID || 4463);
if (!key || !/^0x[0-9a-fA-F]{64}$/.test(key) || !rpcUrl) {
return res.status(503).json({ ok: false, error: 'The faucet is not open yet. It opens with the public testnet.' });
return res.status(503).json({ ok: false, error: 'The faucet is not open yet.' });
}
let body;
try { body = await readBody(req); } catch { return res.status(400).json({ ok: false, error: 'bad json' }); }

View file

@ -6,7 +6,7 @@
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, verifyPaymentReceipt, formatIgn } from './core.js';
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 };
@ -76,28 +76,24 @@ export async function runReceipt(tx) {
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 = verifyPaymentReceipt(receipt, deps);
if (res.payment) { receipt.kind = 'payment receipt'; receipt.authenticates = 'inclusion of the signed transaction in a finalised block and its successful execution outcome (status and logs) through the proven segment\'s receipts commitment'; }
let negPay = null;
if (res.payment) { const b2 = clone(receipt); const r0 = b2.segment.receipts[Number(b2.segment.receipt_position)]; r0.status = '0x0'; negPay = verifyPaymentReceipt(b2, 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
? `<div class="headline"><div class="eyebrow ember">${res.payment ? 'Payment receipt · included, executed, proven and finalised' : 'Transaction inclusion receipt · included and finalised'} · devnet, no value</div><div class="big">${esc(formatIgn(t.value || '0'))} <span>IGN</span></div>
${res.payment ? `<p class="pt"><b>Outcome authenticated:</b> ${esc(res.outcome.asset)}, ${esc(formatIgn(res.outcome.amount_wei))} IGN to ${esc(res.outcome.recipient || 'contract creation')}, executed with status success, through the receipts commitment of the proven segment ending at chain block ${esc(String(receipt.execution.chain_block))}.</p>` : `<p class="pt"><b>This is the inclusion receipt.</b> ${esc(res.payment_unavailable || receipt.payment_unavailable || 'the outcome is not authenticated')}${res.receipt_status === 'failed' ? ' The authenticated status is FAILED: no transfer took place.' : ''}</p>`}
? `<div class="headline"><div class="eyebrow ember">Transaction inclusion receipt · included and finalised · devnet, no value</div><div class="big">${esc(formatIgn(t.value || '0'))} <span>IGN</span></div>
<div class="kv"><div class="k">To</div><div class="mono">${esc(t.to || 'contract creation')}</div><div class="k">From</div><div class="mono">${esc(receipt.tx_as_reported.from)} <small>(as the node reports it; the signature is the chain's check)</small></div>
<div class="k">Transaction</div><div class="mono">0x${esc(receipt.tx_hash)}</div><div class="k">Block</div><div class="mono">${esc(res.block)} <small>at ${esc(when)}, DAA ${esc(res.block_daa)}</small></div>
<div class="k">Finality</div><div>checkpoint ${res.checkpoint}, ${esc(res.certificate)}; ${res.headers} headers from the block to the checkpoint, verified here in ${res.ms} ms</div>
${receipt.execution ? `<div class="k">Executed</div><div>status ${receipt.execution.status === '0x1' ? 'success' : 'failed'}, gas ${parseInt(receipt.execution.gas_used, 16)}, ${receipt.execution.logs} log(s) <small>${res.payment ? '(authenticated: the receipt sits in the shard receipts trie whose root the proven segment statement commits to)' : '(as the node reports it: executed is reported, not authenticated by this receipt; a payment receipt would authenticate the outcome)'}</small></div>` : ''}</div></div>`
${receipt.execution ? `<div class="k">Executed</div><div>status ${receipt.execution.status === '0x1' ? 'success' : 'failed'}, gas ${parseInt(receipt.execution.gas_used, 16)}, ${receipt.execution.logs} log(s) <small>(as the node reports it: executed is reported, not authenticated by this receipt; a payment receipt would authenticate the outcome)</small></div>` : ''}</div></div>`
: `<div class="headline bad"><div class="eyebrow">Not verified · devnet, no value</div><p class="pt">${esc(res.reason)}</p></div>`;
out.innerHTML = top + negativeHtml('a copy of this receipt with one nibble of the raw transaction altered', neg) + (negPay ? negativeHtml('a copy of this payment receipt with the receipt status flipped to failed', { verified: negPay.verified && negPay.payment, reason: negPay.reason }) : '') + stepsHtml(res)
+ (res.verified ? `<p><button class="btn" type="button" data-download>Download the ${res.payment ? 'payment' : 'inclusion'} receipt (JSON, ${Math.round(JSON.stringify(receipt).length / 1024)} KB)</button></p>
out.innerHTML = top + negativeHtml('a copy of this receipt with one nibble of the raw transaction altered', neg) + stepsHtml(res)
+ (res.verified ? `<p><button class="btn" type="button" data-download>Download the inclusion receipt (JSON, ${Math.round(JSON.stringify(receipt).length / 1024)} KB)</button></p>
<p class="note">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: <code>node verify-receipt.js receipt.json</code> (<a href="/lc/verify-receipt.js" download>verify-receipt.js</a>, plain JavaScript, no npm, no network). A tampered file fails there the same way the copy above failed here.</p>` : '');
const dl = $('[data-download]');
if (dl) dl.addEventListener('click', () => {
const blob = new Blob([JSON.stringify(receipt, null, 1)], { type: 'application/json' });
const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `igneum-${res.payment ? 'payment' : 'inclusion'}-receipt-${receipt.tx_hash.slice(0, 12)}.json`; a.click(); setTimeout(() => URL.revokeObjectURL(a.href), 5000);
const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `igneum-inclusion-receipt-${receipt.tx_hash.slice(0, 12)}.json`; a.click(); setTimeout(() => URL.revokeObjectURL(a.href), 5000);
});
setStatus(res.verified ? 'verified' : 'refused', res.verified ? 'ok' : 'bad');
return res;

View file

@ -8,8 +8,8 @@
<style>body{font-family:ui-monospace,Menlo,monospace;font-size:13px;background:#0C0C0E;color:#E8E4DA;padding:24px;max-width:1100px}h1{font-size:16px}h2{font-size:14px;margin-top:28px}table{display:block;overflow-x:auto;max-width:100%}code{overflow-wrap:anywhere}td{padding:4px 10px;border-bottom:1px solid #222;vertical-align:top}.ok{color:#7ED957}.bad{color:#F2541B}p{max-width:90ch}</style>
</head>
<body>
<h1>Igneum reference apps: the negative cases, in this tab (Devnet 3, no value)</h1>
<p>Each row runs <code>site/lc/core.js</code> on live Devnet 3 data from <code id="api"></code>. A tampered case must read <b>refused</b>; the genuine case must read <b>verified</b>. The same cases run under Node in <code>tools/reference-apps/light-service/verify.test.mjs</code>. Parameters: <code>?api=</code> (the read service), <code>?address=</code> (the balance to prove; default: the miner of the latest block), <code>?tx=</code> (a transaction; default: one in a recent block).</p>
<h1>Igneum reference apps: the negative cases, in this tab (devnet, no value)</h1>
<p>Each row runs <code>site/lc/core.js</code> on live devnet data from <code id="api"></code>. A tampered case must read <b>refused</b>; the genuine case must read <b>verified</b>. The same cases run under Node in <code>tools/reference-apps/light-service/verify.test.mjs</code>. Parameters: <code>?api=</code> (the read service), <code>?address=</code> (the balance to prove; default: the miner of the latest block), <code>?tx=</code> (a transaction; default: one in a recent block).</p>
<h2>Receipt</h2>
<table id="r"><thead><tr><td>case</td><td>result</td><td>ms</td><td>reason</td></tr></thead><tbody></tbody></table>
<h2>Balance</h2>

View file

@ -2121,8 +2121,8 @@ var ScalarMultiplier = class {
}
/**
* Implements ec multiplication using precomputed signed fixed-window wNAF tables.
* Constant-time: fixed window count with one table addition per window — zero digits feed
* the fake accumulator — and no doublings; the lookup scans the whole window slice.
* Constant-time: fixed window count with one table addition per window, zero digits feed
* the fake accumulator, and no doublings; the lookup scans the whole window slice.
* Scalar bounds are validated by the public entry points ({@link ScalarMultiplier.mulCT},
* {@link ScalarMultiplier.mulCTBlinded}, {@link ScalarMultiplier.mulUnsafe});
* signedWindowDigits throws if `n` exceeds the table.
@ -2209,7 +2209,7 @@ var ScalarMultiplier = class {
* A cached wNAF table only pays off when reused; a flat 2^FW_WINDOW table (`size-1` adds) is
* far cheaper to build for a single use. The point-operation sequence is independent of `n`:
* build the table, then per window exactly FW_WINDOW doublings, a data-oblivious scan over
* every table entry, and one addition (adds the identity when the window digit is 0 — never
* every table entry, and one addition (adds the identity when the window digit is 0, never
* skipped).
*
* `n` must be `< 2^bits`. Assumes complete addition (adding the identity costs the same as any
@ -2217,7 +2217,7 @@ var ScalarMultiplier = class {
* projective form (no normalizeZ): normalizing this small a table costs more than the
* mixed-add savings it would buy for a single multiply.
* @returns real point `p`; `f` duplicates it only to match {@link wnafCachedCT}'s return shape
* (this path needs no fake accumulator — its op-count is already scalar-independent).
* (this path needs no fake accumulator, its op-count is already scalar-independent).
*/
fixedWindowCT(point, n, bits) {
const W = FW_WINDOW;
@ -5313,7 +5313,7 @@ if (args.includes("--tamper")) {
if (t2.verified) process.exit(1);
}
var r = verifyReceipt(receipt, deps);
console.log(`TRANSACTION INCLUSION RECEIPT 0x${receipt.tx_hash} on ${receipt.chain_id} (Devnet 3, no value)`);
console.log(`TRANSACTION INCLUSION RECEIPT 0x${receipt.tx_hash} on ${receipt.chain_id} (devnet, no value)`);
console.log("What this file authenticates: that the signed transaction is included in a block that is finalised. What it does not: the execution outcome (status, gas), which is carried as the node reported it. A payment receipt, which authenticates the transfer outcome, is a different file.");
print(r);
if (!r.verified) {

View file

@ -257,7 +257,7 @@
<section class="card" aria-labelledby="deployed">
<h2 id="deployed">On Sepolia</h2>
<div class="kv" data-oracle-kv>
<div class="k">Oracle</div><div class="mono" data-oracle-address>0x3ad71d4660d8a8d2f92248b9c286392dd76a3ab6 <small>IgneumStateOracle, deployed 8 October 2026 on the shared verifier (tx 0x16312cf8…4e2a, block 11870855); its trust() names the two unchecked items below; accepts statements with chain id 4463 or 4464. The earlier oracle 0xefe9879d…a1b2 (stand-in verifier at first) stays as deployed.</small></div>
<div class="k">Oracle</div><div class="mono" data-oracle-address>0x3ad71d4660d8a8d2f92248b9c286392dd76a3ab6 <small>IgneumStateOracle, deployed 8 October 2026 on the shared verifier (tx 0x16312cf8…4e2a, block 11870855); its trust() names the two unchecked items below; accepts statements with chain id 4463 or 4464.</small></div>
<div class="k">Verifier</div><div class="mono" data-verifier-address>0xAf74f3F512081291D663Bb1d6b6d37E99e37D744 <small>the shared IgneumCertificateVerifier (the DEX lane's, a real BLS12-381 check on chain through the EIP-2537 precompiles, voter table installed at checkpoint 2127), set by setVerifier on 8 October 2026 (tx 0xd679bb65…db8e); the stand-in 0xa197ef31…a6f6 served until then</small></div>
<div class="k">Chain</div><div>Sepolia, chain id 11155111; the Igneum 2.0 devnet (igneum-devnet-4), no value</div>
<div class="k">Proven roots</div><div class="mono">Certificate 2232 (0xf056c26e…, 29 voters, signed weight 4,988 of 7,164) recorded on the shared verifier with a real BLS check, tx 0x1794b785…1e8a, 792,677 gas. Devnet chain block 28439, post_root 0x6e6d2fc8… stored on this oracle with a 6-header path, tx 0xcdb24dbc…ee5fc, 1,624,984 gas; provenBalance read 721.451608 IGN for 0xcaed79d8…c087 on Sepolia, equal to the devnet node's eth_getProof.</div>

View file

@ -6,7 +6,7 @@ import { readFileSync } from 'node:fs';
import { blake2b } from '@noble/hashes/blake2.js';
import { keccak_256 } from '@noble/hashes/sha3.js';
import { bls12_381 } from '@noble/curves/bls12-381.js';
import { verifyReceipt, verifyPaymentReceipt, formatIgn } from '../../../site/lc/core.js';
import { verifyReceipt, formatIgn } from '../../../site/lc/core.js';
const args = process.argv.slice(2);
const file = args.find(a => !a.startsWith('--'));
@ -24,15 +24,11 @@ if (args.includes('--tamper')) {
console.log(`tampered copy (one nibble of the raw transaction): ${t.verified ? 'NOT REFUSED, this verifier is broken' : 'REFUSED'}${t.reason ? ' :: ' + t.reason : ''}`);
if (t.verified) process.exit(1);
}
const r = receipt.segment ? verifyPaymentReceipt(receipt, deps) : verifyReceipt(receipt, deps);
const payment = !!(r.verified && r.payment);
console.log(`${payment ? 'PAYMENT RECEIPT' : 'TRANSACTION INCLUSION RECEIPT'} 0x${receipt.tx_hash} on ${receipt.chain_id} (Devnet 3, no value)`);
console.log(payment
? 'What this file authenticates: that the signed transaction is included in a finalised block and executed with status success, its receipt sitting in the shard receipts trie whose root the proven segment statement commits to. Trusted: the voter table from the node; the SP1 proof behind the statement is verified by nodes, not here.'
: 'What this file authenticates: that the signed transaction is included in a block that is finalised. What it does not: the execution outcome (status, gas), which is carried as the node reported it.' + (receipt.payment_unavailable ? ' Why no payment receipt: ' + receipt.payment_unavailable : ''));
const r = verifyReceipt(receipt, deps);
console.log(`TRANSACTION INCLUSION RECEIPT 0x${receipt.tx_hash} on ${receipt.chain_id} (devnet, no value)`);
console.log('What this file authenticates: that the signed transaction is included in a block that is finalised. What it does not: the execution outcome (status, gas), which is carried as the node reported it. A payment receipt, which authenticates the transfer outcome, is a different file.');
print(r);
if (!r.verified) { console.log(`REFUSED: ${r.reason}`); process.exit(1); }
const t = r.tx;
if (payment) console.log(`PAYMENT VERIFIED in ${r.ms} ms: ${formatIgn(t.value)} IGN (${t.value} wei of the native coin) to ${t.to || 'contract creation'}, executed with status success (${r.outcome.logs.length} log(s)) at chain block ${receipt.execution.chain_block}, in block ${r.block.slice(0, 16)} at DAA ${r.block_daa}, finalised under checkpoint ${r.checkpoint}; ${r.certificate}.`);
else console.log(`INCLUSION VERIFIED in ${r.ms} ms: a signed transaction of ${formatIgn(t.value)} IGN to ${t.to || 'contract creation'} (${t.value} wei) is included in block ${r.block.slice(0, 16)} at DAA ${r.block_daa} (${new Date(Number(r.block_time)).toISOString()}), finalised under checkpoint ${r.checkpoint}; ${r.headers} headers checked; ${r.certificate}.`);
console.log('Reported by the node, not authenticated by this file: from ' + (receipt.tx_as_reported && receipt.tx_as_reported.from) + (payment ? '' : (receipt.execution ? `, executed with status ${receipt.execution.status === '0x1' ? 'success' : 'failed'}` : '')) + '. The voter table with weights came from the node (spec 10.1). In the four words: ' + (payment ? 'included, executed, proven and finalised are authenticated under the stated trust.' : 'included and finalised are authenticated, executed is reported, proven is not claimed.'));
console.log(`INCLUSION VERIFIED in ${r.ms} ms: a signed transaction of ${formatIgn(t.value)} IGN to ${t.to || 'contract creation'} (${t.value} wei) is included in block ${r.block.slice(0, 16)} at DAA ${r.block_daa} (${new Date(Number(r.block_time)).toISOString()}), finalised under checkpoint ${r.checkpoint}; ${r.headers} headers checked; ${r.certificate}.`);
console.log('Reported by the node, not authenticated by this file: from ' + (receipt.tx_as_reported && receipt.tx_as_reported.from) + (receipt.execution ? `, executed with status ${receipt.execution.status === '0x1' ? 'success' : 'failed'}` : '') + '. The voter table with weights came from the node (spec 10.1). In the four words: included and finalised are authenticated, executed is reported, proven is not claimed.');

View file

@ -18,7 +18,7 @@ LIVE_NETWORK="${LIVE_NETWORK:-igneum-devnet-3}"
INDEX_STRINGS=("At launch the strongest chip in our public model" "git.igneum.network/igneum-network/")
LEGAL_PAGE="${LEGAL_PAGE:-/litepaper}"; LEGAL_STRING="Not legal advice" # the legal line lives on the litepaper in master's tree (22:16 UK: no commit put it on the index)
MINERS_MIN_ROWS="${MINERS_MIN_ROWS:-20}" # the current-class table alone (the datacentre rows sit in their own table since 8 Oct 2026)
SERVED_PAGES=(swap "faucet|Devnet 3 faucet" "faucet|chain id 4464 since its class v5 floor" "build|Built to prove every block" "build|since its class v5 floor" "grants|Not legal advice." proving tx/0x0 "light|<title>" "receipt|<title>" "oracle|<title>" "lc/test|<title>" "explorer|Source since 12:25 UTC" lc/app.js lc/core.js lc/verify-receipt.js "economics|Not legal advice." "income|The calculator" "income|/income-calc.js" "income|id=\"income-data\"" income-calc.js) # clean URLs every deploy must answer 200 (8 Oct 2026: the DEX lane's /swap, the builder lane's /faucet; /light /receipt /oracle /build /grants join as they land)
SERVED_PAGES=(swap "faucet|Igneum 2.0 devnet faucet" "build|Built to prove every block" "build|igneum-devnet-4" "grants|Not legal advice." proving tx/0x0 "light|<title>" "receipt|<title>" "oracle|<title>" "lc/test|<title>" "explorer|Source since 12:25 UTC" lc/app.js lc/core.js lc/verify-receipt.js "economics|Not legal advice." "income|The calculator" "income|/income-calc.js" "income|id=\"income-data\"" income-calc.js) # clean URLs every deploy must answer 200 (8 Oct 2026: the DEX lane's /swap, the builder lane's /faucet; /light /receipt /oracle /build /grants join as they land)
SITE="${SITE_URL:-https://igneum.network}"
# the /miners row count: the rows of the current-class table (table#bench-current, the regrouped bench of 8 Oct 2026; the datacentre
# and earlier tables sit under their own ids); known-failed: an empty table reads 0