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
2 changes: 1 addition & 1 deletion broadcaster/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ if Telegram or the service goes down, the pool is unaffected.
| **Block lost** | a block it announced turns `orphaned` or `rejected` |
| **Pool quiet / back** | no share accepted for `STALE_SHARES_SEC` (default 15m), and when shares resume |
| **Health failing / recovered** | `/health` changes state, with the failing checks listed |
| **Pinned live message** (`BROADCASTER_LIVE=1`) | one message, pinned unless `BROADCASTER_LIVE_PIN=0`, edited every `BROADCASTER_LIVE_MS` (default 5m) with current stats and the last block |
| **Pinned live message** (`BROADCASTER_LIVE=1`) | one message, pinned unless `BROADCASTER_LIVE_PIN=0`, edited every `BROADCASTER_LIVE_MS` (default 5m) with current stats, the chain tip and the pool's last mined block |
| **`/pool_status`** (`BROADCASTER_COMMANDS=1`) | when someone sends it in the chat: the current stats, as a reply in the same topic, at most once per `BROADCASTER_COMMAND_COOLDOWN_SEC` (default 30) |
| **Periodic update** (`BROADCASTER_SUMMARY_HOURS=N`) | the same stats as a new message every N hours, on UTC boundaries (6 → 00, 06, 12, 18 UTC) |

Expand Down
21 changes: 16 additions & 5 deletions broadcaster/lib/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,28 @@ export function digest({ status, blocks, poolName, publicUrl, nowSec }) {
);
}

/* The newest block that still counts: pending or confirmed. An orphaned or
* rejected one is not the pool's last block. `blocks` is newest first. */
/* The newest block THIS POOL mined that still counts: pending or confirmed.
* An orphaned or rejected one is not the pool's last block. `blocks` is
* newest first. Labelled "mined" so it is never read as the chain's height,
* which chainTipLine gives. */
export function lastBlockLine(blocks, nowSec) {
const b = (blocks ?? []).find((x) => x.status === 'pending' || x.status === 'confirmed');
if (!b) return 'Last block: none yet';
if (!b) return 'Last mined block: none yet';
const ago = b.ts != null ? ` · ${fmtAgo(nowSec - b.ts)} ago` : '';
return `Last block: <b>#${fmtN(b.height)}</b>${ago}`;
return `Last mined block: <b>#${fmtN(b.height)}</b>${ago}`;
}

/* The network's latest block, as the pool's node sees it, by anyone. Null
* when the dashboard has no node status yet. */
export function chainTipLine(node, nowSec) {
if (node?.tip_height == null) return null;
const ago = node.tip_observed_at != null ? ` · ${fmtAgo(nowSec - node.tip_observed_at)} ago` : '';
return `Chain tip: #${fmtN(node.tip_height)}${ago}`;
}

/* The pinned message, the periodic summary and the /pool_status reply show
* the same numbers; only the heading and the time line differ. The last
* block line appears whenever the blocks list was read. */
* mined block line appears whenever the blocks list was read. */
function snapshot({ status, blocks, poolName, publicUrl, nowSec, flowing, title, timeLabel }) {
const p = status.pool;
const lastShare = p.last_share_ts ? fmtAgo(nowSec - p.last_share_ts) + ' ago' : 'never';
Expand All @@ -121,6 +131,7 @@ function snapshot({ status, blocks, poolName, publicUrl, nowSec, flowing, title,
'',
...statLines(p),
`Last share: ${lastShare}`,
chainTipLine(status.node, nowSec),
blocks !== undefined ? lastBlockLine(blocks, nowSec) : null,
`Blocks (all time): ${fmtN(p.blocks_lifetime)}`,
'',
Expand Down
15 changes: 13 additions & 2 deletions broadcaster/test/broadcaster.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,20 @@ test('live message and summary show the last counted block', async () => {
block('lost', 101, 'orphaned', T0 / 1000 - 600),
];
await b.tick(T0);
assert.match(telegram.posts[0], /Last block: <b>#100<\/b> · 2h 0m ago/);
assert.match(telegram.posts[0], /Last mined block: <b>#100<\/b> · 2h 0m ago/);
dashboard.s = status({ lastShareTs: (T0 + 60 * MIN) / 1000 });
await b.tick(T0 + 60 * MIN);
assert.match(telegram.posts[1], /— update/);
assert.match(telegram.posts[1], /Last block: <b>#100<\/b>/);
assert.match(telegram.posts[1], /Last mined block: <b>#100<\/b>/);
});

test('chain tip line appears with node status and is left out without it', async () => {
const { b, dashboard, telegram } = setup({ live: true, digestHour: null });
dashboard.s.node = { tip_height: 105, tip_observed_at: T0 / 1000 - 90 };
await b.tick(T0);
assert.match(telegram.posts[0], /Chain tip: #105 · 1m ago/);

const other = setup({ live: true, digestHour: null });
await other.b.tick(T0);
assert.doesNotMatch(other.telegram.posts[0], /Chain tip/);
});
3 changes: 2 additions & 1 deletion broadcaster/test/commands.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ function status() {
db_ready: true, hashrate: 1e12, hashrate_1h: 2e12, hashrate_5m: 3e12, workers_active: 4,
accepted: 1000, rejected: 5, reject_rate_pct: 0.5, best_share_24h: 123456, blocks_lifetime: 7, last_share_ts: T0 / 1000 - 5,
},
node: { tip_height: 970861, tip_observed_at: T0 / 1000 - 1200 },
};
}

Expand Down Expand Up @@ -69,7 +70,7 @@ test('/pool_status is answered in its topic, as a reply, with the last block', a
assert.match(html, /avonpool_beta — status/);
assert.match(html, /Hashrate now: <b>3\.00 TH\/s<\/b> \(5m\)/);
assert.match(html, /1,000 accepted · 5 rejected \(0\.50%\)/);
assert.match(html, /Last block: <b>#970,802<\/b> · 1h 0m ago/);
assert.match(html, /Chain tip: #970,861 · 20m ago\nLast mined block: <b>#970,802<\/b> · 1h 0m ago/);
assert.match(html, /^🟢/);
});

Expand Down
Loading