live: the Proven tile reads a 600 s window of its own every 10 s (?summary=1 counts), the observer marks claimed shards proving behind OBSERVER_CLAIMS
Main's two follow-ons of 7 October 2026 after the per-block read. (1) /api/live?window=N&summary=1 returns the window's counts alone (blocks, chain, planned, proven, shards by state, the proving counters), and the /live Proven tile reads window 600 that way every 10 s with the line "of the last 10 minutes, every shard verified or paid", while the scene keeps its 60 s window; the stat was the scene window's count, zero by the proof lag. (2) The observer's shardStates reads an open claim as 'proving' from the claim until the proof lands: the records reply's own claims field whenever the node sends one, and the claims RPC behind OBSERVER_CLAIMS ('1' = igneum_getProofClaims, or a method name; unset = off); a node without the method turns the flag off for the run after one log line. Nothing lights until the node lane ships the RPC on the 0.3.20 line.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
a91c1f4a9a
commit
ffee1fb78e
4 changed files with 35 additions and 19 deletions
|
|
@ -41,6 +41,9 @@ export default async function handler(req, res) {
|
|||
// (7 October 2026: the cap was 300 s while proofs land two to five minutes after the block, so no window could show a proven block)
|
||||
const q = new URL(req.url || '/', 'http://x').searchParams;
|
||||
const windowS = Math.max(30, Math.min(1800, Number(q.get('window')) || 90));
|
||||
// ?summary=1: the counts of the window alone (blocks, chain blocks, proven, shards by state), for a tile that reads a long
|
||||
// window every 10 s while the scene keeps its short one (7 October 2026); no block rows, no miners, no events
|
||||
const summary = q.get('summary') === '1';
|
||||
const [head, blockRows, minerRows, checkpointRows, proofRows] = await Promise.all([
|
||||
sql(`SELECT now() AS now,
|
||||
(SELECT row_to_json(s) FROM ${T}live_state s WHERE s.id = 1) AS state,
|
||||
|
|
@ -173,6 +176,10 @@ export default async function handler(req, res) {
|
|||
// peer events never carry an address on the public API (review round 4, R4.6.2); older stored rows are redacted here too
|
||||
const events = (head[0].events || []).map(e => ({ ts: e.ts, kind: e.kind, text: String(e.text).replace(/\b\d{1,3}(\.\d{1,3}){3}(:\d+)?\b/g, 'a peer') }));
|
||||
|
||||
if (summary) {
|
||||
const by = {}; for (const b of blocks) for (const x of b.shards) by[x.state] = (by[x.state] || 0) + 1;
|
||||
return res.status(200).json({ ok: true, now: new Date(now).toISOString(), window: windowS, stale, blocks: blocks.length, chain: blocks.filter(b => b.chain).length, planned: blocks.filter(b => b.shards.length).length, proven: blocks.filter(b => b.proven).length, shards: by, proving });
|
||||
}
|
||||
return res.status(200).json({ ok: true, now: new Date(now).toISOString(), state, blocks, miners, events, finality, proving });
|
||||
} catch (e) {
|
||||
res.setHeader('Cache-Control', 'no-store');
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -207,48 +207,48 @@
|
|||
},
|
||||
{
|
||||
"date": "2026-10-04",
|
||||
"text": "One-click Windows workers: what the Apple M5 Max could measure",
|
||||
"short": "One-click Windows workers"
|
||||
"text": "Sim/economy: mining versus proving under stress, agent-based",
|
||||
"short": "Economy simulation: mining versus proving under stress"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-04",
|
||||
"text": "First finality lock on the live devnet: checkpoint 242 at 77.4% of all weight, two hours after genesis",
|
||||
"short": "First live finality lock: 77.4% of weight, 17 voters"
|
||||
"text": "Difficulty rule under attack: pool hopping, pulsed rental, timestamp stretching, short-lane oscillation, epoch games, polluted window, block flood",
|
||||
"short": "Difficulty rule attacked seven ways"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-04",
|
||||
"text": "The gfx1036 worker fault and what the Apple M5 Max could and could not reproduce",
|
||||
"short": "The gfx1036 worker fault and what the Apple M5 Max could and could…"
|
||||
"text": "Difficulty rule: timestamp attack fixed , simulator regression, 3-node forger test",
|
||||
"short": "Timestamp attack on the difficulty rule fixed"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-04",
|
||||
"text": "A node 60 s behind the clock is silently dead",
|
||||
"short": "A node 60 s behind the clock is silently dead"
|
||||
"text": "Devnet-v4 integration: nine branches merged, 3-node test network on the merged node, Windows cross-build",
|
||||
"short": "Devnet v4: nine branches merged into one node"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-04",
|
||||
"text": "First machine on the Igneum Miner app: the RTX 5090 Windows rig's RTX 5090 at 118 MH/s, via Setup.exe",
|
||||
"short": "First machine on the one-click app: a 5090 at 118 MH/s"
|
||||
"text": "Generator version 2 adopted: exact load count, fresh-source loads, program acceptance; every vector re-cut, three workers re-checked, 20,000-program census, devnet-v4 binaries rebuilt",
|
||||
"short": "Generator v2 adopted: every hash does 128 distinct reads"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-04",
|
||||
"text": "Difficulty rule v2: the live oscillation, its cause, the DAG replay, the fix behind a height switch",
|
||||
"short": "Difficulty rule v2"
|
||||
"text": "Proving v0 on the RTX 5090: first GPU proof of an Igneum block",
|
||||
"short": "First GPU proof of an Igneum block: 1.4 s on an RTX 5090"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-04",
|
||||
"text": "The observer stored nothing for 78 minutes, then 7,022 blocks in two minutes",
|
||||
"short": "The observer stored nothing for 78 minutes, then 7,022 blocks in two…"
|
||||
"text": "Devnet v4 cut-over: generator v2, 2/3 floor, three nodes and a seed on a fresh chain",
|
||||
"short": "Devnet v4 live: generator v2, two-thirds floor, fresh chain"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-04",
|
||||
"text": "The RTX 5090 Windows rig at the 14:20 boundary: a worker stuck on the previous epoch",
|
||||
"short": "The RTX 5090 Windows rig at the 14"
|
||||
"text": "First hourly program swap on the live devnet: compile-ahead, no pause, two cards",
|
||||
"short": "First live hourly swap: no pause on Mac, NVIDIA or AMD"
|
||||
},
|
||||
{
|
||||
"date": "2026-10-04",
|
||||
"text": "Shard proving on the RTX 5090: a full shard compressed in 10.9 s, a two-shard block aggregated in 2.2 s, all verified",
|
||||
"short": "Shard layer on the RTX 5090: a full shard proven in 10.9 s, a block aggregated in 2.2 s"
|
||||
"text": "Proving: devnet v4 shards on the Apple M5 Max CPU, loaded machine",
|
||||
"short": "Proving: devnet v4 shards on the Apple M5 Max CPU, loaded machine"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -853,6 +853,9 @@ async function backfillCertificate(rpc, report) {
|
|||
// moves past 'proving' on the page. The open set at 0.45 blocks a second is about 800 blocks; the batch of 80 reads each one
|
||||
// every 10 ticks.
|
||||
const PLAN_BATCH = 24, RECORD_BATCH_ACTIVE = 80, RECORD_BATCH_INACTIVE = 10, PROOF_WINDOW_MS = 30 * 60_000;
|
||||
// OBSERVER_CLAIMS: '1' reads open claims through igneum_getProofClaims; a method name reads them through that method; unset
|
||||
// or '0' reads none (the records reply's own claims field is used whenever the node sends one, flag or not)
|
||||
let CLAIMS_RPC = process.env.OBSERVER_CLAIMS === '1' ? 'igneum_getProofClaims' : /^[a-z_]+$/i.test(process.env.OBSERVER_CLAIMS || '') && process.env.OBSERVER_CLAIMS !== '0' ? process.env.OBSERVER_CLAIMS : null;
|
||||
const pendingPlans = new Map(); // chain block hash -> { tries, nextAt }
|
||||
const openBlocks = new Map(); // chain block hash -> { number, daa, ts, shards: [state...], provers, polledAt, done }
|
||||
const daaOfNumber = new Map(); // chain block number -> daa score, from the plans fetched (carriers are later chain blocks)
|
||||
|
|
@ -942,6 +945,9 @@ async function flushPlans() {
|
|||
// checked by consensus, which is what happens before activation) > proving (a record in the pool) > planned.
|
||||
function shardStates(block, r) {
|
||||
const n = block.shards.length, states = block.shards.map(() => 'planned'), out = states.map(() => ({ prover: null, verified: null, carrier: null, carrierNumber: null, payout: null }));
|
||||
// an open claim (a prover has taken the shard, no proof yet) reads 'proving' from the claim until the proof lands: the
|
||||
// records reply's own claims field when the node carries one, or the claims RPC behind OBSERVER_CLAIMS (7 October 2026)
|
||||
for (const c of r.claims || []) { const i = c.shard; if (i >= n || states[i] !== 'planned') continue; states[i] = 'proving'; out[i] = { ...out[i], prover: short(String(c.keyHash || c.prover || '').replace(/^0x/, '')) }; }
|
||||
for (const e of r.pool || []) { const i = e.shard; if (i >= n) continue; const rank = e.verified === true ? 2 : 1; if (rank > ['planned', 'proving', 'verified', 'paid'].indexOf(states[i])) { states[i] = rank === 2 ? 'verified' : 'proving'; out[i] = { ...out[i], prover: short(String(e.keyHash || '').replace(/^0x/, '')), verified: e.verified, carrier: e.includedIn ? String(e.includedIn).replace(/^0x/, '') : null }; } }
|
||||
for (const c of r.carried || []) { const i = c.shard; if (i >= n || !c.valid) continue; if (states[i] !== 'paid') { states[i] = 'verified'; out[i] = { ...out[i], prover: short(String(c.keyHash || '').replace(/^0x/, '')), carrier: String(c.carrier || '').replace(/^0x/, ''), carrierNumber: hx(c.carrierNumber) }; } }
|
||||
(r.paid || []).forEach((p, i) => { if (!p || i >= n) return; states[i] = 'paid'; out[i] = { ...out[i], prover: short(String(p.keyHash || '').replace(/^0x/, '')), carrierNumber: hx(p.carrierNumber), payout: weiStr(p.wei) }; });
|
||||
|
|
@ -961,6 +967,9 @@ async function pollRecords() {
|
|||
await Promise.all(batch.slice(i, i + 4).map(async ([h, b]) => {
|
||||
b.polledAt = now;
|
||||
let r; try { r = await evm('igneum_getProofRecords', [{ blockHash: '0x' + h }]); } catch (e) { if (!/not found/.test(e.message)) log('proof records failed', h.slice(0, 8), e.message); return; }
|
||||
// the claims read, behind the flag until the node ships the RPC (main's ask of 7 October 2026 on the 0.3.20 line); a
|
||||
// method the node lacks turns the flag off for the run instead of logging on every block
|
||||
if (CLAIMS_RPC && !b.done && !(r && r.claims)) { try { const c = await evm(CLAIMS_RPC, [{ blockHash: '0x' + h }]); r = { ...(r || {}), claims: Array.isArray(c) ? c : (c && c.claims) || [] }; } catch (e) { if (/method not found|unknown method|not supported/i.test(e.message)) { log('claims RPC', CLAIMS_RPC, 'not on this node; claims off for the run'); CLAIMS_RPC = null; } else if (!/not found/.test(e.message)) log('proof claims failed', h.slice(0, 8), e.message); } }
|
||||
const { states, out } = shardStates(b, r);
|
||||
for (let s = 0; s < states.length; s++) {
|
||||
const key = `${states[s]}|${out[s].prover}|${out[s].carrierNumber}|${out[s].carrier}`;
|
||||
|
|
|
|||
Loading…
Reference in a new issue