Reference apps: a receipt proves against the earliest certified checkpoint above its block (the smallest proof, 10 headers and 143 KB instead of 289 headers and 2 MB), the certificate used travels in the receipt

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-08 10:31:00 +00:00
parent e1a4a16aa1
commit 24341d3ae1
4 changed files with 30 additions and 7 deletions

View file

@ -73,6 +73,23 @@ function shape(row, source) {
};
}
/** The earliest certified Devnet 3 checkpoint whose chain block number is at least `number` (the reference apps' receipt:
* the smallest proof that a block is final), from the dn3_ rows; null when no certificate reaches that block yet.
* `chainNumberOf(hash)` resolves a checkpoint hash to its chain block number (the exec RPC); rows are tried newest first
* until one falls below `number`, so the cost is a few lookups. */
export async function earliestCheckpointAbove(sql, number, chainNumberOf, table = 'dn3_live_certificates') {
const rows = await sql(`SELECT index, hash FROM ${table} ORDER BY index DESC LIMIT 400`).catch(() => []);
let pick = null;
for (const r of rows) {
const n = await chainNumberOf(r.hash).catch(() => null);
if (n === null) continue;
if (n >= number) pick = r; else break;
}
if (!pick) return null;
const full = await sql(`SELECT * FROM ${table} WHERE index = $1 LIMIT 1`, [Number(pick.index)]);
return full[0] ? shape(full[0], 'dn3') : null;
}
/** The latest certified checkpoint of `want` ('live', 'test' or 'dn3') through `sql`, shaped for the verifier, or null. */
export async function readCheckpoint(sql, want = 'live') {
let row = null, source = null;

View file

@ -67,7 +67,8 @@ export async function runReceipt(tx) {
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}`);
const receipt = { format: 'igneum-receipt-v1', issued: new Date().toISOString(), ...r, checkpoint: { ...r.checkpoint, certificate: cp } };
// 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));

View file

@ -42,7 +42,7 @@ try {
let tx = q.get('tx');
if (!tx) { const cb = await rpc('eth_getBlockByHash', ['0x' + cp.hash, false]); const n = parseInt(cb.number, 16); for (let k = n - 1; k > n - 200 && !tx; k--) { const b = await rpc('eth_getBlockByNumber', ['0x' + k.toString(16), false]); if (b && b.transactions.length) tx = b.transactions[0]; } }
const rr = await get(`${API}/receipt?tx=${tx}&checkpoint=${cp.hash}&index=${cp.index}`);
const receipt = { format: 'igneum-receipt-v1', ...rr, checkpoint: { ...rr.checkpoint, certificate: cp } };
const receipt = { format: 'igneum-receipt-v1', ...rr, checkpoint: { ...rr.checkpoint, certificate: rr.checkpoint.certificate || cp } };
const R = [
['raw transaction altered by one nibble', d => { d.raw_tx_hex = flipHex(d.raw_tx_hex, 40); }],
['transaction hash altered', d => { d.tx_hash = flipHex(d.tx_hash, 10); }],

View file

@ -19,7 +19,7 @@ import { createServer } from 'node:http';
import { blake2b } from '@noble/hashes/blake2.js';
import { keccak_256 } from '@noble/hashes/sha3.js';
import { coinbaseTxHash, merkleRoot, merklePath, hexToBytes, bytesToHex, segmentRecordsOf } from '../../../site/lc/core.js';
import { readCheckpoint, neon } from '../../../site/api/checkpoint.mjs';
import { readCheckpoint, earliestCheckpointAbove, neon } from '../../../site/api/checkpoint.mjs';
const PORT = Number(process.env.LIGHT_PORT || 26890);
const EXEC = process.env.EXEC_RPC || 'http://127.0.0.1:26881';
@ -207,13 +207,18 @@ void merklePath;
async function receipt(q) {
const tx = q.get('tx') || '';
if (!/^0x[0-9a-fA-F]{64}$/.test(tx)) throw httpError(400, 'tx: 0x and 64 hex digits');
const cpHash = q.get('checkpoint'), cpIndex = Number(q.get('index'));
if (!cpHash || !(cpIndex >= 0)) throw httpError(400, 'checkpoint and index are required (from /api/checkpoint?source=dn3)');
let cpHash = q.get('checkpoint'), cpIndex = Number(q.get('index'));
if (!cpHash || !(cpIndex >= 0)) throw httpError(400, 'checkpoint and index are required (from /checkpoint)');
const t = await exec('eth_getTransactionByHash', [tx]);
if (!t) throw httpError(404, 'the node has not executed a transaction with that hash (unknown, or not yet in a block)');
const rcpt = await exec('eth_getTransactionReceipt', [tx]);
const cpNumber = await chainNumberOf(cpHash);
const chainNumber = Number(t.blockNumber);
// the smallest proof: the earliest certified checkpoint at or above the block (from the Devnet 3 observer's rows), else
// the checkpoint the caller named; the certificate used is returned so the page verifies against that one
let certificate = null;
if (process.env.DATABASE_URL) certificate = await earliestCheckpointAbove(neon(), chainNumber, chainNumberOf).catch(() => null);
if (certificate) { cpHash = certificate.hash; cpIndex = Number(certificate.index); }
const cpNumber = await chainNumberOf(cpHash);
if (chainNumber > cpNumber) throw httpError(409, `not final yet: executed at chain block ${chainNumber}, the latest certified checkpoint is chain block ${cpNumber}; try again in about ${chainNumber - cpNumber + 30} s`);
const including = strip(t.igneum.includingBlock);
const b = await body(including);
@ -226,7 +231,7 @@ async function receipt(q) {
const headers = await headerPath(including, chainNumber, cpNumber, cpHash);
return {
ok: true, now: new Date().toISOString(), chain_id: 'igneum-devnet-3', tx_hash: strip(tx), raw_tx_hex: strip(raws[idx]),
checkpoint: { hash: strip(cpHash), index: cpIndex, chain_block: cpNumber },
checkpoint: { hash: strip(cpHash), index: cpIndex, chain_block: cpNumber, certificate: certificate || undefined },
including_block: { header: headers[0], leaf_index: idx + 1, leaf_count: leaves.length, merkle_siblings: siblings(leaves, idx + 1) },
headers,
execution: rcpt ? { chain_block: chainNumber, chain_block_hash: strip(rcpt.blockHash), status: rcpt.status, gas_used: rcpt.gasUsed, from: rcpt.from, to: rcpt.to, contract_address: rcpt.contractAddress, logs: (rcpt.logs || []).length, effective_gas_price: rcpt.effectiveGasPrice, as_reported_by: 'the node (not proven here)' } : null,