diff --git a/site/api/faucet.mjs b/site/api/faucet.mjs index a92243a8e..7ee001437 100644 --- a/site/api/faucet.mjs +++ b/site/api/faucet.mjs @@ -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' }); } diff --git a/site/lc/app.js b/site/lc/app.js index 10f66b247..1f4b7cdc3 100644 --- a/site/lc/app.js +++ b/site/lc/app.js @@ -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 - ? `
${res.payment ? 'Payment receipt · included, executed, proven and finalised' : 'Transaction inclusion receipt · included and finalised'} · devnet, no value
${esc(formatIgn(t.value || '0'))} IGN
- ${res.payment ? `

Outcome authenticated: ${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))}.

` : `

This is the inclusion receipt. ${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.' : ''}

`} + ? `
Transaction inclusion receipt · included and finalised · devnet, no value
${esc(formatIgn(t.value || '0'))} IGN
To
${esc(t.to || 'contract creation')}
From
${esc(receipt.tx_as_reported.from)} (as the node reports it; the signature is the chain's check)
Transaction
0x${esc(receipt.tx_hash)}
Block
${esc(res.block)} at ${esc(when)}, DAA ${esc(res.block_daa)}
Finality
checkpoint ${res.checkpoint}, ${esc(res.certificate)}; ${res.headers} headers from the block to the checkpoint, verified here in ${res.ms} ms
- ${receipt.execution ? `
Executed
status ${receipt.execution.status === '0x1' ? 'success' : 'failed'}, gas ${parseInt(receipt.execution.gas_used, 16)}, ${receipt.execution.logs} log(s) ${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)'}
` : ''}
` + ${receipt.execution ? `
Executed
status ${receipt.execution.status === '0x1' ? 'success' : 'failed'}, gas ${parseInt(receipt.execution.gas_used, 16)}, ${receipt.execution.logs} log(s) (as the node reports it: executed is reported, not authenticated by this receipt; a payment receipt would authenticate the outcome)
` : ''}
` : `
Not verified · devnet, no value

${esc(res.reason)}

`; - 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 ? `

+ out.innerHTML = top + negativeHtml('a copy of this receipt with one nibble of the raw transaction altered', neg) + stepsHtml(res) + + (res.verified ? `

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.

` : ''); 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; diff --git a/site/lc/test.html b/site/lc/test.html index 496cdb4d1..29c0032c1 100644 --- a/site/lc/test.html +++ b/site/lc/test.html @@ -8,8 +8,8 @@ -

Igneum reference apps: the negative cases, in this tab (Devnet 3, no value)

-

Each row runs site/lc/core.js on live Devnet 3 data from . A tampered case must read refused; the genuine case must read verified. The same cases run under Node in tools/reference-apps/light-service/verify.test.mjs. Parameters: ?api= (the read service), ?address= (the balance to prove; default: the miner of the latest block), ?tx= (a transaction; default: one in a recent block).

+

Igneum reference apps: the negative cases, in this tab (devnet, no value)

+

Each row runs site/lc/core.js on live devnet data from . A tampered case must read refused; the genuine case must read verified. The same cases run under Node in tools/reference-apps/light-service/verify.test.mjs. Parameters: ?api= (the read service), ?address= (the balance to prove; default: the miner of the latest block), ?tx= (a transaction; default: one in a recent block).

Receipt

caseresultmsreason

Balance

diff --git a/site/lc/verify-receipt.js b/site/lc/verify-receipt.js index 5c92158fd..6eb21755b 100644 --- a/site/lc/verify-receipt.js +++ b/site/lc/verify-receipt.js @@ -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) { diff --git a/site/oracle.html b/site/oracle.html index 790010c9a..65f41565d 100644 --- a/site/oracle.html +++ b/site/oracle.html @@ -257,7 +257,7 @@

On Sepolia

-
Oracle
0x3ad71d4660d8a8d2f92248b9c286392dd76a3ab6 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.
+
Oracle
0x3ad71d4660d8a8d2f92248b9c286392dd76a3ab6 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.
Verifier
0xAf74f3F512081291D663Bb1d6b6d37E99e37D744 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
Chain
Sepolia, chain id 11155111; the Igneum 2.0 devnet (igneum-devnet-4), no value
Proven roots
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.
diff --git a/tools/reference-apps/receipt/verify-receipt.src.mjs b/tools/reference-apps/receipt/verify-receipt.src.mjs index 68d77e2e2..c79a1232a 100644 --- a/tools/reference-apps/receipt/verify-receipt.src.mjs +++ b/tools/reference-apps/receipt/verify-receipt.src.mjs @@ -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.'); diff --git a/tools/site-deploy-from-mirror.sh b/tools/site-deploy-from-mirror.sh index 69cc700a6..9f57ff122 100755 --- a/tools/site-deploy-from-mirror.sh +++ b/tools/site-deploy-from-mirror.sh @@ -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|" "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