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
52 changes: 52 additions & 0 deletions src/c2pool/main_dash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,16 @@ int run_node(bool testnet, const std::string& rpc_endpoint,
// ── REAL best-share hash ──────────────────────────────────────
web_server->set_best_share_hash_fn(
[node_ptr]() { return node_ptr->best_share_hash(); });

// ── REAL pool-peer info (node-status card) ────────────────────
// /local_stats {peers:{incoming,outgoing}} + the peer table read
// this. Without it the node-status card reported 0 peers on DASH
// (the m_node fallback in rest_local_stats never sees the pool
// p2p peers). Same shape main_ltc.cpp:2830 uses. Display only.
mi->set_peer_info_fn(
[&p2p_node]() -> nlohmann::json {
return p2p_node.get_peer_info_json();
});
}

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

// ── REAL best-share feed for the dashboard "Best Share" card ──────
// Every ACCEPTED stratum submit reports the actual PoW difficulty of
// the found hash (target_to_difficulty(pow_hash)) + miner + pow-hash.
// record_share_difficulty tracks the pool-wide/session/round max WITH
// the hash + timestamp; /best_share + /local_stats render it. This is
// the PRIMARY best-share feed on the DASH solo path — the tracker's
// verified-share m_on_share_difficulty hook (wired above) almost never
// fires here because solo shares seldom mint onto the sharechain, so
// without this the 🎯 Best Share card sat empty. Display only; the
// callback touches no share/target/payout logic (consensus-neutral).
if (web_server) {
core::WebServer* ws = web_server.get();
work_source->set_on_share_difficulty_fn(
[ws](double diff, const std::string& miner, const uint256& pow_hash) {
ws->get_mining_interface()->record_share_difficulty(
diff, miner, pow_hash.GetHex());
});

// ── Recent-blocks feed: DASH block wins into the history card ──
// Every dispatched block solution records to /recent_blocks with
// the height, X11 block hash (== pow_hash for DASH), miner, and the
// net difficulty at find time. Without this DASH block wins never
// appeared on the recent-blocks card (main_ltc.cpp:2970 parity).
// Display only; the callback runs after dispatch, never gates it.
work_source->set_on_found_block_fn(
[ws](uint32_t height, const uint256& block_hash,
const std::string& miner, bool reached_network) {
auto* mi = ws->get_mining_interface();
mi->record_found_block(
height, block_hash,
static_cast<uint64_t>(std::time(nullptr)),
"DASH", miner, block_hash.GetHex(),
mi->get_network_difficulty(),
/*share_difficulty=*/0.0,
/*pool_hashrate=*/0.0,
/*subsidy=*/0);
if (!reached_network)
LOG_WARNING << "[DASH] recorded found block height="
<< height << " that reached NO network sink";
});
}

// Producer job: the stratum coinbase IS the producer share gentx
// (byte-parity with the mint-time rebuild by construction). The
// frozen per-job context is registered under its ref_hash.
Expand Down
14 changes: 14 additions & 0 deletions src/core/stratum_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,20 @@ nlohmann::json StratumSession::handle_submit(const nlohmann::json& params, const
// Record in per-connection tracker (for VARDIFF adjustment + per-session stats)
hashrate_tracker_.record_mining_share_submission(credited_difficulty, true);

// Best-share dashboard feed: every ACCEPTED pseudoshare's real (X11/scrypt)
// difficulty is a candidate for the "Best Share" card (highest-difficulty
// share seen — how close a miner got to a block). Default no-op; a work
// source whose dashboard is a separate object (c2pool-dash) overrides this
// to forward into the dashboard's best-share tracker. share_difficulty is
// the true per-hash difficulty computed above; pass the raw submit fields so
// the implementor can recover the exact pow-hash. Display only.
mining_interface_->record_best_pseudoshare(
share_difficulty, username_,
job.payload->coinb1, job.payload->coinb2,
extranonce1_, extranonce2, ntime, nonce,
effective_version, job.gbt_prevhash, job.nbits,
job.payload->merkle_branches);

// Check if VARDIFF changed → send new difficulty AND new work to miner.
// p2pool (stratum.py:594-595): after adjusting target, calls _send_work()
// which sends both set_difficulty and mining.notify. Without new work,
Expand Down
19 changes: 19 additions & 0 deletions src/core/stratum_work_source.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,25 @@ class IWorkSource {
uint32_t version, const std::string& prevhash_hex,
const std::string& nbits_hex,
const std::vector<std::string>& merkle_branches) const = 0;

/// Record an ACCEPTED pseudoshare for the dashboard "Best Share" card
/// (the highest-difficulty share seen — how close a miner got to a block).
/// The stratum session invokes this for EVERY accepted pseudoshare (the
/// vardiff-gate acceptance path), passing the already-computed X11/scrypt
/// share difficulty plus the raw submit fields so an implementor that wants
/// the exact pow-hash can recompute it. Default no-op — only work sources
/// whose dashboard is a separate object (c2pool-dash: the DASHWorkSource
/// drives its own stratum acceptor, distinct from the WebServer's
/// MiningInterface) override this to forward to the dashboard's best-share
/// tracker. Display only — never affects acceptance, target, or payout.
virtual void record_best_pseudoshare(
double /*share_difficulty*/, const std::string& /*miner*/,
const std::string& /*coinb1*/, const std::string& /*coinb2*/,
const std::string& /*extranonce1*/, const std::string& /*extranonce2*/,
const std::string& /*ntime*/, const std::string& /*nonce*/,
uint32_t /*version*/, const std::string& /*prevhash_hex*/,
const std::string& /*nbits_hex*/,
const std::vector<std::string>& /*merkle_branches*/) {}
};

} // namespace core::stratum
42 changes: 37 additions & 5 deletions src/core/web_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4667,6 +4667,29 @@ nlohmann::json MiningInterface::rest_local_stats()
}
result["miner_last_difficulties"] = miner_diffs;

// Best-share summary (highest-difficulty pseudoshare seen). Mirrors the
// dedicated /best_share endpoint so /local_stats consumers get the "how
// close did the best share get to a block" metric inline. Display only.
{
std::lock_guard<std::mutex> lock(m_best_diff_mutex);
auto pct = [&](double d) { return net_diff > 0 ? d / net_diff * 100.0 : 0.0; };
auto bs_entry = [&](double diff, const std::string& miner,
const std::string& hash, uint64_t ts) {
return nlohmann::json{
{"difficulty", diff}, {"pct_of_block", pct(diff)},
{"miner", miner}, {"hash", hash}, {"timestamp", ts}};
};
result["best_share"] = {
{"network_difficulty", net_diff},
{"round", bs_entry(m_best_difficulty.round, m_best_difficulty.miner,
m_best_difficulty.hash, m_best_difficulty.timestamp)},
{"session", bs_entry(m_best_difficulty.session, m_best_difficulty.session_miner,
m_best_difficulty.session_hash, m_best_difficulty.session_ts)},
{"all_time", bs_entry(m_best_difficulty.all_time, m_best_difficulty.all_time_miner,
m_best_difficulty.all_time_hash, m_best_difficulty.all_time_ts)}
};
}

// p2pool-compat: version and protocol_version
result["version"] = m_pool_version;
result["protocol_version"] = m_protocol_version.load(std::memory_order_relaxed);
Expand Down Expand Up @@ -5610,23 +5633,28 @@ nlohmann::json MiningInterface::rest_best_share()
best_round = avg;
}

auto make_entry = [&](double diff, const std::string& miner, uint64_t ts) {
auto make_entry = [&](double diff, const std::string& miner, uint64_t ts,
const std::string& hash) {
nlohmann::json e;
e["difficulty"] = diff;
e["pct_of_block"] = (net_diff > 0) ? (diff / net_diff * 100.0) : 0.0;
e["miner"] = miner;
e["timestamp"] = ts;
e["hash"] = hash; // pow-hash of the record share (empty until wired)
return e;
};

result["all_time"] = make_entry(best_all,
m_best_difficulty.all_time_miner, m_best_difficulty.all_time_ts);
m_best_difficulty.all_time_miner, m_best_difficulty.all_time_ts,
m_best_difficulty.all_time_hash);
auto session = make_entry(best_sess,
m_best_difficulty.session_miner, m_best_difficulty.session_ts);
m_best_difficulty.session_miner, m_best_difficulty.session_ts,
m_best_difficulty.session_hash);
session["started"] = m_start_time.time_since_epoch().count() / 1000000000ULL;
result["session"] = session;
auto round = make_entry(best_round,
m_best_difficulty.miner, m_best_difficulty.timestamp);
m_best_difficulty.miner, m_best_difficulty.timestamp,
m_best_difficulty.hash);
round["started"] = m_best_difficulty.round_start;
result["round"] = round;
}
Expand Down Expand Up @@ -7266,7 +7294,8 @@ nlohmann::json MiningInterface::rest_web_graph_data(const std::string& source, c

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

void MiningInterface::record_share_difficulty(double difficulty, const std::string& miner)
void MiningInterface::record_share_difficulty(double difficulty, const std::string& miner,
const std::string& share_hash)
{
// Feed the rolling-hashrate ring. Independent of the best-diff
// tracking below; the ring has its own internal lock.
Expand All @@ -7278,16 +7307,19 @@ void MiningInterface::record_share_difficulty(double difficulty, const std::stri
if (difficulty > m_best_difficulty.all_time) {
m_best_difficulty.all_time = difficulty;
m_best_difficulty.all_time_miner = miner;
m_best_difficulty.all_time_hash = share_hash;
m_best_difficulty.all_time_ts = now_ts;
}
if (difficulty > m_best_difficulty.session) {
m_best_difficulty.session = difficulty;
m_best_difficulty.session_miner = miner;
m_best_difficulty.session_hash = share_hash;
m_best_difficulty.session_ts = now_ts;
}
if (difficulty > m_best_difficulty.round) {
m_best_difficulty.round = difficulty;
m_best_difficulty.miner = miner;
m_best_difficulty.hash = share_hash;
m_best_difficulty.timestamp = now_ts;
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/core/web_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1364,7 +1364,11 @@ class MiningInterface : public jsonrpccxx::JsonRpc2Server,
void auto_detect_external_info();

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

// Stat log entry (appended every 5 minutes, rolling 24h window for /web/log JSON)
Expand Down Expand Up @@ -1426,12 +1430,15 @@ class MiningInterface : public jsonrpccxx::JsonRpc2Server,
struct BestDifficulty {
double all_time{0.0};
std::string all_time_miner;
std::string all_time_hash; // pow-hash of the record share (display only)
uint64_t all_time_ts{0};
double session{0.0};
std::string session_miner;
std::string session_hash;
uint64_t session_ts{0};
double round{0.0};
std::string miner; // round-level miner (backward compat)
std::string hash; // round-level pow-hash (display only)
uint64_t timestamp{0}; // round-level timestamp (backward compat)
uint64_t round_start{0};
// Merged chain (DOGE) best share tracking
Expand Down
24 changes: 24 additions & 0 deletions src/impl/dash/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,30 @@ class NodeImpl : public pool::BaseNode<dash::Config, dash::ShareChain, dash::Pee
/// above, next to send_version.)
uint256 best_share_hash();

/// Pool-peer info for the dashboard node-status card (ltc node.hpp:302
/// parity). Display only — MiningInterface::set_peer_info_fn feeds
/// /local_stats {peers:{incoming,outgoing}} and the peer table. Read-only
/// over the live m_peers map; touches no share/target/payout state.
nlohmann::json get_peer_info_json() const {
nlohmann::json arr = nlohmann::json::array();
for (const auto& [nonce, peer] : m_peers) {
if (!peer) continue;
auto addr = peer->addr();
bool incoming = (m_outbound_addrs.find(addr) == m_outbound_addrs.end());
auto uptime_sec = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::steady_clock::now() - peer->m_connected_at).count();
arr.push_back({
{"address", addr.to_string()},
{"version", peer->m_other_subversion},
{"incoming", incoming},
{"uptime", uptime_sec},
{"downtime", 0},
{"web_port", 0}
});
}
return arr;
}

/// Relay a share (and up to 4 un-broadcast ancestors) to all peers via
/// the message_shares wire codec (+ remember_tx/forget_tx for txs the
/// peer lacks). try_to_lock; deferred if the compute thread is busy.
Expand Down
Loading
Loading