Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions dashboard/lib/health.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ import { rateVerification } from './stats.js';
* drynet3. An hour means something is genuinely wrong, not slow. */
const PAYOUT_STALL_SEC = 3600;

/* The enforcer's first template after a new block carries only the witness
* commitment; the sidechain commitments follow on the next poll, ~30s later.
* Measured on betanet: every block, never longer than one 30s poll. A bare
* template younger than this is that gap, not a fault — four polls of grace,
* and an enforcer that really stopped committing still fails within one
* monitor interval after it. */
const TEMPLATE_COMMIT_GRACE_SEC = 120;

/* The block subsidy at a height, on the standard schedule: 50 BTC, halved
* every 210,000 blocks. Chains derived from Bitcoin — mainnet, testnet,
* signet, and forknets that keep their parent's height — all share it;
Expand Down Expand Up @@ -415,10 +423,14 @@ export function health(handle) {
* so nothing else complains — but no sidechain can be merge-mined into
* them, which is what stalled Thunder before. */
checks.push(guard('template_commitments', 'Templates carry sidechain commitments', () => {
const r = one(d, `SELECT source, cb_op_returns FROM templates
ORDER BY id DESC LIMIT 1`);
const r = one(d, `SELECT source, cb_op_returns,
strftime('%s','now') - ts AS age
FROM templates ORDER BY id DESC LIMIT 1`);
if (!r) return { ok: true, unavailable: true, detail: 'no templates recorded yet' };
const ok = r.source === 'enforcer' && Number(r.cb_op_returns) > 1;
if (!ok && Number(r.age) < TEMPLATE_COMMIT_GRACE_SEC)
return { ok: true, value: Number(r.cb_op_returns),
detail: 'new block — commitments pending' };
return { ok, value: Number(r.cb_op_returns),
detail: ok ? null
: `mining ${r.source} templates with ${r.cb_op_returns} OP_RETURN(s) — no sidechain can merge-mine` };
Expand Down
25 changes: 25 additions & 0 deletions dashboard/test/health.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,31 @@ test('an enforcer template with only a witness commitment is caught', () => {
assert.ok(failing(health(db)).includes('template_commitments'));
});

/* Every new block opens with one bare enforcer template for ~30s before the
* commitments arrive. That gap must not raise the alarm. */
test('a bare template only seconds old is the new-block gap, not a failure', () => {
const db = makeDb();
db.prepare(`INSERT INTO templates (ts,height,prev_hash,bits,network_difficulty,
coinbase_value_sats,tx_count,tx_fees_sats,source,cb_spendable,
cb_op_returns,longpoll,rate_sats_per_diff)
VALUES (?, 3, 'bb', '1a', 111157.455, 312500000, 1, 0,
'enforcer', 1, 1, 1, ?)`).run(Math.floor(Date.now() / 1000) - 20, RATE);
const h = health(db);
assert.ok(!failing(h).includes('template_commitments'));
assert.match(h.checks.find(c => c.id === 'template_commitments').detail, /pending/);
});

/* ...but one that outlives the grace is the real thing. */
test('a bare template past the grace period is caught', () => {
const db = makeDb();
db.prepare(`INSERT INTO templates (ts,height,prev_hash,bits,network_difficulty,
coinbase_value_sats,tx_count,tx_fees_sats,source,cb_spendable,
cb_op_returns,longpoll,rate_sats_per_diff)
VALUES (?, 3, 'bb', '1a', 111157.455, 312500000, 1, 0,
'enforcer', 1, 1, 1, ?)`).run(Math.floor(Date.now() / 1000) - 180, RATE);
assert.ok(failing(health(db)).includes('template_commitments'));
});

/* A check that cannot run must not read as a pass, and must not take the
* page down either. */
test('a DB missing a table degrades to unavailable, not to healthy', () => {
Expand Down
Loading