DChain single-node blockchain + React Native messenger client. Core: - PBFT consensus with multi-sig validator admission + equivocation slashing - BadgerDB + schema migration scaffold (CurrentSchemaVersion=0) - libp2p gossipsub (tx/v1, blocks/v1, relay/v1, version/v1) - Native Go contracts (username_registry) alongside WASM (wazero) - WebSocket gateway with topic-based fanout + Ed25519-nonce auth - Relay mailbox with NaCl envelope encryption (X25519 + Ed25519) - Prometheus /metrics, per-IP rate limit, body-size cap Deployment: - Single-node compose (deploy/single/) with Caddy TLS + optional Prometheus - 3-node dev compose (docker-compose.yml) with mocked internet topology - 3-validator prod compose (deploy/prod/) for federation - Auto-update from Gitea via /api/update-check + systemd timer - Build-time version injection (ldflags → node --version) - UI / Swagger toggle flags (DCHAIN_DISABLE_UI, DCHAIN_DISABLE_SWAGGER) Client (client-app/): - Expo / React Native / NativeWind - E2E NaCl encryption, typing indicator, contact requests - Auto-discovery of canonical contracts, chain_id aware, WS reconnect on node switch Documentation: - README.md, CHANGELOG.md, CONTEXT.md - deploy/single/README.md with 6 operator scenarios - deploy/UPDATE_STRATEGY.md with 4-layer forward-compat design - docs/contracts/*.md per contract
59 lines
2.0 KiB
JavaScript
59 lines
2.0 KiB
JavaScript
(function() {
|
|
var C = window.ExplorerCommon;
|
|
|
|
async function loadValidators() {
|
|
C.setStatus('Loading…', 'warn');
|
|
try {
|
|
var data = await C.fetchJSON('/api/validators');
|
|
var validators = (data && Array.isArray(data.validators)) ? data.validators : [];
|
|
var quorum = validators.length > 0 ? Math.floor(2 * validators.length / 3) + 1 : 0;
|
|
|
|
document.getElementById('valCount').textContent = validators.length;
|
|
document.getElementById('quorumCount').textContent = quorum || '—';
|
|
|
|
var tbody = document.getElementById('valBody');
|
|
if (!validators.length) {
|
|
tbody.innerHTML = '<tr><td colspan="4" class="tbl-empty">No validators found.</td></tr>';
|
|
C.setStatus('No validators registered.', 'warn');
|
|
return;
|
|
}
|
|
|
|
var rows = '';
|
|
validators.forEach(function(v, i) {
|
|
var addr = v.address || '—';
|
|
var pubKey = v.pub_key || '—';
|
|
rows +=
|
|
'<tr>' +
|
|
'<td class="text-muted" style="font-size:12px">' + (i + 1) + '</td>' +
|
|
'<td>' +
|
|
'<a class="mono" href="/address?address=' + encodeURIComponent(pubKey) + '">' +
|
|
C.esc(addr) +
|
|
'</a>' +
|
|
'</td>' +
|
|
'<td class="mono" style="color:var(--muted);font-size:12px">' +
|
|
C.esc(C.short(pubKey, 32)) +
|
|
'</td>' +
|
|
'<td>' +
|
|
'<a href="/node?node=' + encodeURIComponent(pubKey) + '" class="pill-link" style="font-size:12px;padding:4px 10px">' +
|
|
'<i data-lucide="server"></i> Node' +
|
|
'</a>' +
|
|
'</td>' +
|
|
'</tr>';
|
|
});
|
|
tbody.innerHTML = rows;
|
|
|
|
C.setStatus(
|
|
validators.length + ' validator' + (validators.length !== 1 ? 's' : '') +
|
|
' · quorum: ' + quorum,
|
|
'ok'
|
|
);
|
|
C.refreshIcons();
|
|
} catch (e) {
|
|
C.setStatus('Load failed: ' + e.message, 'err');
|
|
}
|
|
}
|
|
|
|
document.getElementById('refreshBtn').addEventListener('click', loadValidators);
|
|
loadValidators();
|
|
})();
|