The numbers profitability sites and pool software read (WhatToMine's form: explorer or pool with an API, the halving schedule, a source for total coins). Reward and supply from the emission rule at the node's DAA score; the halving table (33 rows), the 30-day ramp and the observer's coinbase check. Cached 10 s, CORS open. FIELDS in each handler is the contract; public-stats.test.mjs checks it from a fixture, tools/ci/public-api-check.mjs checks a deployment. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
27 lines
2.3 KiB
JavaScript
27 lines
2.3 KiB
JavaScript
#!/usr/bin/env node
|
|
// Checks that a deployed site answers /api/stats and /api/supply with every documented field (the FIELDS lists in
|
|
// site/api/stats.mjs and site/api/supply.mjs, the same lists docs/api/public-stats.md is written from).
|
|
// node tools/ci/public-api-check.mjs [https://igneum.network]
|
|
// Exit 1 on a missing field, a non-200 answer or a stale observer older than 10 minutes (the numbers are what pool
|
|
// software and profitability sites read; a stale answer is a wrong answer).
|
|
import { FIELDS as STATS, REWARD_FIELDS, LAST_BLOCK_FIELDS } from '../../site/api/stats.mjs';
|
|
import { FIELDS as SUPPLY, SCHEDULE_FIELDS } from '../../site/api/supply.mjs';
|
|
|
|
const base = (process.argv[2] || process.env.PUBLIC_API_URL || 'https://igneum.network').replace(/\/$/, '');
|
|
const missing = [];
|
|
async function get(path) {
|
|
const r = await fetch(base + path, { headers: { 'cache-control': 'no-cache' }, signal: AbortSignal.timeout(15000) });
|
|
const j = await r.json().catch(() => null);
|
|
if (r.status !== 200 || !j || j.ok !== true) { console.error(`${path}: HTTP ${r.status} ${j ? JSON.stringify(j).slice(0, 200) : ''}`); process.exit(1); }
|
|
return j;
|
|
}
|
|
const s = await get('/api/stats');
|
|
for (const f of STATS) if (!(f in s)) missing.push(`stats.${f}`);
|
|
if (s.block_reward) for (const f of REWARD_FIELDS) if (!(f in s.block_reward)) missing.push(`stats.block_reward.${f}`);
|
|
if (s.last_block) for (const f of LAST_BLOCK_FIELDS) if (!(f in s.last_block)) missing.push(`stats.last_block.${f}`);
|
|
const p = await get('/api/supply');
|
|
for (const f of SUPPLY) if (!(f in p)) missing.push(`supply.${f}`);
|
|
if (p.schedule && p.schedule[0]) for (const f of SCHEDULE_FIELDS) if (!(f in p.schedule[0])) missing.push(`supply.schedule.${f}`);
|
|
if (missing.length) { console.error(`public api check: ${missing.length} missing field(s): ${missing.join(', ')}`); process.exit(1); }
|
|
if (s.stale && s.age_s > 600) { console.error(`public api check: the observer is stale (${s.age_s} s)`); process.exit(1); }
|
|
console.log(`public api check: ${base} /api/stats (${STATS.length} fields, height ${s.height}, reward ${s.block_reward && s.block_reward.ign} IGN, ${s.stale ? 'STALE ' + s.age_s + ' s' : 'live'}) and /api/supply (${SUPPLY.length} fields, circulating ${p.circulating_ign} IGN of ${p.max_supply_ign}) answer with every documented field`);
|