diff --git a/src/main.cpp b/src/main.cpp index 801442e..339a02e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,6 +36,7 @@ static void usage() "--loglevel Verbosity of the log, integer number between 0 and 5\n" "--config Name of the p2pool config file\n" "--data-api Path to the p2pool JSON data (use it in tandem with an external web-server)\n" + "--stratum-api Enable /local/ path in api path for Stratum Server statistics\n" "--help Show this help message\n\n" "Example command line:\n\n" "%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n", diff --git a/src/p2pool.cpp b/src/p2pool.cpp index b1b0246..cdb3b8d 100644 --- a/src/p2pool.cpp +++ b/src/p2pool.cpp @@ -103,7 +103,7 @@ p2pool::p2pool(int argc, char* argv[]) uv_mutex_init_checked(&m_foundBlocksLock); uv_mutex_init_checked(&m_submitBlockDataLock); - m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath); + m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath, m_params->m_localStats); m_sideChain = new SideChain(this, type); m_hasher = new RandomX_Hasher(this); diff --git a/src/p2pool.h b/src/p2pool.h index e1f548a..b19aa1f 100644 --- a/src/p2pool.h +++ b/src/p2pool.h @@ -49,6 +49,8 @@ public: SideChain& side_chain() { return *m_sideChain; } const MinerData& miner_data() const { return m_minerData; } + p2pool_api* m_api; + RandomX_Hasher* hasher() const { return m_hasher; } bool calculate_hash(const void* data, size_t size, const hash& seed, hash& result); static uint64_t get_seed_height(uint64_t height); @@ -74,6 +76,10 @@ public: void api_update_block_found(const ChainMain* data); + void api_update_network_stats(); + void api_update_pool_stats(); + void api_update_stats_mod(); + bool get_difficulty_at_height(uint64_t height, difficulty_type& diff); private: @@ -90,7 +96,6 @@ private: Params* m_params; - p2pool_api* m_api; SideChain* m_sideChain; RandomX_Hasher* m_hasher; BlockTemplate* m_blockTemplate; @@ -121,10 +126,6 @@ private: bool parse_block_header(const char* data, size_t size, ChainMain& result); uint32_t parse_block_headers_range(const char* data, size_t size); - void api_update_network_stats(); - void api_update_pool_stats(); - void api_update_stats_mod(); - struct FoundBlock { FORCEINLINE FoundBlock(time_t _t, uint64_t _h, const hash& _id, const difficulty_type& _block_diff, const difficulty_type& _total_hashes) diff --git a/src/p2pool_api.cpp b/src/p2pool_api.cpp index 22758d3..fdb9433 100644 --- a/src/p2pool_api.cpp +++ b/src/p2pool_api.cpp @@ -28,7 +28,7 @@ static constexpr char log_category_prefix[] = "P2Pool API "; namespace p2pool { -p2pool_api::p2pool_api(const std::string& api_path) : m_apiPath(api_path) +p2pool_api::p2pool_api(const std::string& api_path, const bool local_stats): m_apiPath(api_path) { if (m_apiPath.empty()) { LOGERR(1, "api path is empty"); @@ -60,9 +60,14 @@ p2pool_api::p2pool_api(const std::string& api_path) : m_apiPath(api_path) m_networkPath = m_apiPath + "network/"; m_poolPath = m_apiPath + "pool/"; + m_localPath = m_apiPath + "local/"; create_dir(m_networkPath); create_dir(m_poolPath); + + if (local_stats) { + create_dir(m_localPath); + } } p2pool_api::~p2pool_api() @@ -109,6 +114,7 @@ void p2pool_api::dump_to_file_async_internal(const Category& category, const cha case Category::GLOBAL: path = m_apiPath + filename; break; case Category::NETWORK: path = m_networkPath + filename; break; case Category::POOL: path = m_poolPath + filename; break; + case Category::LOCAL: path = m_localPath + filename; break; } { diff --git a/src/p2pool_api.h b/src/p2pool_api.h index de3efbd..203215e 100644 --- a/src/p2pool_api.h +++ b/src/p2pool_api.h @@ -25,13 +25,14 @@ namespace p2pool { class p2pool_api { public: - explicit p2pool_api(const std::string& api_path); + p2pool_api(const std::string& api_path, const bool local_stats); ~p2pool_api(); enum class Category { GLOBAL, NETWORK, POOL, + LOCAL, }; void on_stop(); @@ -80,6 +81,7 @@ private: std::string m_apiPath; std::string m_networkPath; std::string m_poolPath; + std::string m_localPath; uv_mutex_t m_dumpDataLock; std::unordered_map> m_dumpData; diff --git a/src/params.cpp b/src/params.cpp index 7e0eb7f..ae8e71b 100644 --- a/src/params.cpp +++ b/src/params.cpp @@ -69,6 +69,10 @@ Params::Params(int argc, char* argv[]) if ((strcmp(argv[i], "--data-api") == 0) && (i + 1 < argc)) { m_apiPath = argv[++i]; } + + if (strcmp(argv[i], "--stratum-api") == 0) { + m_localStats = true; + } } if (m_stratumAddresses.empty()) { diff --git a/src/params.h b/src/params.h index b38c299..afb7a98 100644 --- a/src/params.h +++ b/src/params.h @@ -37,6 +37,7 @@ struct Params std::string m_p2pPeerList; std::string m_config; std::string m_apiPath; + bool m_localStats = false; }; } // namespace p2pool diff --git a/src/stratum_server.cpp b/src/stratum_server.cpp index 8bda6d5..f12af8e 100644 --- a/src/stratum_server.cpp +++ b/src/stratum_server.cpp @@ -21,6 +21,7 @@ #include "p2pool.h" #include "side_chain.h" #include "params.h" +#include "p2pool_api.h" static constexpr char log_category_prefix[] = "StratumServer "; @@ -48,6 +49,7 @@ StratumServer::StratumServer(p2pool* pool) , m_hashrateDataTail_24h(0) , m_cumulativeFoundSharesDiff(0.0) , m_totalFoundShares(0) + , m_apiLastUpdateTime(-1) { m_hashrateData[0] = { time(nullptr), 0 }; @@ -645,6 +647,7 @@ void StratumServer::on_share_found(uv_work_t* req) if (LIKELY(value < target)) { server->update_hashrate_data(target); + server->api_update_local_stats(); share->m_result = SubmittedShare::Result::OK; } else { @@ -914,4 +917,84 @@ bool StratumServer::StratumClient::process_submit(rapidjson::Document& doc, uint return static_cast(m_owner)->on_submit(this, id, job_id.GetString(), nonce.GetString(), result.GetString()); } +void StratumServer::api_update_local_stats() +{ + if (!m_pool->m_api) { + return; + } + + if (!m_pool->params().m_localStats) { + return; + } + + const time_t api_time_now = time(nullptr); + + // Rate limit to no more than once in 60 seconds. + if (m_apiLastUpdateTime == -1) { + m_apiLastUpdateTime = api_time_now; + } else if (difftime(api_time_now, m_apiLastUpdateTime) < 60) { + return; + } else { + m_apiLastUpdateTime = api_time_now; + } + + uint64_t hashes_15m, hashes_1h, hashes_24h, total_hashes; + int64_t dt_15m, dt_1h, dt_24h; + + uint64_t hashes_since_last_share; + + { + ReadLock lock(m_hashrateDataLock); + + total_hashes = m_cumulativeHashes; + hashes_since_last_share = m_cumulativeHashes - m_cumulativeHashesAtLastShare; + + const HashrateData* data = m_hashrateData; + const HashrateData& head = data[m_hashrateDataHead]; + const HashrateData& tail_15m = data[m_hashrateDataTail_15m]; + const HashrateData& tail_1h = data[m_hashrateDataTail_1h]; + const HashrateData& tail_24h = data[m_hashrateDataTail_24h]; + + hashes_15m = head.m_cumulativeHashes - tail_15m.m_cumulativeHashes; + dt_15m = static_cast(head.m_timestamp - tail_15m.m_timestamp); + + hashes_1h = head.m_cumulativeHashes - tail_1h.m_cumulativeHashes; + dt_1h = static_cast(head.m_timestamp - tail_1h.m_timestamp); + + hashes_24h = head.m_cumulativeHashes - tail_24h.m_cumulativeHashes; + dt_24h = static_cast(head.m_timestamp - tail_24h.m_timestamp); + } + + const uint64_t hashrate_15m = (dt_15m > 0) ? (hashes_15m / dt_15m) : 0; + const uint64_t hashrate_1h = (dt_1h > 0) ? (hashes_1h / dt_1h ) : 0; + const uint64_t hashrate_24h = (dt_24h > 0) ? (hashes_24h / dt_24h) : 0; + + double average_effort = 0.0; + if (m_cumulativeFoundSharesDiff > 0.0) { + average_effort = static_cast(m_cumulativeHashesAtLastShare) * 100.0 / m_cumulativeFoundSharesDiff; + } + + int shares_found = m_totalFoundShares; + + double current_effort = static_cast(hashes_since_last_share) * 100.0 / m_pool->side_chain().difficulty().to_double(); + + int connections = m_numConnections; + int incoming_connections = m_numIncomingConnections; + + m_pool->m_api->set(p2pool_api::Category::LOCAL, "stats", + [hashrate_15m, hashrate_1h, hashrate_24h, total_hashes, shares_found, average_effort, current_effort, connections, incoming_connections](log::Stream& s) + { + s << "{\"hashrate_15m\":" << hashrate_15m + << ",\"hashrate_1h\":" << hashrate_1h + << ",\"hashrate_24h\":" << hashrate_24h + << ",\"total_hashes\":" << total_hashes + << ",\"shares_found\":" << shares_found + << ",\"average_effort\":" << average_effort + << ",\"current_effort\":" << current_effort + << ",\"connections\":" << connections + << ",\"incoming_connections\":" << incoming_connections + << "}"; + }); +} + } // namespace p2pool diff --git a/src/stratum_server.h b/src/stratum_server.h index e825695..b498a1b 100644 --- a/src/stratum_server.h +++ b/src/stratum_server.h @@ -152,7 +152,11 @@ private: double m_cumulativeFoundSharesDiff; uint32_t m_totalFoundShares; + time_t m_apiLastUpdateTime; + void update_hashrate_data(uint64_t target); + + void api_update_local_stats(); }; } // namespace p2pool