From 9ccdd1fe54e0a91f37da1d257bd20878ade2241c Mon Sep 17 00:00:00 2001 From: Intel Date: Wed, 29 Oct 2014 17:09:16 +0100 Subject: [PATCH] Improved ShareLimiter --- src/server/poolserver/Main.cpp | 4 +- src/server/poolserver/Stratum/Client.cpp | 60 +++++++++---------- src/server/poolserver/Stratum/Client.h | 14 ++++- src/server/poolserver/Stratum/Job.h | 6 ++ .../poolserver/Stratum/ShareLimiter.cpp | 16 +++-- src/server/poolserver/Stratum/ShareLimiter.h | 9 +-- 6 files changed, 67 insertions(+), 42 deletions(-) diff --git a/src/server/poolserver/Main.cpp b/src/server/poolserver/Main.cpp index 8da653a..79ced82 100644 --- a/src/server/poolserver/Main.cpp +++ b/src/server/poolserver/Main.cpp @@ -48,9 +48,11 @@ bool InitConfig(int argc, char *argv[]) ("StratumPort,sp", boost::program_options::value()->default_value(3333), "Stratum server port") ("StratumBlockCheckTime", boost::program_options::value()->default_value(2000), "Time between block checks in ms") ("RetargetInterval", boost::program_options::value()->default_value(20), "Time between difficulty checks in seconds") + ("RetargetSharesThreashold", boost::program_options::value()->default_value(20), "Number of shares in retarget interval to trigger a difficulty check") ("RetargetTimeBuffer", boost::program_options::value()->default_value(60*5), "Buffer of shares to keep (in seconds)") - ("RetargetTimePerShare", boost::program_options::value()->default_value(10), "Target in seconds between shares") + ("RetargetTimePerShare", boost::program_options::value()->default_value(4), "Target in seconds between shares") ("RetargetVariance", boost::program_options::value()->default_value(40), "Maximum allowed variance in percent before difficulty change") + ("RetargetStartingDiff", boost::program_options::value()->default_value(2), "Difficulty at which new miner starts") ("RetargetMinDiff", boost::program_options::value()->default_value(1), "Minimum difficulty (also starting difficulty)") ("RetargetMaxDiff", boost::program_options::value()->default_value(1000000), "Maximum difficulty we can reach") ; diff --git a/src/server/poolserver/Stratum/Client.cpp b/src/server/poolserver/Stratum/Client.cpp index cddf229..3213861 100644 --- a/src/server/poolserver/Stratum/Client.cpp +++ b/src/server/poolserver/Stratum/Client.cpp @@ -37,9 +37,9 @@ namespace Stratum void Client::SendJob(bool clean) { if (clean) - _jobs.clear(); + CleanJobs(); - Job job = GetJob(); + Job* job = GetJob(); uint32 jobid = _jobid++; _jobs[jobid] = job; @@ -51,14 +51,14 @@ namespace Stratum JSON merkle_branch(JSON_ARRAY); uint32 j = 0; - for (uint32 size = job.block->tx.size(); size > 1; size = (size+1)/2) + for (uint32 size = job->block->tx.size(); size > 1; size = (size+1)/2) { - merkle_branch.Add(Util::BinToASCII(job.block->merkleTree[j+1])); + merkle_branch.Add(Util::BinToASCII(job->block->merkleTree[j+1])); j += size; } // Reverse prev block hash every 4 bytes... Makes a lot of sense... - ByteBuffer prevhashbuf(Util::Reverse(job.block->prevBlockHash)); + ByteBuffer prevhashbuf(Util::Reverse(job->block->prevBlockHash)); std::vector prevhash(8); prevhashbuf >> prevhash[7] >> prevhash[6] >> prevhash[5] >> prevhash[4] >> prevhash[3] >> prevhash[2] >> prevhash[1] >> prevhash[0]; ByteBuffer prevhashfixed; @@ -67,12 +67,12 @@ namespace Stratum JSON params; params.Add(jobss.str()); params.Add(Util::BinToASCII(prevhashfixed.Binary())); - params.Add(Util::BinToASCII(job.coinbase1)); - params.Add(Util::BinToASCII(job.coinbase2)); + params.Add(Util::BinToASCII(job->coinbase1)); + params.Add(Util::BinToASCII(job->coinbase2)); params.Add(merkle_branch); - params.Add(Util::BinToASCII(Util::Reverse(ByteBuffer(job.block->version).Binary()))); - params.Add(Util::BinToASCII(Util::Reverse(ByteBuffer(job.block->bits).Binary()))); - params.Add(Util::BinToASCII(Util::Reverse(ByteBuffer(job.block->time).Binary()))); + params.Add(Util::BinToASCII(Util::Reverse(ByteBuffer(job->block->version).Binary()))); + params.Add(Util::BinToASCII(Util::Reverse(ByteBuffer(job->block->bits).Binary()))); + params.Add(Util::BinToASCII(Util::Reverse(ByteBuffer(job->block->time).Binary()))); params.Add(clean); JSON msg; @@ -122,10 +122,10 @@ namespace Stratum } // Get job - Job& job = _jobs[jobid]; + Job* job = _jobs[jobid]; // Share limiter - if (!_shareLimiter.Submit(job.diff)) { + if (!_shareLimiter.Submit(job->diff)) { JSON response; response["id"] = msg["id"]; response["result"]; @@ -183,9 +183,9 @@ namespace Stratum sLog.Debug(LOG_STRATUM, "Job::SubmitShare: Nonce: %s, Extranonce: %s, Share: %u", Util::BinToASCII(noncebuf.Binary()).c_str(), Util::BinToASCII(extranonce2).c_str(), share); - if (!job.SubmitShare(share)) { + if (!job->SubmitShare(share)) { sLog.Warn(LOG_STRATUM, "%s: Duplicate share", username.c_str()); - DataMgr::Instance()->Push(Share(_ip, username, false, "Duplicate share", Util::Date(), job.diff)); + DataMgr::Instance()->Push(Share(_ip, username, false, "Duplicate share", Util::Date(), job->diff)); _shareLimiter.LogBad(); JSON response; @@ -199,7 +199,7 @@ namespace Stratum } // Copy block we are working on - Bitcoin::Block block = *job.block; + Bitcoin::Block block = *job->block; // Start assembling the block timebuf >> block.time; @@ -208,7 +208,7 @@ namespace Stratum // Assemble coinbase Bitcoin::Transaction coinbasetx; ByteBuffer coinbasebuf; - coinbasebuf << job.coinbase1 << _extranonce << extranonce2 << job.coinbase2; + coinbasebuf << job->coinbase1 << _extranonce << extranonce2 << job->coinbase2; coinbasebuf >> coinbasetx; // Set coinbase tx @@ -224,9 +224,9 @@ namespace Stratum BigInt target(Util::BinToASCII(Util::Reverse(hash)), 16); // Check if difficulty meets job diff - if (target > job.jobTarget) { + if (target > job->jobTarget) { sLog.Warn(LOG_STRATUM, "%s: Share above target", username.c_str()); - DataMgr::Instance()->Push(Share(_ip, username, false, "Share above target", Util::Date(), job.diff)); + DataMgr::Instance()->Push(Share(_ip, username, false, "Share above target", Util::Date(), job->diff)); _shareLimiter.LogBad(); JSON response; @@ -240,11 +240,11 @@ namespace Stratum } // Check if block meets criteria - if (target <= job.blockTarget) { + if (target <= job->blockTarget) { sLog.Info(LOG_STRATUM, "We have found a block candidate!"); // copy job diff because job will be deleted after submiting share by block template update - uint64 jobDiff = job.diff; + uint64 jobDiff = job->diff; if (_server->SubmitBlock(block)) { std::string query("INSERT INTO `shares` (`rem_host`, `username`, `our_result`, `upstream_result`, `reason`, `solution`, `time`, `difficulty`) VALUES "); @@ -259,7 +259,7 @@ namespace Stratum return; } } else { - DataMgr::Instance()->Push(Share(_ip, username, true, "", Util::Date(), job.diff)); + DataMgr::Instance()->Push(Share(_ip, username, true, "", Util::Date(), job->diff)); JSON response; response["id"] = msg["id"]; @@ -330,17 +330,17 @@ namespace Stratum } } - Job Client::GetJob() + Job* Client::GetJob() { - Job job; - job.block = _server->GetWork(); - job.diff = _diff; - job.jobTarget = Bitcoin::DiffToTarget(job.diff); - job.blockTarget = Bitcoin::TargetFromBits(job.block->bits); + Job* job = new Job(); + job->block = _server->GetWork(); + job->diff = _diff; + job->jobTarget = Bitcoin::DiffToTarget(job->diff); + job->blockTarget = Bitcoin::TargetFromBits(job->block->bits); // Serialize transaction ByteBuffer coinbasebuf; - coinbasebuf << job.block->tx[0]; + coinbasebuf << job->block->tx[0]; BinaryData coinbase = coinbasebuf.Binary(); // Split coinbase @@ -350,8 +350,8 @@ namespace Stratum // tx.script (varint) - 2 bytes // tx.script (block height) - 4 bytes uint32 cbsplit = 4 + 32 + 4 + 2 + 4; - job.coinbase1 = BinaryData(coinbase.begin(), coinbase.begin() + cbsplit); - job.coinbase2 = BinaryData(coinbase.begin() + cbsplit + 8, coinbase.end()); // plus extranonce size + job->coinbase1 = BinaryData(coinbase.begin(), coinbase.begin() + cbsplit); + job->coinbase2 = BinaryData(coinbase.begin() + cbsplit + 8, coinbase.end()); // plus extranonce size return job; } diff --git a/src/server/poolserver/Stratum/Client.h b/src/server/poolserver/Stratum/Client.h index 9945a12..5a0945b 100644 --- a/src/server/poolserver/Stratum/Client.h +++ b/src/server/poolserver/Stratum/Client.h @@ -27,12 +27,13 @@ namespace Stratum public: Client(Server* server, asio::io_service& io_service, uint64 id) : _io_service(io_service), _server(server), _socket(io_service), _ioStrand(io_service), _id(id), _subscribed(false), _jobid(0), _shareLimiter(this) { - _diff = sConfig.Get("RetargetMinDiff"); + _diff = sConfig.Get("RetargetStartingDiff"); _minDiff = sConfig.Get("RetargetMinDiff"); } ~Client() { + CleanJobs(); sLog.Info(LOG_STRATUM, "%u: I'm going out! Cya!", _ip); } @@ -89,7 +90,14 @@ namespace Stratum } // Gets new job from the server - Job GetJob(); + Job* GetJob(); + + void CleanJobs() + { + for (std::map::iterator it = _jobs.begin(); it != _jobs.end(); ++it) + delete it->second; + _jobs.clear(); + } // Worker difficulty uint64 GetDifficulty() @@ -177,7 +185,7 @@ namespace Stratum // Jobs bool _subscribed; uint32 _extranonce; - std::map _jobs; + std::map _jobs; uint32 _jobid; // Share limiting diff --git a/src/server/poolserver/Stratum/Job.h b/src/server/poolserver/Stratum/Job.h index 26aba4b..4653d20 100644 --- a/src/server/poolserver/Stratum/Job.h +++ b/src/server/poolserver/Stratum/Job.h @@ -6,6 +6,7 @@ #include "Util.h" #include "BigNum.h" #include +#include namespace Stratum { @@ -24,6 +25,8 @@ namespace Stratum // Returns false if the same share already exists bool SubmitShare(uint64 share) { + boost::unique_lock guard(_mtx); + std::set::iterator it = shares.find(share); if (it == shares.end()) { shares.insert(share); @@ -31,6 +34,9 @@ namespace Stratum } else return false; } + + private: + boost::mutex _mtx; }; } diff --git a/src/server/poolserver/Stratum/ShareLimiter.cpp b/src/server/poolserver/Stratum/ShareLimiter.cpp index 3b5e580..d1084eb 100644 --- a/src/server/poolserver/Stratum/ShareLimiter.cpp +++ b/src/server/poolserver/Stratum/ShareLimiter.cpp @@ -12,14 +12,16 @@ namespace Stratum ++_totalShares; uint64 curTime = Util::Date(); - uint64 sinceLast = curTime - _lastRetarget; + uint64 timeSinceRetarget = curTime - _lastRetargetTime; + uint64 sharesSinceRetarget = _totalShares - _lastRetargetShares; _shares.push_back(ShareLimiterRecord(diff, curTime)); - if (sinceLast < sConfig.Get("RetargetInterval")) + if (timeSinceRetarget < sConfig.Get("RetargetInterval") && sharesSinceRetarget < sConfig.Get("RetargetSharesThreshold")) return true; - _lastRetarget = curTime; + _lastRetargetTime = curTime; + _lastRetargetShares = _totalShares; // Check if miner is ok if (_totalShares > 200 && (double(_totalBadShares)/double(_totalShares)) > 0.9) { @@ -61,7 +63,13 @@ namespace Stratum if (variance < sConfig.Get("RetargetVariance")) return true; - _client->SetDifficulty(newDiff, true); + // If variance is huge, reset difficulty with job discard (DoS prevention) + if (variance > 2000) { + _client->SetDifficulty(newDiff, false); + _client->SendJob(true); + return false; + } else + _client->SetDifficulty(newDiff, true); return true; } diff --git a/src/server/poolserver/Stratum/ShareLimiter.h b/src/server/poolserver/Stratum/ShareLimiter.h index 6299a39..f41c86e 100644 --- a/src/server/poolserver/Stratum/ShareLimiter.h +++ b/src/server/poolserver/Stratum/ShareLimiter.h @@ -25,9 +25,9 @@ namespace Stratum public: ShareLimiter(Client* client) : _client(client), _totalShares(0), _totalBadShares(0) { - // Minus one to prevent crash when interval is zero - _startTime = Util::Date()-1; - _lastRetarget = _startTime; + // Minus to prevent crash when interval is zero + _startTime = Util::Date()-10; + _lastRetargetTime = _startTime; } bool Submit(uint64 diff); @@ -40,7 +40,8 @@ namespace Stratum private: std::deque _shares; Client* _client; - uint64 _lastRetarget; + uint64 _lastRetargetTime; + uint64 _lastRetargetShares; uint64 _startTime; uint64 _totalShares;