Better handling of unexpected pool stats responses, add CKPool

This commit is contained in:
Djuri Baars 2024-12-28 13:40:08 +01:00
parent 10fe5b5053
commit b7ff9d8101
10 changed files with 210 additions and 57 deletions

View file

@ -1,32 +1,43 @@
#include "brains_pool.hpp"
void BraiinsPool::prepareRequest(HTTPClient& http) const {
void BraiinsPool::prepareRequest(HTTPClient &http) const
{
http.addHeader("Pool-Auth-Token", poolUser.c_str());
}
std::string BraiinsPool::getApiUrl() const {
std::string BraiinsPool::getApiUrl() const
{
return "https://pool.braiins.com/accounts/profile/json/btc/";
}
PoolStats BraiinsPool::parseResponse(const JsonDocument &doc) const
{
if (doc["btc"].isNull()) {
try
{
if (doc["btc"].isNull())
{
return PoolStats{
.hashrate = "0",
.dailyEarnings = 0};
}
std::string unit = doc["btc"]["hash_rate_unit"].as<std::string>();
static const std::unordered_map<std::string, int> multipliers = {
{"Zh/s", 21}, {"Eh/s", 18}, {"Ph/s", 15}, {"Th/s", 12}, {"Gh/s", 9}, {"Mh/s", 6}, {"Kh/s", 3}};
int multiplier = multipliers.at(unit);
float hashValue = doc["btc"]["hash_rate_5m"].as<float>();
return PoolStats{
.hashrate = std::to_string(static_cast<int>(std::round(hashValue))) + std::string(multiplier, '0'),
.dailyEarnings = static_cast<int64_t>(doc["btc"]["today_reward"].as<float>() * 100000000)};
}
catch (const std::exception &e)
{
Serial.printf("Error parsing %s response: %s\n", getPoolName().c_str(), e.what());
return PoolStats{
.hashrate = "0",
.dailyEarnings = 0
};
.dailyEarnings = std::nullopt};
}
std::string unit = doc["btc"]["hash_rate_unit"].as<std::string>();
static const std::unordered_map<std::string, int> multipliers = {
{"Zh/s", 21}, {"Eh/s", 18}, {"Ph/s", 15}, {"Th/s", 12}, {"Gh/s", 9}, {"Mh/s", 6}, {"Kh/s", 3}};
int multiplier = multipliers.at(unit);
float hashValue = doc["btc"]["hash_rate_5m"].as<float>();
return PoolStats{
.hashrate = std::to_string(static_cast<int>(std::round(hashValue))) + std::string(multiplier, '0'),
.dailyEarnings = static_cast<int64_t>(doc["btc"]["today_reward"].as<float>() * 100000000)};
}

View file

@ -0,0 +1,47 @@
#include "ckpool.hpp"
void CKPool::prepareRequest(HTTPClient &http) const
{
// Empty as CKPool doesn't need special headers
}
std::string CKPool::getApiUrl() const
{
return getBaseUrl() + "/users/" + poolUser;
}
PoolStats CKPool::parseResponse(const JsonDocument &doc) const
{
try
{
std::string hashrateStr = doc["hashrate1m"].as<std::string>();
// Special case for "0"
if (hashrateStr == "0") {
return PoolStats{
.hashrate = "0",
.dailyEarnings = std::nullopt
};
}
char unit = hashrateStr.back();
std::string value = hashrateStr.substr(0, hashrateStr.size() - 1);
int multiplier = getHashrateMultiplier(unit);
double hashrate = std::stod(value) * std::pow(10, multiplier);
char buffer[32];
snprintf(buffer, sizeof(buffer), "%.0f", hashrate);
return PoolStats{
.hashrate = buffer,
.dailyEarnings = std::nullopt};
}
catch (const std::exception &e)
{
Serial.printf("Error parsing %s response: %s\n", getPoolName().c_str(), e.what());
return PoolStats{
.hashrate = "0",
.dailyEarnings = std::nullopt};
}
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "lib/mining_pool/mining_pool_interface.hpp"
#include <utils.hpp>
class CKPool : public MiningPoolInterface {
public:
void setPoolUser(const std::string& user) override { poolUser = user; }
void prepareRequest(HTTPClient& http) const override;
std::string getApiUrl() const override;
PoolStats parseResponse(const JsonDocument& doc) const override;
bool supportsDailyEarnings() const override { return false; }
std::string getDailyEarningsLabel() const override { return ""; }
bool hasLogo() const override { return false; }
std::string getDisplayLabel() const override { return "CK/POOL"; }
std::string getPoolName() const override {
return "ckpool";
}
protected:
virtual std::string getBaseUrl() const {
return "https://solo.ckpool.org";
}
};

View file

@ -0,0 +1,16 @@
#pragma once
#include "ckpool.hpp"
class EUCKPool : public CKPool {
public:
std::string getDisplayLabel() const override { return "CK/POOL"; }
std::string getPoolName() const override {
return "eu_ckpool";
}
protected:
std::string getBaseUrl() const override {
return "https://eusolo.ckpool.org";
}
};

View file

@ -1,27 +1,48 @@
// src/noderunners/noderunners_pool.cpp
#include "noderunners_pool.hpp"
void NoderunnersPool::prepareRequest(HTTPClient& http) const {
// Empty as NodeRunners doesn't need special headers
void NoderunnersPool::prepareRequest(HTTPClient &http) const
{
// Empty as Noderunners doesn't need special headers
}
std::string NoderunnersPool::getApiUrl() const {
std::string NoderunnersPool::getApiUrl() const
{
return "https://pool.noderunners.network/api/v1/users/" + poolUser;
}
PoolStats NoderunnersPool::parseResponse(const JsonDocument& doc) const {
std::string hashrateStr = doc["hashrate1m"].as<std::string>();
char unit = hashrateStr.back();
std::string value = hashrateStr.substr(0, hashrateStr.size() - 1);
int multiplier = getHashrateMultiplier(unit);
double hashrate = std::stod(value) * std::pow(10, multiplier);
char buffer[32];
snprintf(buffer, sizeof(buffer), "%.0f", hashrate);
return PoolStats{
.hashrate = buffer,
.dailyEarnings = std::nullopt
};
PoolStats NoderunnersPool::parseResponse(const JsonDocument &doc) const
{
try
{
std::string hashrateStr = doc["hashrate1m"].as<std::string>();
// Special case for "0"
if (hashrateStr == "0") {
return PoolStats{
.hashrate = "0",
.dailyEarnings = std::nullopt
};
}
char unit = hashrateStr.back();
std::string value = hashrateStr.substr(0, hashrateStr.size() - 1);
int multiplier = getHashrateMultiplier(unit);
double hashrate = std::stod(value) * std::pow(10, multiplier);
char buffer[32];
snprintf(buffer, sizeof(buffer), "%.0f", hashrate);
return PoolStats{
.hashrate = buffer,
.dailyEarnings = std::nullopt};
}
catch (const std::exception &e)
{
Serial.printf("Error parsing %s response: %s\n", getPoolName().c_str(), e.what());
return PoolStats{
.hashrate = "0",
.dailyEarnings = std::nullopt};
}
}

View file

@ -1,18 +1,29 @@
#include "ocean_pool.hpp"
void OceanPool::prepareRequest(HTTPClient& http) const {
void OceanPool::prepareRequest(HTTPClient &http) const
{
// Empty as Ocean doesn't need special headers
}
std::string OceanPool::getApiUrl() const {
std::string OceanPool::getApiUrl() const
{
return "https://api.ocean.xyz/v1/statsnap/" + poolUser;
}
PoolStats OceanPool::parseResponse(const JsonDocument& doc) const {
return PoolStats{
.hashrate = doc["result"]["hashrate_300s"].as<std::string>(),
.dailyEarnings = static_cast<int64_t>(
doc["result"]["estimated_earn_next_block"].as<float>() * 100000000
)
};
}
PoolStats OceanPool::parseResponse(const JsonDocument &doc) const
{
try
{
return PoolStats{
.hashrate = doc["result"]["hashrate_300s"].as<std::string>(),
.dailyEarnings = static_cast<int64_t>(
doc["result"]["estimated_earn_next_block"].as<float>() * 100000000)};
}
catch (const std::exception &e)
{
Serial.printf("Error parsing %s response: %s\n", getPoolName().c_str(), e.what());
return PoolStats{
.hashrate = "0",
.dailyEarnings = std::nullopt};
}
}

View file

@ -6,6 +6,8 @@ const char* PoolFactory::MINING_POOL_NAME_BRAIINS = "braiins";
const char* PoolFactory::MINING_POOL_NAME_SATOSHI_RADIO = "satoshi_radio";
const char* PoolFactory::MINING_POOL_NAME_PUBLIC_POOL = "public_pool";
const char* PoolFactory::MINING_POOL_NAME_GOBRRR_POOL = "gobrrr_pool";
const char* PoolFactory::MINING_POOL_NAME_CKPOOL = "ckpool";
const char* PoolFactory::MINING_POOL_NAME_EU_CKPOOL = "eu_ckpool";
const char* PoolFactory::LOGOS_DIR = "/logos";
std::unique_ptr<MiningPoolInterface> PoolFactory::createPool(const std::string& poolName) {
@ -15,7 +17,9 @@ std::unique_ptr<MiningPoolInterface> PoolFactory::createPool(const std::string&
{MINING_POOL_NAME_BRAIINS, []() { return std::make_unique<BraiinsPool>(); }},
{MINING_POOL_NAME_SATOSHI_RADIO, []() { return std::make_unique<SatoshiRadioPool>(); }},
{MINING_POOL_NAME_PUBLIC_POOL, []() { return std::make_unique<PublicPool>(); }},
{MINING_POOL_NAME_GOBRRR_POOL, []() { return std::make_unique<GoBrrrPool>(); }}
{MINING_POOL_NAME_GOBRRR_POOL, []() { return std::make_unique<GoBrrrPool>(); }},
{MINING_POOL_NAME_CKPOOL, []() { return std::make_unique<CKPool>(); }},
{MINING_POOL_NAME_EU_CKPOOL, []() { return std::make_unique<EUCKPool>(); }}
};
auto it = poolFactories.find(poolName);

View file

@ -11,6 +11,8 @@
#include "satoshi_radio/satoshi_radio_pool.hpp"
#include "public_pool/public_pool.hpp"
#include "gobrrr_pool/gobrrr_pool.hpp"
#include "ckpool/ckpool.hpp"
#include "ckpool/eu_ckpool.hpp"
#include <LittleFS.h>
#include <HTTPClient.h>
@ -26,7 +28,9 @@ class PoolFactory {
MINING_POOL_NAME_SATOSHI_RADIO,
MINING_POOL_NAME_BRAIINS,
MINING_POOL_NAME_PUBLIC_POOL,
MINING_POOL_NAME_GOBRRR_POOL
MINING_POOL_NAME_GOBRRR_POOL,
MINING_POOL_NAME_CKPOOL,
MINING_POOL_NAME_EU_CKPOOL
};
}
@ -52,5 +56,7 @@ class PoolFactory {
static const char* MINING_POOL_NAME_SATOSHI_RADIO;
static const char* MINING_POOL_NAME_PUBLIC_POOL;
static const char* MINING_POOL_NAME_GOBRRR_POOL;
static const char* MINING_POOL_NAME_CKPOOL;
static const char* MINING_POOL_NAME_EU_CKPOOL;
static const char* LOGOS_DIR;
};

View file

@ -1,21 +1,32 @@
// src/noderunners/noderunners_pool.cpp
#include "public_pool.hpp"
std::string PublicPool::getApiUrl() const {
std::string PublicPool::getApiUrl() const
{
return "https://public-pool.io:40557/api/client/" + poolUser;
}
PoolStats PublicPool::parseResponse(const JsonDocument& doc) const {
PoolStats PublicPool::parseResponse(const JsonDocument &doc) const
{
uint64_t totalHashrate = 0;
for (JsonVariantConst worker : doc["workers"].as<JsonArrayConst>()) {
totalHashrate += static_cast<uint64_t>(std::llround(worker["hashRate"].as<double>()));
}
try
{
for (JsonVariantConst worker : doc["workers"].as<JsonArrayConst>())
{
totalHashrate += static_cast<uint64_t>(std::llround(worker["hashRate"].as<double>()));
}
}
catch (const std::exception &e)
{
Serial.printf("Error parsing %s response: %s\n", getPoolName().c_str(), e.what());
return PoolStats{
.hashrate = "0",
.dailyEarnings = std::nullopt};
}
return PoolStats{
.hashrate = std::to_string(totalHashrate),
.dailyEarnings = std::nullopt // Public Pool doesn't support daily earnings
.dailyEarnings = std::nullopt // Public Pool doesn't support daily earnings
};
}

View file

@ -1,6 +1,7 @@
// src/noderunners/noderunners_pool.cpp
#include "satoshi_radio_pool.hpp"
std::string SatoshiRadioPool::getApiUrl() const {
std::string SatoshiRadioPool::getApiUrl() const
{
return "https://pool.satoshiradio.nl/api/v1/users/" + poolUser;
}