blockyard 0.0.1 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +679 -0
- package/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +172 -4
- package/SECURITY.md +38 -0
- package/bin/blockyard.js +40 -0
- package/config/pool-map.json +2620 -0
- package/docs/API.md +1575 -0
- package/docs/ARCHITECTURE.md +1307 -0
- package/docs/AUTO-UPDATE.md +269 -0
- package/docs/CONFIGURATION.md +840 -0
- package/docs/DEFECTS.md +813 -0
- package/docs/EFFECTS-AGENTS.md +448 -0
- package/docs/GETTING-STARTED.md +202 -0
- package/docs/INSTALL.md +490 -0
- package/docs/MEASUREMENTS.md +1254 -0
- package/docs/PRIVATE-LEADERBOARD.md +230 -0
- package/docs/RULES.md +681 -0
- package/docs/SECURITY-AUDIT-2026-09-14.md +177 -0
- package/docs/SECURITY-AUDIT.md +258 -0
- package/docs/SECURITY.md +195 -0
- package/docs/STATE-2026-09-09.md +200 -0
- package/docs/TROUBLESHOOTING.md +298 -0
- package/docs/USER-GUIDE.md +1022 -0
- package/package.json +53 -5
- package/public/404.html +9 -0
- package/public/css/app.css +1785 -0
- package/public/index.html +893 -0
- package/public/js/about.js +112 -0
- package/public/js/agents.js +964 -0
- package/public/js/app.js +1312 -0
- package/public/js/arkanoid.js +806 -0
- package/public/js/blockanoid.js +347 -0
- package/public/js/blockout.js +347 -0
- package/public/js/blockpack.js +428 -0
- package/public/js/blockscene3d.js +2678 -0
- package/public/js/breakout.js +224 -0
- package/public/js/charts.js +635 -0
- package/public/js/depthchart.js +311 -0
- package/public/js/details3d.js +2957 -0
- package/public/js/explorer.js +405 -0
- package/public/js/feepalette.js +149 -0
- package/public/js/fmt.js +162 -0
- package/public/js/goggles.js +886 -0
- package/public/js/kiosk.js +41 -0
- package/public/js/login.js +83 -0
- package/public/js/markets.js +357 -0
- package/public/js/mining.js +1138 -0
- package/public/js/panels.js +966 -0
- package/public/js/pricechart.js +188 -0
- package/public/js/settings.js +1014 -0
- package/public/js/tetris.js +226 -0
- package/public/js/tetrust.js +356 -0
- package/public/js/tetsound.js +175 -0
- package/public/login.html +33 -0
- package/scripts/blockfile-measure.js +156 -0
- package/scripts/browser-check.mjs +286 -0
- package/scripts/check.js +173 -0
- package/scripts/decode-check.js +81 -0
- package/scripts/doc-counts.js +109 -0
- package/scripts/donate-qr.py +20 -0
- package/scripts/fake-node.js +534 -0
- package/scripts/index-bench.js +216 -0
- package/scripts/index-benchmark.js +117 -0
- package/scripts/index-build.js +40 -0
- package/scripts/live-render-check.mjs +89 -0
- package/scripts/manage-users.js +132 -0
- package/scripts/motion-check.mjs +138 -0
- package/scripts/pool-map.js +157 -0
- package/scripts/setup.js +410 -0
- package/scripts/shots.mjs +272 -0
- package/scripts/smoke.sh +327 -0
- package/scripts/ui.js +174 -0
- package/server/auth/sessions.js +221 -0
- package/server/auth/users.js +243 -0
- package/server/chain/blockfile.js +234 -0
- package/server/chain/index/build.js +193 -0
- package/server/chain/index/heights.js +36 -0
- package/server/chain/index/live.js +276 -0
- package/server/chain/index/rows.js +145 -0
- package/server/chain/index/store.js +154 -0
- package/server/chain/index/worker.js +109 -0
- package/server/chain/tx.js +310 -0
- package/server/collect/gbt.js +229 -0
- package/server/collect/logparse.js +765 -0
- package/server/collect/logtail.js +189 -0
- package/server/collect/markets.js +333 -0
- package/server/collect/mining.js +333 -0
- package/server/collect/monitor.js +2516 -0
- package/server/collect/nextblock.js +275 -0
- package/server/collect/sync.js +386 -0
- package/server/config.js +620 -0
- package/server/http/api.js +1275 -0
- package/server/http/explorer.js +418 -0
- package/server/http/server.js +412 -0
- package/server/http/sse.js +176 -0
- package/server/http/static.js +212 -0
- package/server/main.js +628 -0
- package/server/netinfo.js +253 -0
- package/server/rpc/allowlist.js +130 -0
- package/server/rpc/client.js +414 -0
- package/server/store/audit.js +148 -0
- package/server/store/history.js +220 -0
- package/server/store/ledger.js +290 -0
- package/server/store/ring.js +173 -0
- package/server/util/fmt.js +29 -0
- package/systemd/blockyard.service +100 -0
package/public/js/fmt.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Display formatters. Units are decimal (1 KB = 1000 B) to match how the node
|
|
2
|
+
// itself prints sizes -- its log renders 4096 bytes as "4.0KB", so the monitor
|
|
3
|
+
// must not be the one place on the box that says 4.1 KB.
|
|
4
|
+
const U = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
5
|
+
|
|
6
|
+
export function bytes(n, dp = 1) {
|
|
7
|
+
if (n == null || !Number.isFinite(n)) return '–';
|
|
8
|
+
let v = Math.abs(n);
|
|
9
|
+
let i = 0;
|
|
10
|
+
while (v >= 1000 && i < U.length - 1) { v /= 1000; i += 1; }
|
|
11
|
+
return `${n < 0 ? '-' : ''}${v.toFixed(i === 0 ? 0 : dp)} ${U[i]}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function rate(bps, unit = 'B/s') {
|
|
15
|
+
if (bps == null || !Number.isFinite(bps)) return '–';
|
|
16
|
+
let v = Math.abs(bps);
|
|
17
|
+
let i = 0;
|
|
18
|
+
while (v >= 1000 && i < U.length - 1) { v /= 1000; i += 1; }
|
|
19
|
+
return `${(bps < 0 ? '-' : '')}${v.toFixed(i === 0 ? 0 : 1)} ${U[i]}/${unit === 'B/s' ? 's' : unit}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function num(n, dp = 0) {
|
|
23
|
+
if (n == null || !Number.isFinite(n)) return '–';
|
|
24
|
+
return n.toLocaleString('en-US', { minimumFractionDigits: dp, maximumFractionDigits: dp });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function pct(n, dp = 2) {
|
|
28
|
+
if (n == null || !Number.isFinite(n)) return '–';
|
|
29
|
+
return `${n.toFixed(dp)}%`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// DD:HH:MM:SS, matching the node's own ETA and heartbeat format.
|
|
33
|
+
export function eta(sec) {
|
|
34
|
+
if (sec == null || !Number.isFinite(sec) || sec < 0) return '–';
|
|
35
|
+
const s = Math.floor(sec);
|
|
36
|
+
const p = (x, w = 2) => String(x).padStart(w, '0');
|
|
37
|
+
return `${p(Math.floor(s / 86400), 2)}:${p(Math.floor((s % 86400) / 3600))}:${p(Math.floor((s % 3600) / 60))}:${p(s % 60)}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function ago(ms, now = Date.now()) {
|
|
41
|
+
if (ms == null || !Number.isFinite(ms)) return '–';
|
|
42
|
+
const s = Math.max(0, (now - ms) / 1000);
|
|
43
|
+
if (s < 45) return `${Math.round(s)}s ago`;
|
|
44
|
+
if (s < 5400) return `${Math.round(s / 60)}m ago`;
|
|
45
|
+
if (s < 172800) return `${(s / 3600).toFixed(1)}h ago`;
|
|
46
|
+
return `${(s / 86400).toFixed(1)}d ago`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function ageSec(sec) {
|
|
50
|
+
if (sec == null || !Number.isFinite(sec)) return '–';
|
|
51
|
+
if (sec < 60) return `${Math.round(sec)}s`;
|
|
52
|
+
if (sec < 5400) return `${(sec / 60).toFixed(1)}m`;
|
|
53
|
+
if (sec < 172800) return `${(sec / 3600).toFixed(1)}h`;
|
|
54
|
+
return `${(sec / 86400).toFixed(1)}d`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function sats(n) {
|
|
58
|
+
if (n == null || !Number.isFinite(n)) return '–';
|
|
59
|
+
return n.toLocaleString('en-US');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function btc(sat) {
|
|
63
|
+
if (sat == null || !Number.isFinite(sat)) return '–';
|
|
64
|
+
return (sat / 1e8).toFixed(8);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Core reports feerates in BTC/kvB; operators think in sat/vB. Convert, and show
|
|
68
|
+
// the unit every time so nobody compares two numbers in different currencies.
|
|
69
|
+
export function satPerVb(btcPerKvB, dp = 2) {
|
|
70
|
+
if (btcPerKvB == null || !Number.isFinite(btcPerKvB)) return '–';
|
|
71
|
+
return (btcPerKvB * 1e8 / 1000).toFixed(dp);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// `n` is already in EH/s, so each step up is a THOUSAND of them: 1e3 EH/s is a zettahash and 1e6
|
|
75
|
+
// is a yottahash. Both labels were one prefix too low (2026-09-12) -- 1112 EH/s printed as
|
|
76
|
+
// "1.11 EH/s" -- which stayed hidden while the estimator that feeds this was itself out by 2^32
|
|
77
|
+
// and never produced a number above 1.
|
|
78
|
+
export function eh(n) {
|
|
79
|
+
if (n == null || !Number.isFinite(n)) return '–';
|
|
80
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(2)} YH/s`;
|
|
81
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(2)} ZH/s`;
|
|
82
|
+
return `${n.toFixed(1)} EH/s`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function clock(ms) {
|
|
86
|
+
const d = new Date(ms);
|
|
87
|
+
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
export function short(n) {
|
|
92
|
+
if (n == null || !Number.isFinite(n)) return '–';
|
|
93
|
+
const a = Math.abs(n);
|
|
94
|
+
if (a >= 1e12) return (n / 1e12).toFixed(1) + 'T';
|
|
95
|
+
if (a >= 1e9) return (n / 1e9).toFixed(1) + 'G';
|
|
96
|
+
if (a >= 1e6) return (n / 1e6).toFixed(1) + 'M';
|
|
97
|
+
if (a >= 1e3) return (n / 1e3).toFixed(a >= 1e4 ? 0 : 1) + 'k';
|
|
98
|
+
if (a >= 100) return n.toFixed(0);
|
|
99
|
+
if (a >= 1) return (+n.toFixed(1)).toString();
|
|
100
|
+
return (+n.toFixed(4)).toString();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function hash(x, keep = 8) {
|
|
104
|
+
if (!x || typeof x !== 'string') return '–';
|
|
105
|
+
if (x.length <= keep * 2 + 3) return x;
|
|
106
|
+
return `${x.slice(0, keep)}…${x.slice(-keep)}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function esc(s) {
|
|
110
|
+
return String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export function uptime(ms) {
|
|
114
|
+
if (!Number.isFinite(ms)) return '–';
|
|
115
|
+
const s = Math.floor(ms / 1000);
|
|
116
|
+
const d = Math.floor(s / 86400);
|
|
117
|
+
const h = Math.floor((s % 86400) / 3600);
|
|
118
|
+
const m = Math.floor((s % 3600) / 60);
|
|
119
|
+
return d > 0 ? `${d}d ${h}h` : h > 0 ? `${h}h ${m}m` : `${m}m`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// THE ONE WARNING THE BANNER DOES NOT SHOW (operator, 2026-09-13, getting ready for release:
|
|
123
|
+
// "how do we get rid of the node warning?" -- "This is a pre-release test build - use at your
|
|
124
|
+
// own risk - do not use for mining or merchant applications").
|
|
125
|
+
//
|
|
126
|
+
// It is TRUE: a node built from source reports itself pre-release on every poll, which means a red
|
|
127
|
+
// caveat sits across the hero permanently and stops meaning anything. The fix is deliberately
|
|
128
|
+
// the narrowest one available -- this exact notice, matched on the phrase bitcoind has always
|
|
129
|
+
// used for it, and nothing else. "unknown new rules activated", a chain reorganisation, an
|
|
130
|
+
// unsupported chainstate: all still land in the banner, in red.
|
|
131
|
+
//
|
|
132
|
+
// WHERE THIS DOES NOT HAPPEN: the server. monitor.js and sync.js keep passing the node's exact
|
|
133
|
+
// words through, so /api/state still reports what the node said and the filter cannot become a
|
|
134
|
+
// way for the monitor to conceal it. This is a presentation choice, applied at the banner.
|
|
135
|
+
const PRE_RELEASE = /pre-release test build/i;
|
|
136
|
+
|
|
137
|
+
/** The node's warnings, minus the permanent pre-release notice. Never throws on odd input. */
|
|
138
|
+
export function nodeWarnings(list) {
|
|
139
|
+
if (!Array.isArray(list)) return [];
|
|
140
|
+
return list.filter((w) => typeof w === 'string' && w.trim() && !PRE_RELEASE.test(w));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Copy text to the clipboard. The monitor is served over plain http on the LAN, where
|
|
145
|
+
* navigator.clipboard does not exist (it needs a secure context), so a select-and-copy fallback is
|
|
146
|
+
* kept. Resolves when copied, rejects when the browser refused. (public/js/explorer.js keeps its
|
|
147
|
+
* own copy of this: that file imports nothing by design.)
|
|
148
|
+
*/
|
|
149
|
+
export function copyText(text) {
|
|
150
|
+
if (globalThis.navigator?.clipboard && globalThis.isSecureContext) return navigator.clipboard.writeText(text);
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
const ta = document.createElement('textarea');
|
|
153
|
+
ta.value = text;
|
|
154
|
+
ta.setAttribute('readonly', '');
|
|
155
|
+
ta.className = 'xclip';
|
|
156
|
+
document.body.appendChild(ta);
|
|
157
|
+
ta.select();
|
|
158
|
+
const ok = document.execCommand?.('copy');
|
|
159
|
+
ta.remove();
|
|
160
|
+
if (ok) resolve(); else reject(new Error('copy refused'));
|
|
161
|
+
});
|
|
162
|
+
}
|