From 990916ab6272228674f4f3ddf9b72b27a443262e Mon Sep 17 00:00:00 2001 From: SChernykh Date: Sun, 8 May 2022 12:30:22 +0200 Subject: [PATCH] StratumServer: added autodiff Autodiff is enabled by default, target time is 30 seconds. It can be disabled with `--no-autodiff` command line option. --- src/common.h | 3 + src/log.h | 3 +- src/main.cpp | 3 +- src/params.cpp | 5 + src/params.h | 1 + src/stratum_server.cpp | 216 ++++++++++++++++++++++++++++++----------- src/stratum_server.h | 24 ++++- 7 files changed, 194 insertions(+), 61 deletions(-) diff --git a/src/common.h b/src/common.h index 75c3495..d333534 100644 --- a/src/common.h +++ b/src/common.h @@ -198,6 +198,9 @@ struct difficulty_type FORCEINLINE bool operator==(const difficulty_type& other) const { return (lo == other.lo) && (hi == other.hi); } FORCEINLINE bool operator!=(const difficulty_type& other) const { return (lo != other.lo) || (hi != other.hi); } + FORCEINLINE bool operator==(uint64_t other) const { return (lo == other) && (hi == 0); } + FORCEINLINE bool operator!=(uint64_t other) const { return (lo != other) || (hi != 0); } + friend std::ostream& operator<<(std::ostream& s, const difficulty_type& d); friend std::istream& operator>>(std::istream& s, difficulty_type& d); diff --git a/src/log.h b/src/log.h index 21a0fa6..905e003 100644 --- a/src/log.h +++ b/src/log.h @@ -307,6 +307,7 @@ struct Hashrate { FORCEINLINE Hashrate() : m_data(0), m_valid(false) {} explicit FORCEINLINE Hashrate(uint64_t data) : m_data(data), m_valid(true) {} + FORCEINLINE Hashrate(uint64_t data, bool valid) : m_data(data), m_valid(valid) {} uint64_t m_data; bool m_valid; @@ -314,7 +315,7 @@ struct Hashrate template<> struct log::Stream::Entry { - static NOINLINE void put(Hashrate&& value, Stream* wrapper) + static NOINLINE void put(const Hashrate& value, Stream* wrapper) { if (!value.m_valid) { return; diff --git a/src/main.cpp b/src/main.cpp index 379e61b..25bb4ee 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,7 +44,8 @@ void p2pool_usage() "--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 1000)\n" "--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 1000)\n" "--start-mining N Start built-in miner using N threads (any value between 1 and 64)\n" - "--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d.\n" + "--mini Connect to p2pool-mini sidechain. Note that it will also change default p2p port from %d to %d\n" + "--no-autodiff Disable automatic difficulty adjustment for miners connected to stratum\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/params.cpp b/src/params.cpp index 72a63fc..8ce4736 100644 --- a/src/params.cpp +++ b/src/params.cpp @@ -125,6 +125,11 @@ Params::Params(int argc, char* argv[]) ok = true; } + if (strcmp(argv[i], "--no-autodiff") == 0) { + m_autoDiff = false; + ok = true; + } + if (!ok) { fprintf(stderr, "Unknown command line parameter %s\n\n", argv[i]); p2pool_usage(); diff --git a/src/params.h b/src/params.h index 719702a..78a2eb2 100644 --- a/src/params.h +++ b/src/params.h @@ -48,6 +48,7 @@ struct Params uint32_t m_maxIncomingPeers = 1000; uint32_t m_minerThreads = 0; bool m_mini = false; + bool m_autoDiff = true; }; } // namespace p2pool diff --git a/src/stratum_server.cpp b/src/stratum_server.cpp index 2502151..5d18564 100644 --- a/src/stratum_server.cpp +++ b/src/stratum_server.cpp @@ -27,9 +27,11 @@ static constexpr char log_category_prefix[] = "StratumServer "; static constexpr int DEFAULT_BACKLOG = 128; static constexpr uint64_t DEFAULT_BAN_TIME = 600; +static constexpr uint64_t MIN_DIFF = 1000; +static constexpr uint64_t AUTO_DIFF_TARGET_TIME = 30; // Use short target format (4 bytes) for diff <= 4 million -static constexpr uint64_t TARGET_4_BYTES_LIMIT = std::numeric_limits::max() / 4000000; +static constexpr uint64_t TARGET_4_BYTES_LIMIT = std::numeric_limits::max() / 4000000 + 1; #include "tcp_server.inl" @@ -38,6 +40,7 @@ namespace p2pool { StratumServer::StratumServer(p2pool* pool) : TCPServer(StratumClient::allocate) , m_pool(pool) + , m_autoDiff(pool->params().m_autoDiff) , m_extraNonce(0) , m_rng(RandomDeviceSeed::instance) , m_cumulativeHashes(0) @@ -57,7 +60,6 @@ StratumServer::StratumServer(p2pool* pool) uv_mutex_init_checked(&m_blobsQueueLock); uv_mutex_init_checked(&m_rngLock); - uv_mutex_init_checked(&m_submittedSharesPoolLock); uv_rwlock_init_checked(&m_hashrateDataLock); m_submittedSharesPool.resize(10); @@ -84,7 +86,6 @@ StratumServer::~StratumServer() uv_mutex_destroy(&m_blobsQueueLock); uv_mutex_destroy(&m_rngLock); - uv_mutex_destroy(&m_submittedSharesPoolLock); uv_rwlock_destroy(&m_hashrateDataLock); for (SubmittedShare* share : m_submittedSharesPool) { @@ -222,8 +223,8 @@ static bool get_custom_diff(const char* s, difficulty_type& diff) if (diff_str) { const uint64_t t = strtoull(diff_str + 1, nullptr, 10); if (t) { - // Don't let clients set difficulty less than 1000 - diff = { std::max(t + 1, 1000), 0 }; + // Don't let clients set difficulty less than MIN_DIFF + diff = { std::max(t + 1, MIN_DIFF), 0 }; return true; } } @@ -259,11 +260,9 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log uint32_t job_id; { - MutexLock lock(client->m_jobsLock); + job_id = ++client->m_perConnectionJobId; - job_id = client->m_perConnectionJobId++; - - StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)]; + StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE]; saved_job.job_id = job_id; saved_job.extra_nonce = extra_nonce; saved_job.template_id = template_id; @@ -311,6 +310,11 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo job_id = (job_id << 4) + d; } + if (!job_id) { + LOGWARN(4, "client " << static_cast(client->m_addrString) << " invalid params ('job_id' can't be 0)"); + return false; + } + uint32_t nonce = 0; for (int i = static_cast(sizeof(uint32_t)) - 1; i >= 0; --i) { @@ -339,9 +343,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo bool found = false; { - MutexLock lock(client->m_jobsLock); - - const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)]; + const StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE]; if (saved_job.job_id == job_id) { template_id = saved_job.template_id; extra_nonce = saved_job.extra_nonce; @@ -374,16 +376,16 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo SubmittedShare* share; - { - MutexLock lock(m_submittedSharesPoolLock); + if (!m_submittedSharesPool.empty()) { + share = m_submittedSharesPool.back(); + m_submittedSharesPool.pop_back(); + } + else { + share = new SubmittedShare{}; + } - if (!m_submittedSharesPool.empty()) { - share = m_submittedSharesPool.back(); - m_submittedSharesPool.pop_back(); - } - else { - share = new SubmittedShare{}; - } + if (target >= TARGET_4_BYTES_LIMIT) { + target = (target >> 32) << 32; } share->m_req.data = share; @@ -399,9 +401,16 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo share->m_target = target; share->m_resultHash = resultHash; share->m_sidechainDifficulty = sidechain_diff; + share->m_timestamp = seconds_since_epoch(); + + uint64_t rem; + share->m_hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 1; + share->m_highEnoughDifficulty = sidechain_diff.check_pow(resultHash); + + update_auto_diff(client, share->m_timestamp, share->m_hashes); // If this share is below sidechain difficulty, process it in this thread because it'll be quick - if (!share->m_sidechainDifficulty.check_pow(share->m_resultHash)) { + if (!share->m_highEnoughDifficulty) { on_share_found(&share->m_req); on_after_share_found(&share->m_req, 0); return true; @@ -458,10 +467,18 @@ void StratumServer::show_workers() size_t n = 0; + LOGINFO(0, log::pad_right("IP:port", addr_len + 8) + << log::pad_right("uptime", 20) + << log::pad_right("difficulty", 20) + << log::pad_right("hashrate", 15) + << "name" + ); + for (const StratumClient* c = static_cast(m_connectedClientsList->m_next); c != m_connectedClientsList; c = static_cast(c->m_next)) { LOGINFO(0, log::pad_right(static_cast(c->m_addrString), addr_len + 8) << log::pad_right(log::Duration(cur_time - c->m_connectedTime), 20) - << log::pad_right(c->m_customDiff, 20) + << log::pad_right(((c->m_customDiff != 0) || !m_autoDiff) ? c->m_customDiff : c->m_autoDiff, 20) + << log::pad_right(log::Hashrate(c->m_autoDiff.lo / AUTO_DIFF_TARGET_TIME, c->m_autoDiff.lo != 0), 15) << (c->m_rpcId ? c->m_customUser : "not logged in") ); ++n; @@ -528,6 +545,80 @@ void StratumServer::print_stratum_status() const ); } +// Compresses 64-bit hashes value into 16-bit value (5 bits for shift, 11 bits for data) +namespace { + +enum HashValue : uint64_t { + bits = 11, + mask = (1 << bits) - 1, +}; + +static constexpr FORCEINLINE uint64_t hash_uncompress(uint64_t h) +{ + return (h & HashValue::mask) << (h >> HashValue::bits); +}; + +enum HashMaxValue : uint64_t { + value = hash_uncompress(std::numeric_limits::max()) +}; + +static constexpr FORCEINLINE uint16_t hash_compress(uint64_t h) +{ + if (h > HashMaxValue::value) { + h = HashMaxValue::value; + } + + uint64_t shift = 0; + while (h > HashValue::mask) { + h >>= 1; + shift += (1 << HashValue::bits); + } + + return static_cast(shift | h); +} + +} + +void StratumServer::update_auto_diff(StratumClient* client, const uint64_t timestamp, const uint64_t hashes) +{ + const uint16_t hashes_compressed = hash_compress(hashes); + client->m_autoDiffWindowHashes += hash_uncompress(hashes_compressed); + + const uint32_t k = client->m_autoDiffIndex++; + constexpr uint32_t N = StratumClient::AUTO_DIFF_SIZE; + + StratumClient::AutoDiffData& auto_diff_data = client->m_autoDiffData[k % N]; + + if (k >= N) { + client->m_autoDiffWindowHashes -= hash_uncompress(auto_diff_data.m_hashes); + } + + const uint16_t t1 = auto_diff_data.m_timestamp; + const uint16_t t2 = static_cast(timestamp); + auto_diff_data.m_timestamp = t2; + auto_diff_data.m_hashes = hashes_compressed; + + if (k >= N) { + // Full window + const uint64_t dt = t2 - t1; + client->m_autoDiff.lo = std::max((client->m_autoDiffWindowHashes * AUTO_DIFF_TARGET_TIME) / (dt ? dt : 1), MIN_DIFF); + client->m_autoDiff.hi = 0; + } + else if (k >= 10) { + // Partial window + const uint64_t h0 = hash_uncompress(client->m_autoDiffData[0].m_hashes); + const uint64_t dt = client->m_autoDiffData[k].m_timestamp - client->m_autoDiffData[0].m_timestamp; + + client->m_autoDiff.lo = std::max(((client->m_autoDiffWindowHashes - h0) * AUTO_DIFF_TARGET_TIME) / (dt ? dt : 1), MIN_DIFF); + client->m_autoDiff.hi = 0; + } + else if (k == 0) { + // First share, fix auto diff to current difficulty until we have at least 10 shares + client->m_autoDiff.lo = hashes; + client->m_autoDiff.hi = 0; + } +} + void StratumServer::on_blobs_ready() { std::vector blobs_queue; @@ -584,14 +675,32 @@ void StratumServer::on_blobs_ready() if (client->m_customDiff.lo) { target = std::max(target, client->m_customDiff.target()); } + else if (m_autoDiff) { + if (client->m_autoDiff.lo) { + const uint32_t k = client->m_autoDiffIndex; + const uint16_t elapsed_time = static_cast(cur_time) - client->m_autoDiffData[(k - 1) % StratumClient::AUTO_DIFF_SIZE].m_timestamp; + if (elapsed_time > AUTO_DIFF_TARGET_TIME * 5) { + // More than 500% effort, reduce the auto diff by 1/8 every time until the share is found + client->m_autoDiff.lo = std::max(client->m_autoDiff.lo - client->m_autoDiff.lo / 8, MIN_DIFF); + } + target = std::max(target, client->m_autoDiff.target()); + } + else { + // Not enough shares from the client yet, cut diff in half every 16 seconds + const uint64_t num_halvings = (cur_time - client->m_connectedTime) / 16; + constexpr uint64_t max_target = (std::numeric_limits::max() / MIN_DIFF) + 1; + for (uint64_t i = 0; (i < num_halvings) && (target < max_target); ++i) { + target *= 2; + } + target = std::min(target, max_target); + } + } uint32_t job_id; { - MutexLock lock3(client->m_jobsLock); - - job_id = client->m_perConnectionJobId++; + job_id = ++client->m_perConnectionJobId; - StratumClient::SavedJob& saved_job = client->m_jobs[job_id % array_size(&StratumClient::m_jobs)]; + StratumClient::SavedJob& saved_job = client->m_jobs[job_id % StratumClient::JOBS_SIZE]; saved_job.job_id = job_id; saved_job.extra_nonce = extra_nonce; saved_job.template_id = data->m_templateId; @@ -667,26 +776,23 @@ void StratumServer::update_hashrate_data(uint64_t hashes, uint64_t timestamp) void StratumServer::on_share_found(uv_work_t* req) { - bkg_jobs_tracker.start("StratumServer::on_share_found"); - SubmittedShare* share = reinterpret_cast(req->data); + if (share->m_highEnoughDifficulty) { + bkg_jobs_tracker.start("StratumServer::on_share_found"); + } + StratumClient* client = share->m_client; StratumServer* server = share->m_server; p2pool* pool = server->m_pool; - uint64_t target = share->m_target; - if (target >= TARGET_4_BYTES_LIMIT) { - target = (target >> 32) << 32; - } - - uint64_t rem; - const uint64_t hashes = (target > 1) ? udiv128(1, 0, target, &rem) : 0; + const uint64_t target = share->m_target; + const uint64_t hashes = share->m_hashes; if (pool->stopped()) { LOGWARN(0, "p2pool is shutting down, but a share was found. Trying to process it anyway!"); } - if (share->m_sidechainDifficulty.check_pow(share->m_resultHash)) { + if (share->m_highEnoughDifficulty) { uint8_t blob[128]; uint64_t height; difficulty_type difficulty; @@ -736,7 +842,7 @@ void StratumServer::on_share_found(uv_work_t* req) const uint64_t value = *reinterpret_cast(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t)); if (LIKELY(value < target)) { - const uint64_t timestamp = seconds_since_epoch(); + const uint64_t timestamp = share->m_timestamp; server->update_hashrate_data(hashes, timestamp); server->api_update_local_stats(timestamp); share->m_result = SubmittedShare::Result::OK; @@ -750,16 +856,11 @@ void StratumServer::on_share_found(uv_work_t* req) void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/) { SubmittedShare* share = reinterpret_cast(req->data); + if (share->m_highEnoughDifficulty) { + bkg_jobs_tracker.stop("StratumServer::on_share_found"); + } - ON_SCOPE_LEAVE( - [share]() - { - { - MutexLock lock(share->m_server->m_submittedSharesPoolLock); - share->m_server->m_submittedSharesPool.push_back(share); - } - bkg_jobs_tracker.stop("StratumServer::on_share_found"); - }); + ON_SCOPE_LEAVE([share]() { share->m_server->m_submittedSharesPool.push_back(share); }); StratumClient* client = share->m_client; StratumServer* server = share->m_server; @@ -809,15 +910,13 @@ StratumServer::StratumClient::StratumClient() , m_perConnectionJobId(0) , m_connectedTime(0) , m_jobs{} + , m_autoDiffData{} + , m_autoDiffWindowHashes(0) + , m_autoDiffIndex(0) , m_customDiff{} + , m_autoDiff{} , m_customUser{} { - uv_mutex_init_checked(&m_jobsLock); -} - -StratumServer::StratumClient::~StratumClient() -{ - uv_mutex_destroy(&m_jobsLock); } void StratumServer::StratumClient::reset() @@ -826,9 +925,16 @@ void StratumServer::StratumClient::reset() m_rpcId = 0; m_perConnectionJobId = 0; m_connectedTime = 0; - memset(m_jobs, 0, sizeof(m_jobs)); + + for (int i = 0; i < JOBS_SIZE; ++i) { + m_jobs[i].job_id = 0; + } + + m_autoDiffWindowHashes = 0; + m_autoDiffIndex = 0; m_customDiff = {}; - memset(m_customUser, 0, sizeof(m_customUser)); + m_autoDiff = {}; + m_customUser[0] = '\0'; } bool StratumServer::StratumClient::on_connect() diff --git a/src/stratum_server.h b/src/stratum_server.h index 1a97279..81a667c 100644 --- a/src/stratum_server.h +++ b/src/stratum_server.h @@ -39,7 +39,7 @@ public: struct StratumClient : public Client { StratumClient(); - ~StratumClient(); + FORCEINLINE ~StratumClient() {} static Client* allocate() { return new StratumClient(); } @@ -55,16 +55,28 @@ public: uint32_t m_perConnectionJobId; uint64_t m_connectedTime; - uv_mutex_t m_jobsLock; + enum { + JOBS_SIZE = 4, + AUTO_DIFF_SIZE = 64, + }; struct SavedJob { uint32_t job_id; uint32_t extra_nonce; uint32_t template_id; uint64_t target; - } m_jobs[4]; + } m_jobs[JOBS_SIZE]; + + struct AutoDiffData { + uint16_t m_timestamp; + uint16_t m_hashes; + } m_autoDiffData[AUTO_DIFF_SIZE]; + + uint64_t m_autoDiffWindowHashes; + uint32_t m_autoDiffIndex; difficulty_type m_customDiff; + difficulty_type m_autoDiff; char m_customUser[32]; }; @@ -79,11 +91,13 @@ public: private: void print_stratum_status() const; + void update_auto_diff(StratumClient* client, const uint64_t timestamp, const uint64_t hashes); static void on_share_found(uv_work_t* req); static void on_after_share_found(uv_work_t* req, int status); p2pool* m_pool; + bool m_autoDiff; struct BlobsData { @@ -123,6 +137,9 @@ private: uint64_t m_target; hash m_resultHash; difficulty_type m_sidechainDifficulty; + uint64_t m_timestamp; + uint64_t m_hashes; + bool m_highEnoughDifficulty; enum class Result { STALE, @@ -133,7 +150,6 @@ private: } m_result; }; - uv_mutex_t m_submittedSharesPoolLock; std::vector m_submittedSharesPool; struct HashrateData