Skip to content

Commit 991ca9e

Browse files
authored
dash(dashboard): wire Best Share + peers + found-blocks to the real work source (#790)
Priority — Best Share card. On the DASH solo path shares seldom mint onto the sharechain, so the tracker's verified-share hook never fired and the "Best Share" card sat empty. Record every ACCEPTED stratum pseudoshare's real X11 difficulty + miner + pow-hash into the dashboard best-share tracker via a new IWorkSource::record_best_pseudoshare() hook that the core stratum session fires on its vardiff-accept path (the one place every pseudoshare is seen). DASHWorkSource overrides it to recompute the exact pow-hash and forward to record_share_difficulty; LTC/BTC/DGB inherit a no-op (unchanged). record_share_difficulty + BestDifficulty + /best_share now carry the share hash; a compact best_share block is added to /local_stats. The dashboard card renders the record hash line. The card now shows how close the best share got to net difficulty (pct_of_block). Peers / node-status card. Add dash::Node::get_peer_info_json (ltc node.hpp parity) and wire MiningInterface::set_peer_info_fn so /local_stats peers and the node-status panel report the real pool p2p peers instead of 0. Recent-blocks card. DASHWorkSource fires a found-block hook after a won block is dispatched to the network; main_dash records it via record_found_block so DASH block wins appear in the found-block history. All three are display-only feeds: no share/target/payout/consensus logic is touched (the record_best_pseudoshare and found-block calls run after the existing accept/dispatch decisions and only read callbacks). Verified live against a testnet dashd: best_share populated difficulty=7.05e-4, pct_of_block=8.5%, with the exact pow-hash + miner; miner_hash_rates, block_value and payout split also populate through the reused LTC wiring.
1 parent 7c98660 commit 991ca9e

9 files changed

Lines changed: 301 additions & 16 deletions

File tree

src/c2pool/main_dash.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,16 @@ int run_node(bool testnet, const std::string& rpc_endpoint,
674674
// ── REAL best-share hash ──────────────────────────────────────
675675
web_server->set_best_share_hash_fn(
676676
[node_ptr]() { return node_ptr->best_share_hash(); });
677+
678+
// ── REAL pool-peer info (node-status card) ────────────────────
679+
// /local_stats {peers:{incoming,outgoing}} + the peer table read
680+
// this. Without it the node-status card reported 0 peers on DASH
681+
// (the m_node fallback in rest_local_stats never sees the pool
682+
// p2p peers). Same shape main_ltc.cpp:2830 uses. Display only.
683+
mi->set_peer_info_fn(
684+
[&p2p_node]() -> nlohmann::json {
685+
return p2p_node.get_peer_info_json();
686+
});
677687
}
678688

679689
// ── REAL per-share difficulty feed (main_ltc.cpp:4213) ────────────
@@ -940,6 +950,48 @@ int run_node(bool testnet, const std::string& rpc_endpoint,
940950
work_source->set_best_share_hash_fn(
941951
[node_ptr]() -> uint256 { return node_ptr->best_share_hash(); });
942952

953+
// ── REAL best-share feed for the dashboard "Best Share" card ──────
954+
// Every ACCEPTED stratum submit reports the actual PoW difficulty of
955+
// the found hash (target_to_difficulty(pow_hash)) + miner + pow-hash.
956+
// record_share_difficulty tracks the pool-wide/session/round max WITH
957+
// the hash + timestamp; /best_share + /local_stats render it. This is
958+
// the PRIMARY best-share feed on the DASH solo path — the tracker's
959+
// verified-share m_on_share_difficulty hook (wired above) almost never
960+
// fires here because solo shares seldom mint onto the sharechain, so
961+
// without this the 🎯 Best Share card sat empty. Display only; the
962+
// callback touches no share/target/payout logic (consensus-neutral).
963+
if (web_server) {
964+
core::WebServer* ws = web_server.get();
965+
work_source->set_on_share_difficulty_fn(
966+
[ws](double diff, const std::string& miner, const uint256& pow_hash) {
967+
ws->get_mining_interface()->record_share_difficulty(
968+
diff, miner, pow_hash.GetHex());
969+
});
970+
971+
// ── Recent-blocks feed: DASH block wins into the history card ──
972+
// Every dispatched block solution records to /recent_blocks with
973+
// the height, X11 block hash (== pow_hash for DASH), miner, and the
974+
// net difficulty at find time. Without this DASH block wins never
975+
// appeared on the recent-blocks card (main_ltc.cpp:2970 parity).
976+
// Display only; the callback runs after dispatch, never gates it.
977+
work_source->set_on_found_block_fn(
978+
[ws](uint32_t height, const uint256& block_hash,
979+
const std::string& miner, bool reached_network) {
980+
auto* mi = ws->get_mining_interface();
981+
mi->record_found_block(
982+
height, block_hash,
983+
static_cast<uint64_t>(std::time(nullptr)),
984+
"DASH", miner, block_hash.GetHex(),
985+
mi->get_network_difficulty(),
986+
/*share_difficulty=*/0.0,
987+
/*pool_hashrate=*/0.0,
988+
/*subsidy=*/0);
989+
if (!reached_network)
990+
LOG_WARNING << "[DASH] recorded found block height="
991+
<< height << " that reached NO network sink";
992+
});
993+
}
994+
943995
// Producer job: the stratum coinbase IS the producer share gentx
944996
// (byte-parity with the mint-time rebuild by construction). The
945997
// frozen per-job context is registered under its ref_hash.

src/core/stratum_server.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,6 +1286,20 @@ nlohmann::json StratumSession::handle_submit(const nlohmann::json& params, const
12861286
// Record in per-connection tracker (for VARDIFF adjustment + per-session stats)
12871287
hashrate_tracker_.record_mining_share_submission(credited_difficulty, true);
12881288

1289+
// Best-share dashboard feed: every ACCEPTED pseudoshare's real (X11/scrypt)
1290+
// difficulty is a candidate for the "Best Share" card (highest-difficulty
1291+
// share seen — how close a miner got to a block). Default no-op; a work
1292+
// source whose dashboard is a separate object (c2pool-dash) overrides this
1293+
// to forward into the dashboard's best-share tracker. share_difficulty is
1294+
// the true per-hash difficulty computed above; pass the raw submit fields so
1295+
// the implementor can recover the exact pow-hash. Display only.
1296+
mining_interface_->record_best_pseudoshare(
1297+
share_difficulty, username_,
1298+
job.payload->coinb1, job.payload->coinb2,
1299+
extranonce1_, extranonce2, ntime, nonce,
1300+
effective_version, job.gbt_prevhash, job.nbits,
1301+
job.payload->merkle_branches);
1302+
12891303
// Check if VARDIFF changed → send new difficulty AND new work to miner.
12901304
// p2pool (stratum.py:594-595): after adjusting target, calls _send_work()
12911305
// which sends both set_difficulty and mining.notify. Without new work,

src/core/stratum_work_source.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,25 @@ class IWorkSource {
183183
uint32_t version, const std::string& prevhash_hex,
184184
const std::string& nbits_hex,
185185
const std::vector<std::string>& merkle_branches) const = 0;
186+
187+
/// Record an ACCEPTED pseudoshare for the dashboard "Best Share" card
188+
/// (the highest-difficulty share seen — how close a miner got to a block).
189+
/// The stratum session invokes this for EVERY accepted pseudoshare (the
190+
/// vardiff-gate acceptance path), passing the already-computed X11/scrypt
191+
/// share difficulty plus the raw submit fields so an implementor that wants
192+
/// the exact pow-hash can recompute it. Default no-op — only work sources
193+
/// whose dashboard is a separate object (c2pool-dash: the DASHWorkSource
194+
/// drives its own stratum acceptor, distinct from the WebServer's
195+
/// MiningInterface) override this to forward to the dashboard's best-share
196+
/// tracker. Display only — never affects acceptance, target, or payout.
197+
virtual void record_best_pseudoshare(
198+
double /*share_difficulty*/, const std::string& /*miner*/,
199+
const std::string& /*coinb1*/, const std::string& /*coinb2*/,
200+
const std::string& /*extranonce1*/, const std::string& /*extranonce2*/,
201+
const std::string& /*ntime*/, const std::string& /*nonce*/,
202+
uint32_t /*version*/, const std::string& /*prevhash_hex*/,
203+
const std::string& /*nbits_hex*/,
204+
const std::vector<std::string>& /*merkle_branches*/) {}
186205
};
187206

188207
} // namespace core::stratum

src/core/web_server.cpp

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3803,6 +3803,29 @@ nlohmann::json MiningInterface::rest_local_stats()
38033803
}
38043804
result["miner_last_difficulties"] = miner_diffs;
38053805

3806+
// Best-share summary (highest-difficulty pseudoshare seen). Mirrors the
3807+
// dedicated /best_share endpoint so /local_stats consumers get the "how
3808+
// close did the best share get to a block" metric inline. Display only.
3809+
{
3810+
std::lock_guard<std::mutex> lock(m_best_diff_mutex);
3811+
auto pct = [&](double d) { return net_diff > 0 ? d / net_diff * 100.0 : 0.0; };
3812+
auto bs_entry = [&](double diff, const std::string& miner,
3813+
const std::string& hash, uint64_t ts) {
3814+
return nlohmann::json{
3815+
{"difficulty", diff}, {"pct_of_block", pct(diff)},
3816+
{"miner", miner}, {"hash", hash}, {"timestamp", ts}};
3817+
};
3818+
result["best_share"] = {
3819+
{"network_difficulty", net_diff},
3820+
{"round", bs_entry(m_best_difficulty.round, m_best_difficulty.miner,
3821+
m_best_difficulty.hash, m_best_difficulty.timestamp)},
3822+
{"session", bs_entry(m_best_difficulty.session, m_best_difficulty.session_miner,
3823+
m_best_difficulty.session_hash, m_best_difficulty.session_ts)},
3824+
{"all_time", bs_entry(m_best_difficulty.all_time, m_best_difficulty.all_time_miner,
3825+
m_best_difficulty.all_time_hash, m_best_difficulty.all_time_ts)}
3826+
};
3827+
}
3828+
38063829
// p2pool-compat: version and protocol_version
38073830
result["version"] = m_pool_version;
38083831
result["protocol_version"] = m_protocol_version.load(std::memory_order_relaxed);
@@ -4746,23 +4769,28 @@ nlohmann::json MiningInterface::rest_best_share()
47464769
best_round = avg;
47474770
}
47484771

4749-
auto make_entry = [&](double diff, const std::string& miner, uint64_t ts) {
4772+
auto make_entry = [&](double diff, const std::string& miner, uint64_t ts,
4773+
const std::string& hash) {
47504774
nlohmann::json e;
47514775
e["difficulty"] = diff;
47524776
e["pct_of_block"] = (net_diff > 0) ? (diff / net_diff * 100.0) : 0.0;
47534777
e["miner"] = miner;
47544778
e["timestamp"] = ts;
4779+
e["hash"] = hash; // pow-hash of the record share (empty until wired)
47554780
return e;
47564781
};
47574782

47584783
result["all_time"] = make_entry(best_all,
4759-
m_best_difficulty.all_time_miner, m_best_difficulty.all_time_ts);
4784+
m_best_difficulty.all_time_miner, m_best_difficulty.all_time_ts,
4785+
m_best_difficulty.all_time_hash);
47604786
auto session = make_entry(best_sess,
4761-
m_best_difficulty.session_miner, m_best_difficulty.session_ts);
4787+
m_best_difficulty.session_miner, m_best_difficulty.session_ts,
4788+
m_best_difficulty.session_hash);
47624789
session["started"] = m_start_time.time_since_epoch().count() / 1000000000ULL;
47634790
result["session"] = session;
47644791
auto round = make_entry(best_round,
4765-
m_best_difficulty.miner, m_best_difficulty.timestamp);
4792+
m_best_difficulty.miner, m_best_difficulty.timestamp,
4793+
m_best_difficulty.hash);
47664794
round["started"] = m_best_difficulty.round_start;
47674795
result["round"] = round;
47684796
}
@@ -6402,7 +6430,8 @@ nlohmann::json MiningInterface::rest_web_graph_data(const std::string& source, c
64026430

64036431
// ──────────── Difficulty tracking and stat log helpers ────────────────────
64046432

6405-
void MiningInterface::record_share_difficulty(double difficulty, const std::string& miner)
6433+
void MiningInterface::record_share_difficulty(double difficulty, const std::string& miner,
6434+
const std::string& share_hash)
64066435
{
64076436
// Feed the rolling-hashrate ring. Independent of the best-diff
64086437
// tracking below; the ring has its own internal lock.
@@ -6414,16 +6443,19 @@ void MiningInterface::record_share_difficulty(double difficulty, const std::stri
64146443
if (difficulty > m_best_difficulty.all_time) {
64156444
m_best_difficulty.all_time = difficulty;
64166445
m_best_difficulty.all_time_miner = miner;
6446+
m_best_difficulty.all_time_hash = share_hash;
64176447
m_best_difficulty.all_time_ts = now_ts;
64186448
}
64196449
if (difficulty > m_best_difficulty.session) {
64206450
m_best_difficulty.session = difficulty;
64216451
m_best_difficulty.session_miner = miner;
6452+
m_best_difficulty.session_hash = share_hash;
64226453
m_best_difficulty.session_ts = now_ts;
64236454
}
64246455
if (difficulty > m_best_difficulty.round) {
64256456
m_best_difficulty.round = difficulty;
64266457
m_best_difficulty.miner = miner;
6458+
m_best_difficulty.hash = share_hash;
64276459
m_best_difficulty.timestamp = now_ts;
64286460
}
64296461
}

src/core/web_server.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1364,7 +1364,11 @@ class MiningInterface : public jsonrpccxx::JsonRpc2Server,
13641364
void auto_detect_external_info();
13651365

13661366
// Best share difficulty tracking (for /best_share, /miner_stats)
1367-
void record_share_difficulty(double difficulty, const std::string& miner);
1367+
// share_hash: optional pow-hash of the record-setting share (display only;
1368+
// DASH stratum submit path supplies it so the Best Share card can show the
1369+
// exact hash that got closest to net difficulty).
1370+
void record_share_difficulty(double difficulty, const std::string& miner,
1371+
const std::string& share_hash = "");
13681372
void record_merged_share_difficulty(double difficulty, const std::string& miner);
13691373

13701374
// Stat log entry (appended every 5 minutes, rolling 24h window for /web/log JSON)
@@ -1426,12 +1430,15 @@ class MiningInterface : public jsonrpccxx::JsonRpc2Server,
14261430
struct BestDifficulty {
14271431
double all_time{0.0};
14281432
std::string all_time_miner;
1433+
std::string all_time_hash; // pow-hash of the record share (display only)
14291434
uint64_t all_time_ts{0};
14301435
double session{0.0};
14311436
std::string session_miner;
1437+
std::string session_hash;
14321438
uint64_t session_ts{0};
14331439
double round{0.0};
14341440
std::string miner; // round-level miner (backward compat)
1441+
std::string hash; // round-level pow-hash (display only)
14351442
uint64_t timestamp{0}; // round-level timestamp (backward compat)
14361443
uint64_t round_start{0};
14371444
// Merged chain (DOGE) best share tracking

src/impl/dash/node.hpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,30 @@ class NodeImpl : public pool::BaseNode<dash::Config, dash::ShareChain, dash::Pee
620620
/// above, next to send_version.)
621621
uint256 best_share_hash();
622622

623+
/// Pool-peer info for the dashboard node-status card (ltc node.hpp:302
624+
/// parity). Display only — MiningInterface::set_peer_info_fn feeds
625+
/// /local_stats {peers:{incoming,outgoing}} and the peer table. Read-only
626+
/// over the live m_peers map; touches no share/target/payout state.
627+
nlohmann::json get_peer_info_json() const {
628+
nlohmann::json arr = nlohmann::json::array();
629+
for (const auto& [nonce, peer] : m_peers) {
630+
if (!peer) continue;
631+
auto addr = peer->addr();
632+
bool incoming = (m_outbound_addrs.find(addr) == m_outbound_addrs.end());
633+
auto uptime_sec = std::chrono::duration_cast<std::chrono::seconds>(
634+
std::chrono::steady_clock::now() - peer->m_connected_at).count();
635+
arr.push_back({
636+
{"address", addr.to_string()},
637+
{"version", peer->m_other_subversion},
638+
{"incoming", incoming},
639+
{"uptime", uptime_sec},
640+
{"downtime", 0},
641+
{"web_port", 0}
642+
});
643+
}
644+
return arr;
645+
}
646+
623647
/// Relay a share (and up to 4 un-broadcast ancestors) to all peers via
624648
/// the message_shares wire codec (+ remember_tx/forget_tx for txs the
625649
/// peer lacks). try_to_lock; deferred if the compute thread is busy.

0 commit comments

Comments
 (0)