51 lines
5.6 KiB
JavaScript
51 lines
5.6 KiB
JavaScript
// Renders tools/reference-apps/compat/results.json into docs/build/compatibility.md, the source of /compatibility (the
|
|
// builder programme's renderer turns docs/build/*.md into site pages). Run after run.mjs; both outputs are committed.
|
|
// node render.mjs [results.json]
|
|
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
const here = path.dirname(new URL(import.meta.url).pathname);
|
|
const src = process.argv[2] || path.join(here, 'results.json');
|
|
const out = path.join(here, '..', '..', '..', 'docs', 'build', 'compatibility.md');
|
|
const res = existsSync(src) ? JSON.parse(readFileSync(src, 'utf8')) : { meta: {}, summary: { passed: 0, failed: 0, untested: 0 }, rows: [] };
|
|
const esc = s => String(s).replace(/\|/g, '\\|');
|
|
const short = h => (typeof h === 'string' && /^0x[0-9a-f]{64}$/i.test(h) ? h.slice(0, 10) + '…' + h.slice(-4) : h);
|
|
const evidence = r => Object.entries(r.evidence || {}).filter(([, v]) => ['string', 'number', 'boolean'].includes(typeof v)).map(([k, v]) => `${k} ${short(v)}`).join(', ');
|
|
const ITEMS = ['Representative contracts', 'Transaction binding and replay', 'Wallet fee estimation', 'Indexing', 'Failed transactions', 'Receipts', 'Application assumptions', 'Differences: block context', 'Differences: randomness', 'Differences: two-dimensional fees'];
|
|
const PLANNED = {
|
|
'Representative contracts': ['an ERC-20 deploys (CREATE address, code at the address)', 'ERC-20 transfer: balances, the Transfer log, eth_call', 'ERC-20 approve and transferFrom', 'a probe contract deploys', 'a counter increments and persists'],
|
|
'Wallet fee estimation': ['eth_gasPrice, eth_maxPriorityFeePerGas, eth_feeHistory shapes', 'a transfer: estimateGas against gasUsed', 'a contract call: estimateGas against gasUsed', 'the documented difference: a transfer sent with a local 21,000 limit'],
|
|
'Indexing': ['eth_getLogs by address, topic and range against the receipts', 'block, receipts and transaction lookups agree on indices'],
|
|
'Failed transactions': ['a revert: status 0, gas charged, nonce advanced, eth_call reason', 'an out-of-gas call'],
|
|
'Receipts': ['every Ethereum field, logsBloom from the logs, contractAddress on creation', 'the receipts trie root rebuilt equal to the header'],
|
|
'Application assumptions': ['block.number, timestamp, chainid, basefee, blockhash(n-1), msg.sender, tx.origin', 'CREATE2 address'],
|
|
'Differences: block context': ['block.coinbase is the including DAG block\'s miner'],
|
|
'Differences: randomness': ['block.prevrandao derives from the epoch seed, fixed per epoch'],
|
|
'Differences: two-dimensional fees': ['the receipt\'s proving dimension and the fee split'],
|
|
};
|
|
const when = res.meta.finished ? new Date(res.meta.finished).toISOString().replace('T', ' ').slice(0, 16) + ' UTC' : 'not run yet';
|
|
let md = `# Compatibility, measured
|
|
|
|
A GPU-secured network for Ethereum-compatible applications and verifiable computation. Compatibility is a product deliverable here, not a sentence: every row below is a test that ran against the devnet from the repository (\`tools/reference-apps/compat/run.mjs\`), with its evidence, and a row that could not run says so and why. Devnet, no value.
|
|
|
|
Last run: ${when}${res.meta.network ? ` on ${res.meta.network} (chain id ${res.meta.chain_id})` : ''}${res.meta.run_id ? `, run id ${res.meta.run_id}` : ''}. ${res.summary.passed} passed, ${res.summary.failed} failed, ${res.summary.untested} untested. Sender ${res.meta.sender ? res.meta.sender.slice(0, 10) + '…' : 'none'}${res.meta.solc ? `, contracts compiled with solc ${res.meta.solc}` : ''}.
|
|
|
|
Three boundaries hold for everything on this page: proven execution is not finality; EVM compatibility is not Ethereum security; ZK is not privacy. The four words included, executed, proven and finalised are defined on [/receipt](/receipt).
|
|
|
|
`;
|
|
for (const item of ITEMS) {
|
|
const rows = res.rows.filter(r => r.item === item);
|
|
const rc = rows[0] && rows[0].register_case; md += `## ${item}${rc ? ` (register case ${rc})` : ''}\n\n| Row | Verdict | What was measured | Evidence |\n|---|---|---|---|\n`;
|
|
if (rows.length) for (const r of rows) md += `| ${esc(r.name)} | **${r.verdict}** | ${esc(r.detail || '')} | ${esc(evidence(r))} |\n`;
|
|
else for (const name of PLANNED[item] || []) md += `| ${esc(name)} | **untested** | not run yet: the devnet was switched off when this page was built; the row runs on the 2.0 devnet | |\n`;
|
|
md += '\n';
|
|
}
|
|
md += `## What the differences mean for an application
|
|
|
|
- Block context: \`block.coinbase\` is the miner of the DAG block that first included the transaction, so one chain block can carry transactions with different coinbases; code that pays or trusts \`block.coinbase\` as "the block producer" sees several producers per block.
|
|
- Randomness: \`block.prevrandao\` derives from the chain's epoch seed and holds for an epoch, so it is not fresh per block and must not seed a lottery or a commitment; use an application-level randomness source.
|
|
- Two-dimensional fees: every transaction pays execution gas and proving gas; the node folds the second into the gas it quotes, so a wallet that quotes from the node (eth_estimateGas, eth_gasPrice, eth_feeHistory) is covered and a wallet with Ethereum's constants (21,000 for a transfer) is not; the receipt carries the split under \`igneum\` (pgasUsed, provingBaseFeePerGas, burnedProvingFee, burnedExecutionBaseFee, minerTip).
|
|
|
|
Source: \`tools/reference-apps/compat/run.mjs\` (the rows), \`tools/reference-apps/compat/results.json\` (the evidence), \`docs/build/compatibility.md\` (this page, rendered by \`render.mjs\`).
|
|
`;
|
|
writeFileSync(out, md);
|
|
console.log('wrote', out, res.rows.length, 'rows');
|