mirror of
https://github.com/SChernykh/p2pool.git
synced 2024-11-17 00:07:47 +00:00
API for stratum server statistics.
Users can pass `--stratum-api` to command line to enable a file called `local/stats` in `--data-api` directory. `local/stats` contains statistics that `StratumServer status` provides but in JSON format. It is currently setup that `local/stats` is updated each time a new job is sent to a worker.
This commit is contained in:
parent
2a3cd13b19
commit
7742d163f7
9 changed files with 110 additions and 8 deletions
|
@ -36,6 +36,7 @@ static void usage()
|
||||||
"--loglevel Verbosity of the log, integer number between 0 and 5\n"
|
"--loglevel Verbosity of the log, integer number between 0 and 5\n"
|
||||||
"--config Name of the p2pool config file\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"
|
"--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"
|
"--help Show this help message\n\n"
|
||||||
"Example command line:\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",
|
"%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",
|
||||||
|
|
|
@ -103,7 +103,7 @@ p2pool::p2pool(int argc, char* argv[])
|
||||||
uv_mutex_init_checked(&m_foundBlocksLock);
|
uv_mutex_init_checked(&m_foundBlocksLock);
|
||||||
uv_mutex_init_checked(&m_submitBlockDataLock);
|
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_sideChain = new SideChain(this, type);
|
||||||
m_hasher = new RandomX_Hasher(this);
|
m_hasher = new RandomX_Hasher(this);
|
||||||
|
|
11
src/p2pool.h
11
src/p2pool.h
|
@ -49,6 +49,8 @@ public:
|
||||||
SideChain& side_chain() { return *m_sideChain; }
|
SideChain& side_chain() { return *m_sideChain; }
|
||||||
const MinerData& miner_data() const { return m_minerData; }
|
const MinerData& miner_data() const { return m_minerData; }
|
||||||
|
|
||||||
|
p2pool_api* m_api;
|
||||||
|
|
||||||
RandomX_Hasher* hasher() const { return m_hasher; }
|
RandomX_Hasher* hasher() const { return m_hasher; }
|
||||||
bool calculate_hash(const void* data, size_t size, const hash& seed, hash& result);
|
bool calculate_hash(const void* data, size_t size, const hash& seed, hash& result);
|
||||||
static uint64_t get_seed_height(uint64_t height);
|
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_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);
|
bool get_difficulty_at_height(uint64_t height, difficulty_type& diff);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -90,7 +96,6 @@ private:
|
||||||
|
|
||||||
Params* m_params;
|
Params* m_params;
|
||||||
|
|
||||||
p2pool_api* m_api;
|
|
||||||
SideChain* m_sideChain;
|
SideChain* m_sideChain;
|
||||||
RandomX_Hasher* m_hasher;
|
RandomX_Hasher* m_hasher;
|
||||||
BlockTemplate* m_blockTemplate;
|
BlockTemplate* m_blockTemplate;
|
||||||
|
@ -121,10 +126,6 @@ private:
|
||||||
bool parse_block_header(const char* data, size_t size, ChainMain& result);
|
bool parse_block_header(const char* data, size_t size, ChainMain& result);
|
||||||
uint32_t parse_block_headers_range(const char* data, size_t size);
|
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
|
struct FoundBlock
|
||||||
{
|
{
|
||||||
FORCEINLINE FoundBlock(time_t _t, uint64_t _h, const hash& _id, const difficulty_type& _block_diff, const difficulty_type& _total_hashes)
|
FORCEINLINE FoundBlock(time_t _t, uint64_t _h, const hash& _id, const difficulty_type& _block_diff, const difficulty_type& _total_hashes)
|
||||||
|
|
|
@ -28,7 +28,7 @@ static constexpr char log_category_prefix[] = "P2Pool API ";
|
||||||
|
|
||||||
namespace p2pool {
|
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()) {
|
if (m_apiPath.empty()) {
|
||||||
LOGERR(1, "api path is 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_networkPath = m_apiPath + "network/";
|
||||||
m_poolPath = m_apiPath + "pool/";
|
m_poolPath = m_apiPath + "pool/";
|
||||||
|
m_localPath = m_apiPath + "local/";
|
||||||
|
|
||||||
create_dir(m_networkPath);
|
create_dir(m_networkPath);
|
||||||
create_dir(m_poolPath);
|
create_dir(m_poolPath);
|
||||||
|
|
||||||
|
if (local_stats) {
|
||||||
|
create_dir(m_localPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p2pool_api::~p2pool_api()
|
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::GLOBAL: path = m_apiPath + filename; break;
|
||||||
case Category::NETWORK: path = m_networkPath + filename; break;
|
case Category::NETWORK: path = m_networkPath + filename; break;
|
||||||
case Category::POOL: path = m_poolPath + filename; break;
|
case Category::POOL: path = m_poolPath + filename; break;
|
||||||
|
case Category::LOCAL: path = m_localPath + filename; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,13 +25,14 @@ namespace p2pool {
|
||||||
class p2pool_api
|
class p2pool_api
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit p2pool_api(const std::string& api_path);
|
p2pool_api(const std::string& api_path, const bool local_stats);
|
||||||
~p2pool_api();
|
~p2pool_api();
|
||||||
|
|
||||||
enum class Category {
|
enum class Category {
|
||||||
GLOBAL,
|
GLOBAL,
|
||||||
NETWORK,
|
NETWORK,
|
||||||
POOL,
|
POOL,
|
||||||
|
LOCAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
void on_stop();
|
void on_stop();
|
||||||
|
@ -80,6 +81,7 @@ private:
|
||||||
std::string m_apiPath;
|
std::string m_apiPath;
|
||||||
std::string m_networkPath;
|
std::string m_networkPath;
|
||||||
std::string m_poolPath;
|
std::string m_poolPath;
|
||||||
|
std::string m_localPath;
|
||||||
|
|
||||||
uv_mutex_t m_dumpDataLock;
|
uv_mutex_t m_dumpDataLock;
|
||||||
std::unordered_map<std::string, std::vector<char>> m_dumpData;
|
std::unordered_map<std::string, std::vector<char>> m_dumpData;
|
||||||
|
|
|
@ -69,6 +69,10 @@ Params::Params(int argc, char* argv[])
|
||||||
if ((strcmp(argv[i], "--data-api") == 0) && (i + 1 < argc)) {
|
if ((strcmp(argv[i], "--data-api") == 0) && (i + 1 < argc)) {
|
||||||
m_apiPath = argv[++i];
|
m_apiPath = argv[++i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcmp(argv[i], "--stratum-api") == 0) {
|
||||||
|
m_localStats = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_stratumAddresses.empty()) {
|
if (m_stratumAddresses.empty()) {
|
||||||
|
|
|
@ -37,6 +37,7 @@ struct Params
|
||||||
std::string m_p2pPeerList;
|
std::string m_p2pPeerList;
|
||||||
std::string m_config;
|
std::string m_config;
|
||||||
std::string m_apiPath;
|
std::string m_apiPath;
|
||||||
|
bool m_localStats = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace p2pool
|
} // namespace p2pool
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "p2pool.h"
|
#include "p2pool.h"
|
||||||
#include "side_chain.h"
|
#include "side_chain.h"
|
||||||
#include "params.h"
|
#include "params.h"
|
||||||
|
#include "p2pool_api.h"
|
||||||
|
|
||||||
static constexpr char log_category_prefix[] = "StratumServer ";
|
static constexpr char log_category_prefix[] = "StratumServer ";
|
||||||
|
|
||||||
|
@ -48,6 +49,7 @@ StratumServer::StratumServer(p2pool* pool)
|
||||||
, m_hashrateDataTail_24h(0)
|
, m_hashrateDataTail_24h(0)
|
||||||
, m_cumulativeFoundSharesDiff(0.0)
|
, m_cumulativeFoundSharesDiff(0.0)
|
||||||
, m_totalFoundShares(0)
|
, m_totalFoundShares(0)
|
||||||
|
, m_apiLastUpdateTime(-1)
|
||||||
{
|
{
|
||||||
m_hashrateData[0] = { time(nullptr), 0 };
|
m_hashrateData[0] = { time(nullptr), 0 };
|
||||||
|
|
||||||
|
@ -645,6 +647,7 @@ void StratumServer::on_share_found(uv_work_t* req)
|
||||||
|
|
||||||
if (LIKELY(value < target)) {
|
if (LIKELY(value < target)) {
|
||||||
server->update_hashrate_data(target);
|
server->update_hashrate_data(target);
|
||||||
|
server->api_update_local_stats();
|
||||||
share->m_result = SubmittedShare::Result::OK;
|
share->m_result = SubmittedShare::Result::OK;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -914,4 +917,84 @@ bool StratumServer::StratumClient::process_submit(rapidjson::Document& doc, uint
|
||||||
return static_cast<StratumServer*>(m_owner)->on_submit(this, id, job_id.GetString(), nonce.GetString(), result.GetString());
|
return static_cast<StratumServer*>(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<int64_t>(head.m_timestamp - tail_15m.m_timestamp);
|
||||||
|
|
||||||
|
hashes_1h = head.m_cumulativeHashes - tail_1h.m_cumulativeHashes;
|
||||||
|
dt_1h = static_cast<int64_t>(head.m_timestamp - tail_1h.m_timestamp);
|
||||||
|
|
||||||
|
hashes_24h = head.m_cumulativeHashes - tail_24h.m_cumulativeHashes;
|
||||||
|
dt_24h = static_cast<int64_t>(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<double>(m_cumulativeHashesAtLastShare) * 100.0 / m_cumulativeFoundSharesDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
int shares_found = m_totalFoundShares;
|
||||||
|
|
||||||
|
double current_effort = static_cast<double>(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
|
} // namespace p2pool
|
||||||
|
|
|
@ -152,7 +152,11 @@ private:
|
||||||
double m_cumulativeFoundSharesDiff;
|
double m_cumulativeFoundSharesDiff;
|
||||||
uint32_t m_totalFoundShares;
|
uint32_t m_totalFoundShares;
|
||||||
|
|
||||||
|
time_t m_apiLastUpdateTime;
|
||||||
|
|
||||||
void update_hashrate_data(uint64_t target);
|
void update_hashrate_data(uint64_t target);
|
||||||
|
|
||||||
|
void api_update_local_stats();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace p2pool
|
} // namespace p2pool
|
||||||
|
|
Loading…
Reference in a new issue