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
30 changes: 18 additions & 12 deletions src/painel.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,14 +299,17 @@ const proxyUp = () => !!PROXY_URL() && Date.now() - proxy.okAt < 2 * 60e3;
let known = loadJson('workers.json', {});

let records = loadJson('records.json', null);
if (!Array.isArray(records)) {
records = Object.values(known)
.filter((w) => Number(w.bestDifficulty) > 0)
.map((w) => ({ worker: w.name, value: Number(w.bestDifficulty), at: w.lastSeen }));
saveJson('records.json', records);
let recordsDirty = false;
if (!Array.isArray(records)) { records = []; recordsDirty = true; }
for (const w of Object.values(known)) {
const v = Number(w.bestDifficulty);
if (v > 0 && !records.some((r) => (r.worker === w.name || r.worker === '—') && sameShare(r.value, v))) {
records.push({ worker: w.name, value: v, at: w.lastSeen || new Date().toISOString() });
recordsDirty = true;
}
}
// O mesmo share chega por mais de um caminho (pool com 2 casas, proxy com mais): 1410,95 e 1410,9534 são um recorde só
const sameShare = (a, b) => Math.abs(a - b) <= Math.max(0.011, Math.max(a, b) * 1e-4);
const sameShare = (a, b) => Math.abs(a - b) <= Math.max(Math.min(0.011, Math.max(a, b) * 0.01), Math.max(a, b) * 1e-4);
function dedupeRecords(list) {
const out = [];
for (const r of [...list].sort((a, b) => new Date(a.at || 0) - new Date(b.at || 0))) {
Expand All @@ -318,7 +321,8 @@ function dedupeRecords(list) {
}
{
const clean = dedupeRecords(records);
if (clean.length !== records.length) { console.log(`recordes: ${records.length - clean.length} repetido(s) removido(s)`); records = clean; saveJson('records.json', records); }
if (clean.length !== records.length) { console.log(`recordes: ${records.length - clean.length} repetido(s) removido(s)`); records = clean; recordsDirty = true; }
if (recordsDirty) saveJson('records.json', records);
}

let history = loadJson('history.json', []);
Expand Down Expand Up @@ -348,7 +352,8 @@ function rememberWorkers(workers, poolName) {
const prev = known[w.name] || {};
const prevBest = Number(prev.bestDifficulty || 0);
const best = Number(w.bestDifficulty || 0);
if (best > prevBest && best >= 1 && !sameShare(best, prevBest)) addRecord(w.name, best);
const hasRec = records.some((r) => (r.worker === w.name || r.worker === '—') && sameShare(r.value, best));
if (((best > prevBest && !sameShare(best, prevBest)) || (!hasRec && best > 0)) && best > 0) addRecord(w.name, best);
known[w.name] = {
name: w.name,
hashRate: Number(w.hashRate || 0),
Expand Down Expand Up @@ -934,7 +939,8 @@ async function tickTuya() {
function snapshot(internal = false) {
const view = workersView();
const client = pool.client || {};
const best = Math.max(Number(client.bestDifficulty || 0), topRecord());
const proxyBest = Number((proxy.bestHash && proxy.bestHash.diff) || 0);
const best = Math.max(Number(client.bestDifficulty || 0), topRecord(), proxyBest);
const halving = net.tip ? (() => {
const remaining = Math.max(0, NEXT_HALVING - net.tip.height);
return { remaining, at: Date.now() + remaining * ((net.adj && net.adj.timeAvg) || 600000) };
Expand Down Expand Up @@ -1888,7 +1894,7 @@ function renderBitaxes(j) {
function renderFarm(j) {
const f = j.farm, a = f.accounting || {}, D = f.difficulty;
if (!f.hasData) return;
set('best', f.best >= 1e7 ? fmtBig(f.best) : nf(f.best, 2)); // recordes enormes não cabem no cartão
set('best', f.best >= 1e7 ? fmtBig(f.best) : f.best > 0 && f.best < 0.01 ? nf(f.best, 4) : nf(f.best, 2)); // recordes enormes não cabem no cartão
set('dist', f.best > 0 ? 'faltam ' + fmtBig(D / f.best) + ' de vezes' : '–');
set('hash', fmtHash(f.hashrate));
const av = f.hashrateAvg;
Expand Down Expand Up @@ -2319,7 +2325,7 @@ function renderHistory(j) {
const medals = ['1º', '2º', '3º', '4º', '5º'];
const hall = j.hall || [];
$('hall').innerHTML = hall.length ? hall.map((r, i) =>
'<li><span class="pos' + (i === 0 ? ' first' : '') + '">' + medals[i] + '</span><span class="val">' + nf(r.value, 2) +
'<li><span class="pos' + (i === 0 ? ' first' : '') + '">' + medals[i] + '</span><span class="val">' + (r.value > 0 && r.value < 0.01 ? nf(r.value, 4) : nf(r.value, 2)) +
'</span><span class="who">' + esc(r.worker) + '<br>' + (r.at ? diaMes(r.at) : '') + '</span></li>').join('')
: '<li><span></span><span class="sub">nenhum recorde ainda</span></li>';

Expand Down Expand Up @@ -2728,7 +2734,7 @@ function widgetData(lang) {
const pingMs = px && px.pingMs;
const pingLabel = (W.ping && W.ping[pingScore]) || pingScore || '';
return {
recorde: fmtNum(f.best, 2),
recorde: fmtNum(f.best, f.best > 0 && f.best < 0.01 ? 4 : 2),
recordeNum: f.best,
recordista: holder ? holder.worker : '',
faltam: f.best > 0 ? fmtBigW(f.difficulty / f.best) + W.times : '',
Expand Down
34 changes: 34 additions & 0 deletions tests/painel.unit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -694,3 +694,37 @@ test('recordes repetidos já gravados: limpa mantendo o mais antigo e o nome do
['tablet', 774.93, '2026-11-05T09:02:00Z'],
]);
});

test('recordes com dificuldade fracionária (< 1, ex.: NerdMiner / ESP32)', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'painel-nerd-'));
const oldDataDir = process.env.DATA_DIR;
process.env.DATA_DIR = dir;
const state = painel._state();
const oldRecords = state.records.slice();
const oldPoolClient = state.pool.client;
state.records.length = 0;
state.pool.client = {};
try {
const at = new Date().toISOString();
// Share inicial com diff < 1 deve ser registrado
painel.rememberWorkers([{ name: 'nmminer', hashRate: 994000, bestDifficulty: 0.297, lastSeen: at }], 'nerdminers');
assert.strictEqual(painel.topRecord(), 0.297);
assert.strictEqual(painel.snapshot().farm.best, 0.297);

// Novo recorde fracionário
painel.rememberWorkers([{ name: 'nmminer', hashRate: 994000, bestDifficulty: 0.5339238, lastSeen: at }], 'nerdminers');
assert.strictEqual(painel.topRecord(), 0.5339238);
assert.strictEqual(painel.snapshot().farm.best, 0.5339238);

// O mesmo share vindo com menos casas decimais (ex.: 0.53) não duplica
painel.rememberWorkers([{ name: 'nmminer', hashRate: 994000, bestDifficulty: 0.53, lastSeen: at }], 'nerdminers');
const records = painel.snapshot().hall.filter((r) => r.worker === 'nmminer');
assert.strictEqual(records.length, 2); // 0.297 e 0.5339238
} finally {
state.records.length = 0;
state.records.push(...oldRecords);
state.pool.client = oldPoolClient;
process.env.DATA_DIR = oldDataDir;
fs.rmSync(dir, { recursive: true, force: true });
}
});
Loading