Better handling of unexpected pool stats responses, add CKPool
This commit is contained in:
parent
10fe5b5053
commit
b7ff9d8101
10 changed files with 210 additions and 57 deletions
|
@ -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)};
|
||||
}
|
||||
|
||||
|
|
47
src/lib/mining_pool/ckpool/ckpool.cpp
Normal file
47
src/lib/mining_pool/ckpool/ckpool.cpp
Normal 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};
|
||||
}
|
||||
}
|
25
src/lib/mining_pool/ckpool/ckpool.hpp
Normal file
25
src/lib/mining_pool/ckpool/ckpool.hpp
Normal 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";
|
||||
}
|
||||
};
|
16
src/lib/mining_pool/ckpool/eu_ckpool.hpp
Normal file
16
src/lib/mining_pool/ckpool/eu_ckpool.hpp
Normal 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";
|
||||
}
|
||||
};
|
|
@ -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};
|
||||
}
|
||||
}
|
|
@ -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};
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue