diff --git a/CMakeLists.txt b/CMakeLists.txt index 01f44c039..ed932390c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,9 @@ set(HEADERS src/proxy/Server.h src/proxy/splitters/donate/DonateMapper.h src/proxy/splitters/donate/DonateSplitter.h + src/proxy/splitters/extra_nonce/ExtraNonceMapper.h + src/proxy/splitters/extra_nonce/ExtraNonceSplitter.h + src/proxy/splitters/extra_nonce/ExtraNonceStorage.h src/proxy/splitters/nicehash/NonceMapper.h src/proxy/splitters/nicehash/NonceSplitter.h src/proxy/splitters/nicehash/NonceStorage.h @@ -93,6 +96,9 @@ set(SOURCES src/proxy/Server.cpp src/proxy/splitters/donate/DonateMapper.cpp src/proxy/splitters/donate/DonateSplitter.cpp + src/proxy/splitters/extra_nonce/ExtraNonceMapper.cpp + src/proxy/splitters/extra_nonce/ExtraNonceSplitter.cpp + src/proxy/splitters/extra_nonce/ExtraNonceStorage.cpp src/proxy/splitters/nicehash/NonceMapper.cpp src/proxy/splitters/nicehash/NonceSplitter.cpp src/proxy/splitters/nicehash/NonceStorage.cpp diff --git a/src/base/net/stratum/DaemonClient.cpp b/src/base/net/stratum/DaemonClient.cpp index 43493e752..c1d3d3a41 100644 --- a/src/base/net/stratum/DaemonClient.cpp +++ b/src/base/net/stratum/DaemonClient.cpp @@ -96,7 +96,7 @@ bool xmrig::DaemonClient::isTLS() const int64_t xmrig::DaemonClient::submit(const JobResult &result) { - if (result.jobId != (m_blocktemplateStr.data() + m_blocktemplateStr.size() - 32)) { + if (result.jobId != m_currentJobId) { return -1; } @@ -114,6 +114,10 @@ int64_t xmrig::DaemonClient::submit(const JobResult &result) memcpy(data + m_blocktemplate.eph_public_key_index * 2, result.sig_data + 32 * 2, 32 * 2); } + if (result.extra_nonce >= 0) { + Cvt::toHex(data + m_blocktemplate.tx_extra_nonce_index * 2, 8, reinterpret_cast(&result.extra_nonce), 4); + } + # else Cvt::toHex(data + m_job.nonceOffset() * 2, 8, reinterpret_cast(&result.nonce), 4); @@ -277,6 +281,19 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code) return false; } +# ifdef XMRIG_PROXY_PROJECT + const size_t k = m_blocktemplate.miner_tx_prefix_begin_index; + job.setMinerTx( + m_blocktemplate.raw_blob.data() + k, + m_blocktemplate.raw_blob.data() + m_blocktemplate.miner_tx_prefix_end_index, + m_blocktemplate.eph_public_key_index - k, + m_blocktemplate.tx_pubkey_index - k, + m_blocktemplate.tx_extra_nonce_index - k, + m_blocktemplate.tx_extra_nonce_size, + m_blocktemplate.miner_tx_merkle_tree_branch + ); +# endif + m_blockhashingblob = Json::getString(params, "blockhashing_blob"); if (m_blocktemplate.has_miner_signature) { @@ -308,13 +325,6 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code) # ifdef XMRIG_PROXY_PROJECT job.setSpendSecretKey(secret_spendkey); - job.setMinerTx( - m_blocktemplate.raw_blob.data() + m_blocktemplate.miner_tx_prefix_begin_index, - m_blocktemplate.raw_blob.data() + m_blocktemplate.miner_tx_prefix_end_index, - m_blocktemplate.eph_public_key_index - m_blocktemplate.miner_tx_prefix_begin_index, - m_blocktemplate.tx_pubkey_index - m_blocktemplate.miner_tx_prefix_begin_index, - m_blocktemplate.miner_tx_merkle_tree_branch - ); # else uint8_t secret_viewkey[32]; derive_view_secret_key(secret_spendkey, secret_viewkey); @@ -377,7 +387,8 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code) job.setHeight(Json::getUint64(params, kHeight)); job.setDiff(Json::getUint64(params, "difficulty")); - job.setId(blocktemplate.data() + blocktemplate.size() - 32); + m_currentJobId = Cvt::toHex(Cvt::randomBytes(4)); + job.setId(m_currentJobId); m_job = std::move(job); m_blocktemplateStr = std::move(blocktemplate); diff --git a/src/base/net/stratum/DaemonClient.h b/src/base/net/stratum/DaemonClient.h index 45a844646..f701240e5 100644 --- a/src/base/net/stratum/DaemonClient.h +++ b/src/base/net/stratum/DaemonClient.h @@ -84,6 +84,7 @@ class DaemonClient : public BaseClient, public ITimerListener, public IHttpListe } m_apiVersion = API_MONERO; std::shared_ptr m_httpListener; + String m_currentJobId; String m_blocktemplateStr; String m_blockhashingblob; String m_prevHash; diff --git a/src/base/net/stratum/Job.cpp b/src/base/net/stratum/Job.cpp index 5e0c0f489..920bf95f7 100644 --- a/src/base/net/stratum/Job.cpp +++ b/src/base/net/stratum/Job.cpp @@ -201,6 +201,8 @@ void xmrig::Job::copy(const Job &other) m_minerTxPrefix = other.m_minerTxPrefix; m_minerTxEphPubKeyOffset = other.m_minerTxEphPubKeyOffset; m_minerTxPubKeyOffset = other.m_minerTxPubKeyOffset; + m_minerTxExtraNonceOffset = other.m_minerTxExtraNonceOffset; + m_minerTxExtraNonceSize = other.m_minerTxExtraNonceSize; m_minerTxMerkleTreeBranch = other.m_minerTxMerkleTreeBranch; # else memcpy(m_ephPublicKey, other.m_ephPublicKey, sizeof(m_ephPublicKey)); @@ -254,6 +256,8 @@ void xmrig::Job::move(Job &&other) m_minerTxPrefix = std::move(other.m_minerTxPrefix); m_minerTxEphPubKeyOffset = other.m_minerTxEphPubKeyOffset; m_minerTxPubKeyOffset = other.m_minerTxPubKeyOffset; + m_minerTxExtraNonceOffset = other.m_minerTxExtraNonceOffset; + m_minerTxExtraNonceSize = other.m_minerTxExtraNonceSize; m_minerTxMerkleTreeBranch = std::move(other.m_minerTxMerkleTreeBranch); # else memcpy(m_ephPublicKey, other.m_ephPublicKey, sizeof(m_ephPublicKey)); @@ -278,16 +282,24 @@ void xmrig::Job::setSpendSecretKey(const uint8_t *key) } -void xmrig::Job::setMinerTx(const uint8_t *begin, const uint8_t *end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, const Buffer &minerTxMerkleTreeBranch) +void xmrig::Job::setMinerTx(const uint8_t *begin, const uint8_t *end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, size_t minerTxExtraNonceOffset, size_t minerTxExtraNonceSize, const Buffer &minerTxMerkleTreeBranch) { m_minerTxPrefix.assign(begin, end); m_minerTxEphPubKeyOffset = minerTxEphPubKeyOffset; m_minerTxPubKeyOffset = minerTxPubKeyOffset; + m_minerTxExtraNonceOffset = minerTxExtraNonceOffset; + m_minerTxExtraNonceSize = minerTxExtraNonceSize; m_minerTxMerkleTreeBranch = minerTxMerkleTreeBranch; } -void xmrig::Job::generateHashingBlob(String &signatureData) +void xmrig::Job::setExtraNonceInMinerTx(uint32_t extra_nonce) +{ + memcpy(m_minerTxPrefix.data() + m_minerTxExtraNonceOffset, &extra_nonce, std::min(m_minerTxExtraNonceSize, sizeof(uint32_t))); +} + + +void xmrig::Job::generateSignatureData(String &signatureData) const { uint8_t* eph_public_key = m_minerTxPrefix.data() + m_minerTxEphPubKeyOffset; uint8_t* txkey_pub = m_minerTxPrefix.data() + m_minerTxPubKeyOffset; @@ -309,13 +321,22 @@ void xmrig::Job::generateHashingBlob(String &signatureData) derive_secret_key(derivation, 0, m_spendSecretKey, buf + 64); signatureData = Cvt::toHex(buf, sizeof(buf)); +} +void xmrig::Job::generateHashingBlob(String &blob) const +{ uint8_t root_hash[32]; const uint8_t* p = m_minerTxPrefix.data(); BlockTemplate::CalculateRootHash(p, p + m_minerTxPrefix.size(), m_minerTxMerkleTreeBranch, root_hash); - const uint64_t offset = nonceOffset() + nonceSize() + BlockTemplate::SIGNATURE_SIZE + 2 /* vote */; - Cvt::toHex(m_rawBlob + offset * 2, 64, root_hash, 32); + uint64_t root_hash_offset = nonceOffset() + nonceSize(); + + if (m_hasMinerSignature) { + root_hash_offset += BlockTemplate::SIGNATURE_SIZE + 2 /* vote */; + } + + blob = rawBlob(); + Cvt::toHex(blob.data() + root_hash_offset * 2, 64, root_hash, BlockTemplate::HASH_SIZE); } diff --git a/src/base/net/stratum/Job.h b/src/base/net/stratum/Job.h index 42e6947f3..c72902756 100644 --- a/src/base/net/stratum/Job.h +++ b/src/base/net/stratum/Job.h @@ -119,9 +119,11 @@ class Job # endif # ifdef XMRIG_PROXY_PROJECT - void setSpendSecretKey(const uint8_t *key); - void setMinerTx(const uint8_t *begin, const uint8_t *end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, const Buffer &minerTxMerkleTreeBranch); - void generateHashingBlob(String &signatureData); + void setSpendSecretKey(const uint8_t* key); + void setMinerTx(const uint8_t* begin, const uint8_t* end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, size_t minerTxExtraNonceOffset, size_t minerTxExtraNonceSize, const Buffer& minerTxMerkleTreeBranch); + void setExtraNonceInMinerTx(uint32_t extra_nonce); + void generateSignatureData(String& signatureData) const; + void generateHashingBlob(String& blob) const; # else inline const uint8_t* ephSecretKey() const { return m_hasMinerSignature ? m_ephSecretKey : nullptr; } @@ -170,6 +172,8 @@ class Job mutable Buffer m_minerTxPrefix; size_t m_minerTxEphPubKeyOffset = 0; size_t m_minerTxPubKeyOffset = 0; + size_t m_minerTxExtraNonceOffset = 0; + size_t m_minerTxExtraNonceSize = 0; Buffer m_minerTxMerkleTreeBranch; # else // Miner signatures diff --git a/src/base/tools/cryptonote/BlobReader.h b/src/base/tools/cryptonote/BlobReader.h index 58f81e2dd..f10bcd88b 100644 --- a/src/base/tools/cryptonote/BlobReader.h +++ b/src/base/tools/cryptonote/BlobReader.h @@ -61,6 +61,8 @@ class CBlobReader inline size_t index() const { return m_index; } + inline void skip(size_t N) { m_index += N; } + private: inline bool getByte(uint8_t& data) { diff --git a/src/base/tools/cryptonote/BlockTemplate.cpp b/src/base/tools/cryptonote/BlockTemplate.cpp index 0b54c3998..5449ec23c 100644 --- a/src/base/tools/cryptonote/BlockTemplate.cpp +++ b/src/base/tools/cryptonote/BlockTemplate.cpp @@ -86,13 +86,35 @@ bool BlockTemplate::Init(const String& blockTemplate, Coin coin) ar(eph_public_key); ar(extra_size); - tx_pubkey_index = ar.index() + 1; + const uint64_t tx_extra_index = ar.index(); ar.readItems(extra, extra_size); - // First thing in tx_extra must be TX_EXTRA_TAG_PUBKEY - if (extra[0] != 0x01) - return false; + CBlobReader ar_extra(extra.data(), extra_size); + + tx_extra_nonce_size = 0; + tx_extra_nonce_index = 0; + + while (ar_extra.index() < extra_size) { + uint64_t extra_tag; + ar_extra(extra_tag); + + switch (extra_tag) { + case 0x01: // TX_EXTRA_TAG_PUBKEY + tx_pubkey_index = tx_extra_index + ar_extra.index(); + ar_extra.skip(KEY_SIZE); + break; + + case 0x02: // TX_EXTRA_NONCE + ar_extra(tx_extra_nonce_size); + tx_extra_nonce_index = tx_extra_index + ar_extra.index(); + ar_extra.skip(tx_extra_nonce_size); + break; + + default: + return false; // TODO: handle other tags + } + } miner_tx_prefix_end_index = ar.index(); // Prefix end diff --git a/src/base/tools/cryptonote/BlockTemplate.h b/src/base/tools/cryptonote/BlockTemplate.h index e80ece141..4a6ef8051 100644 --- a/src/base/tools/cryptonote/BlockTemplate.h +++ b/src/base/tools/cryptonote/BlockTemplate.h @@ -42,6 +42,8 @@ struct BlockTemplate Buffer raw_blob; size_t eph_public_key_index; size_t tx_pubkey_index; + uint64_t tx_extra_nonce_size; + size_t tx_extra_nonce_index; size_t miner_tx_prefix_begin_index; size_t miner_tx_prefix_end_index; diff --git a/src/core/config/Config.cpp b/src/core/config/Config.cpp index 34541866a..a6fb29a32 100644 --- a/src/core/config/Config.cpp +++ b/src/core/config/Config.cpp @@ -34,7 +34,7 @@ namespace xmrig { -static const std::array modeNames = { "nicehash", "simple" }; +static const std::array modeNames = { "nicehash", "simple", "extra_nonce"}; } // namespace xmrig diff --git a/src/core/config/Config.h b/src/core/config/Config.h index f851a400c..208334f69 100644 --- a/src/core/config/Config.h +++ b/src/core/config/Config.h @@ -44,7 +44,8 @@ class Config : public BaseConfig public: enum Mode { NICEHASH_MODE, - SIMPLE_MODE + SIMPLE_MODE, + EXTRA_NONCE_MODE, }; Config() = default; diff --git a/src/net/JobResult.cpp b/src/net/JobResult.cpp index e487ae06f..3ee88565d 100644 --- a/src/net/JobResult.cpp +++ b/src/net/JobResult.cpp @@ -25,14 +25,15 @@ #include -xmrig::JobResult::JobResult(int64_t id, const char *jobId, const char *nonce, const char *result, const xmrig::Algorithm &algorithm, const char* sig, const char* sig_data) : +xmrig::JobResult::JobResult(int64_t id, const char *jobId, const char *nonce, const char *result, const xmrig::Algorithm &algorithm, const char* sig, const char* sig_data, int64_t extra_nonce) : algorithm(algorithm), nonce(nonce), result(result), sig(sig), sig_data(sig_data), id(id), - jobId(jobId) + jobId(jobId), + extra_nonce(extra_nonce) { if (result && strlen(result) == 64) { uint64_t target = 0; diff --git a/src/net/JobResult.h b/src/net/JobResult.h index cbc3090c3..9996caacf 100644 --- a/src/net/JobResult.h +++ b/src/net/JobResult.h @@ -37,7 +37,7 @@ class JobResult static constexpr uint32_t backend = 0; JobResult() = default; - JobResult(int64_t id, const char *jobId, const char *nonce, const char *result, const xmrig::Algorithm &algorithm, const char* sig, const char* sig_data); + JobResult(int64_t id, const char *jobId, const char *nonce, const char *result, const xmrig::Algorithm &algorithm, const char* sig, const char* sig_data, int64_t extra_nonce); bool isCompatible(uint8_t fixedByte) const; bool isValid() const; @@ -45,16 +45,17 @@ class JobResult inline uint64_t actualDiff() const { return m_actualDiff; } Algorithm algorithm; - const char *nonce = nullptr; - const char *result = nullptr; - const char *sig = nullptr; - const char *sig_data = nullptr; - const int64_t id = 0; + const char *nonce = nullptr; + const char *result = nullptr; + const char *sig = nullptr; + const char *sig_data = nullptr; + const int64_t id = 0; + const int64_t extra_nonce = -1; String jobId; - uint64_t diff = 0; + uint64_t diff = 0; private: - uint64_t m_actualDiff = 0; + uint64_t m_actualDiff = 0; }; diff --git a/src/proxy/Miner.cpp b/src/proxy/Miner.cpp index 8aded119d..9ef253155 100644 --- a/src/proxy/Miner.cpp +++ b/src/proxy/Miner.cpp @@ -140,7 +140,7 @@ void xmrig::Miner::replyWithError(int64_t id, const char *message) } -void xmrig::Miner::setJob(Job &job) +void xmrig::Miner::setJob(Job &job, int64_t extra_nonce) { using namespace rapidjson; @@ -158,14 +158,27 @@ void xmrig::Miner::setJob(Job &job) customDiff = true; } + const char* blob = job.rawBlob(); + String tmp_blob; + if (job.hasMinerSignature()) { - job.generateHashingBlob(m_signatureData); + job.generateSignatureData(m_signatureData); } else if (!job.rawSigKey().isNull()) { m_signatureData = job.rawSigKey(); } - sendJob(job.rawBlob(), job.id().data(), customDiff ? m_sendBuf : job.rawTarget(), job.algorithm().shortName(), job.height(), job.rawSeedHash(), m_signatureData); + if (extra_nonce >= 0) { + m_extraNonce = extra_nonce; + job.setExtraNonceInMinerTx(static_cast(m_extraNonce)); + } + + if (job.hasMinerSignature() || (extra_nonce >= 0)) { + job.generateHashingBlob(tmp_blob); + blob = tmp_blob; + } + + sendJob(blob, job.id().data(), customDiff ? m_sendBuf : job.rawTarget(), job.algorithm().shortName(), job.height(), job.rawSeedHash(), m_signatureData); } @@ -237,7 +250,7 @@ bool xmrig::Miner::parseRequest(int64_t id, const char *method, const rapidjson: Algorithm algorithm(Json::getString(params, "algo")); - SubmitEvent *event = SubmitEvent::create(this, id, Json::getString(params, "job_id"), Json::getString(params, "nonce"), Json::getString(params, "result"), algorithm, Json::getString(params, "sig"), m_signatureData); + SubmitEvent *event = SubmitEvent::create(this, id, Json::getString(params, "job_id"), Json::getString(params, "nonce"), Json::getString(params, "result"), algorithm, Json::getString(params, "sig"), m_signatureData, m_extraNonce); if (!event->request.isValid() || event->request.actualDiff() < diff()) { event->reject(Error::LowDifficulty); diff --git a/src/proxy/Miner.h b/src/proxy/Miner.h index eaea891f3..d9afcc29f 100644 --- a/src/proxy/Miner.h +++ b/src/proxy/Miner.h @@ -74,7 +74,7 @@ class Miner : public ILineListener bool accept(uv_stream_t *server); void forwardJob(const Job &job, const char *algo, const char *sig_key); void replyWithError(int64_t id, const char *message); - void setJob(Job &job); + void setJob(Job &job, int64_t extra_nonce = -1); void success(int64_t id, const char *status); inline bool hasExtension(Extension ext) const noexcept { return m_extensions.test(ext); } @@ -156,6 +156,7 @@ class Miner : public ILineListener uint64_t m_timestamp; uint64_t m_tx = 0; uint8_t m_fixedByte = 0; + int64_t m_extraNonce = -1; uintptr_t m_key; uv_tcp_t *m_socket; diff --git a/src/proxy/Proxy.cpp b/src/proxy/Proxy.cpp index 1f49cdd01..9554a2eaf 100644 --- a/src/proxy/Proxy.cpp +++ b/src/proxy/Proxy.cpp @@ -49,6 +49,7 @@ #include "proxy/ProxyDebug.h" #include "proxy/Server.h" #include "proxy/splitters/donate/DonateSplitter.h" +#include "proxy/splitters/extra_nonce/ExtraNonceSplitter.h" #include "proxy/splitters/nicehash/NonceSplitter.h" #include "proxy/splitters/simple/SimpleSplitter.h" #include "proxy/Stats.h" @@ -77,6 +78,13 @@ xmrig::Proxy::Proxy(Controller *controller) : if (controller->config()->mode() == Config::NICEHASH_MODE) { splitter = new NonceSplitter(controller); } + else if (controller->config()->mode() == Config::EXTRA_NONCE_MODE) { + splitter = ExtraNonceSplitter::Create(controller); + if (!splitter) { + LOG_WARN("Switching to nicehash mode"); + splitter = new NonceSplitter(controller); + } + } else { splitter = new SimpleSplitter(controller); } diff --git a/src/proxy/events/SubmitEvent.h b/src/proxy/events/SubmitEvent.h index f4bad9a5c..55460a815 100644 --- a/src/proxy/events/SubmitEvent.h +++ b/src/proxy/events/SubmitEvent.h @@ -37,9 +37,9 @@ namespace xmrig { class SubmitEvent : public MinerEvent { public: - static inline SubmitEvent *create(Miner *miner, int64_t id, const char *jobId, const char *nonce, const char *result, const Algorithm &algorithm, const char* sig, const char* sig_data) + static inline SubmitEvent *create(Miner *miner, int64_t id, const char *jobId, const char *nonce, const char *result, const Algorithm &algorithm, const char* sig, const char* sig_data, int64_t extra_nonce) { - return new (m_buf) SubmitEvent(miner, id, jobId, nonce, result, algorithm, sig, sig_data); + return new (m_buf) SubmitEvent(miner, id, jobId, nonce, result, algorithm, sig, sig_data, extra_nonce); } @@ -54,9 +54,9 @@ class SubmitEvent : public MinerEvent protected: - inline SubmitEvent(Miner *miner, int64_t id, const char *jobId, const char *nonce, const char *result, const Algorithm &algorithm, const char* sig, const char* sig_data) + inline SubmitEvent(Miner *miner, int64_t id, const char *jobId, const char *nonce, const char *result, const Algorithm &algorithm, const char* sig, const char* sig_data, int64_t extra_nonce) : MinerEvent(SubmitType, miner), - request(id, jobId, nonce, result, algorithm, sig, sig_data), + request(id, jobId, nonce, result, algorithm, sig, sig_data, extra_nonce), m_error(Error::NoError) {} diff --git a/src/proxy/splitters/extra_nonce/ExtraNonceMapper.cpp b/src/proxy/splitters/extra_nonce/ExtraNonceMapper.cpp new file mode 100644 index 000000000..1897743c6 --- /dev/null +++ b/src/proxy/splitters/extra_nonce/ExtraNonceMapper.cpp @@ -0,0 +1,307 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2020 SChernykh + * Copyright 2016-2020 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include + + +#include "proxy/splitters/extra_nonce/ExtraNonceMapper.h" +#include "base/io/log/Log.h" +#include "base/io/log/Tags.h" +#include "base/net/stratum/Client.h" +#include "base/net/stratum/Pools.h" +#include "core/config/Config.h" +#include "core/Controller.h" +#include "net/JobResult.h" +#include "net/strategies/DonateStrategy.h" +#include "proxy/Counters.h" +#include "proxy/Error.h" +#include "proxy/events/AcceptEvent.h" +#include "proxy/events/SubmitEvent.h" +#include "proxy/Miner.h" +#include "proxy/splitters/extra_nonce/ExtraNonceStorage.h" + + +xmrig::ExtraNonceMapper::ExtraNonceMapper(size_t, Controller *controller) : + m_controller(controller) +{ + m_storage = new ExtraNonceStorage(); + m_strategy = controller->config()->pools().createStrategy(this); + + if (controller->config()->pools().donateLevel() > 0) { + m_donate = new DonateStrategy(controller, this); + } +} + + +xmrig::ExtraNonceMapper::~ExtraNonceMapper() +{ + delete m_pending; + delete m_strategy; + delete m_storage; + delete m_donate; +} + + +bool xmrig::ExtraNonceMapper::add(Miner *miner) +{ + miner->setExtension(Miner::EXT_ALGO, m_controller->config()->hasAlgoExt()); + + if (!m_storage->add(miner)) { + return false; + } + + if (isSuspended()) { + connect(); + } + + miner->setMapperId(0); + return true; +} + + +bool xmrig::ExtraNonceMapper::isActive() const +{ + return m_storage->isActive() && !isSuspended(); +} + + +void xmrig::ExtraNonceMapper::gc() +{ + if (isSuspended()) { + m_suspended++; + return; + } +} + + +void xmrig::ExtraNonceMapper::reload(const Pools &pools) +{ + delete m_pending; + + m_pending = pools.createStrategy(this); + m_pending->connect(); +} + + +void xmrig::ExtraNonceMapper::remove(const Miner *miner) +{ + m_storage->remove(miner); +} + + +void xmrig::ExtraNonceMapper::start() +{ + connect(); +} + + +void xmrig::ExtraNonceMapper::submit(SubmitEvent *event) +{ + if (!m_storage->isActive()) { + return event->reject(Error::BadGateway); + } + + if (!m_storage->isValidJobId(event->request.jobId)) { + return event->reject(Error::InvalidJobId); + } + + if (event->request.algorithm.isValid() && event->request.algorithm != m_storage->job().algorithm()) { + return event->reject(Error::IncorrectAlgorithm); + } + + JobResult req = event->request; + req.diff = m_storage->job().diff(); + + IStrategy *strategy = m_donate && m_donate->isActive() ? m_donate : m_strategy; + + m_results[strategy->submit(req)] = SubmitCtx(req.id, event->miner()->id()); +} + + +void xmrig::ExtraNonceMapper::tick(uint64_t, uint64_t now) +{ + m_strategy->tick(now); + + if (m_donate) { + m_donate->tick(now); + + if (m_donate->isActive() && m_donate->hasPendingJob() && m_donate->reschedule()) { + const auto &pending = m_donate->pending(); + setJob(pending.host.data(), pending.port, pending.job); + } + } +} + + +#ifdef APP_DEVEL +void xmrig::ExtraNonceMapper::printState() +{ + if (isSuspended()) { + return; + } + + m_storage->printState(0); +} +#endif + + +void xmrig::ExtraNonceMapper::onActive(IStrategy *strategy, IClient *client) +{ + m_storage->setActive(true); + + if (client->id() == -1) { + return; + } + + if (m_pending && strategy == m_pending) { + delete m_strategy; + + m_strategy = strategy; + m_pending = nullptr; + } + + if (m_controller->config()->isVerbose()) { + const char *tlsVersion = client->tlsVersion(); + + LOG_INFO("%s " CYAN("%04u ") WHITE_BOLD("use %s ") CYAN_BOLD("%s:%d ") GREEN_BOLD("%s") " \x1B[1;30m%s ", + Tags::network(), 0, client->mode(), client->pool().host().data(), client->pool().port(), tlsVersion ? tlsVersion : "", client->ip().data()); + + const char *fingerprint = client->tlsFingerprint(); + if (fingerprint != nullptr) { + LOG_INFO("%s \x1B[1;30mfingerprint (SHA-256): \"%s\"", Tags::network(), fingerprint); + } + } +} + + +void xmrig::ExtraNonceMapper::onJob(IStrategy *, IClient *client, const Job &job, const rapidjson::Value &) +{ + if (m_donate) { + if (m_donate->isActive() && client->id() != -1 && !m_donate->reschedule()) { + m_donate->save(client, job); + return; + } + + m_donate->setAlgo(job.algorithm()); + } + + setJob(client->pool().host(), client->pool().port(), job); +} + + +void xmrig::ExtraNonceMapper::onLogin(IStrategy *strategy, IClient *client, rapidjson::Document &doc, rapidjson::Value ¶ms) +{ + +} + + +void xmrig::ExtraNonceMapper::onPause(IStrategy *) +{ + m_storage->setActive(false); + + if (!isSuspended()) { + LOG_ERR("%s " CYAN("%04u ") RED("no active pools, stop"), Tags::network(), 0); + } +} + + +void xmrig::ExtraNonceMapper::onResultAccepted(IStrategy *, IClient *client, const SubmitResult &result, const char *error) +{ + const SubmitCtx ctx = submitCtx(result.seq); + + AcceptEvent::start(0, ctx.miner, result, client->id() == -1, false, error); + + if (!ctx.miner) { + return; + } + + if (error) { + ctx.miner->replyWithError(ctx.id, error); + } + else { + ctx.miner->success(ctx.id, "OK"); + } +} + + +void xmrig::ExtraNonceMapper::onVerifyAlgorithm(IStrategy *strategy, const IClient *client, const Algorithm &algorithm, bool *ok) +{ + +} + + +xmrig::SubmitCtx xmrig::ExtraNonceMapper::submitCtx(int64_t seq) +{ + if (!m_results.count(seq)) { + return {}; + } + + SubmitCtx ctx = m_results.at(seq); + ctx.miner = m_storage->miner(ctx.minerId); + + auto it = m_results.find(seq); + if (it != m_results.end()) { + m_results.erase(it); + } + + return ctx; +} + + +void xmrig::ExtraNonceMapper::connect() +{ + m_suspended = 0; + m_strategy->connect(); + + if (m_donate) { + m_donate->connect(); + } +} + + +void xmrig::ExtraNonceMapper::setJob(const char *host, int port, const Job &job) +{ + if (m_controller->config()->isVerbose()) { + LOG_INFO("%s " CYAN("%04u ") MAGENTA_BOLD("new job") " from " WHITE_BOLD("%s:%d") " diff " WHITE_BOLD("%" PRIu64) " algo " WHITE_BOLD("%s") " height " WHITE_BOLD("%" PRIu64), + Tags::network(), 0, host, port, job.diff(), job.algorithm().shortName(), job.height()); + } + + m_storage->setJob(job); +} + + +void xmrig::ExtraNonceMapper::suspend() +{ + m_suspended = 1; + m_storage->setActive(false); + m_storage->reset(); + m_strategy->stop(); + + if (m_donate) { + m_donate->stop(); + } +} diff --git a/src/proxy/splitters/extra_nonce/ExtraNonceMapper.h b/src/proxy/splitters/extra_nonce/ExtraNonceMapper.h new file mode 100644 index 000000000..077c533e7 --- /dev/null +++ b/src/proxy/splitters/extra_nonce/ExtraNonceMapper.h @@ -0,0 +1,115 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2020 SChernykh + * Copyright 2016-2020 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef XMRIG_EXTRANONCEMAPPER_H +#define XMRIG_EXTRANONCEMAPPER_H + + +#include +#include +#include + + +#include "base/kernel/interfaces/IStrategyListener.h" +#include "base/net/stratum/Job.h" +#include "base/tools/Object.h" + + +namespace xmrig { + + +class Controller; +class DonateStrategy; +class IStrategy; +class JobResult; +class Miner; +class ExtraNonceStorage; +class Pools; +class SubmitEvent; + + +class SubmitCtx +{ +public: + inline SubmitCtx() : id(0), minerId(0), miner(nullptr) {} + inline SubmitCtx(int64_t id, int64_t minerId) : id(id), minerId(minerId), miner(nullptr) {} + + int64_t id; + int64_t minerId; + Miner *miner; +}; + + +class ExtraNonceMapper : public IStrategyListener +{ +public: + XMRIG_DISABLE_COPY_MOVE_DEFAULT(ExtraNonceMapper) + + ExtraNonceMapper(size_t id, Controller *controller); + ~ExtraNonceMapper() override; + + bool add(Miner *miner); + bool isActive() const; + void gc(); + void reload(const Pools &pools); + void remove(const Miner *miner); + void start(); + void submit(SubmitEvent *event); + void tick(uint64_t ticks, uint64_t now); + + inline bool isSuspended() const { return m_suspended > 0; } + inline int suspended() const { return m_suspended; } + +# ifdef APP_DEVEL + void printState(); +# endif + +protected: + void onActive(IStrategy *strategy, IClient *client) override; + void onJob(IStrategy *strategy, IClient *client, const Job &job, const rapidjson::Value ¶ms) override; + void onLogin(IStrategy *strategy, IClient *client, rapidjson::Document &doc, rapidjson::Value ¶ms) override; + void onPause(IStrategy *strategy) override; + void onResultAccepted(IStrategy *strategy, IClient *client, const SubmitResult &result, const char *error) override; + void onVerifyAlgorithm(IStrategy *strategy, const IClient *client, const Algorithm &algorithm, bool *ok) override; + +private: + SubmitCtx submitCtx(int64_t seq); + void connect(); + void setJob(const char *host, int port, const Job &job); + void suspend(); + + Controller *m_controller; + DonateStrategy *m_donate = nullptr; + int m_suspended = 0; + IStrategy *m_pending = nullptr; + IStrategy *m_strategy; + ExtraNonceStorage *m_storage; + std::map m_results; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_EXTRANONCEMAPPER_H */ diff --git a/src/proxy/splitters/extra_nonce/ExtraNonceSplitter.cpp b/src/proxy/splitters/extra_nonce/ExtraNonceSplitter.cpp new file mode 100644 index 000000000..38b667845 --- /dev/null +++ b/src/proxy/splitters/extra_nonce/ExtraNonceSplitter.cpp @@ -0,0 +1,170 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2020 SChernykh + * Copyright 2016-2020 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "proxy/splitters/extra_nonce/ExtraNonceSplitter.h" +#include "base/io/log/Log.h" +#include "core/config/Config.h" +#include "core/Controller.h" +#include "proxy/Counters.h" +#include "proxy/events/CloseEvent.h" +#include "proxy/events/LoginEvent.h" +#include "proxy/events/SubmitEvent.h" +#include "proxy/Miner.h" +#include "proxy/splitters/extra_nonce/ExtraNonceMapper.h" +#include "Summary.h" + + +#define LABEL(x) " \x1B[01;30m" x ":\x1B[0m " + + +xmrig::ExtraNonceSplitter* xmrig::ExtraNonceSplitter::Create(Controller* controller) +{ + for (const Pool& pool : controller->config()->pools().data()) { + if ((pool.algorithm() == Algorithm::ASTROBWT_DERO) || (pool.coin() == Coin::DERO)) { + LOG_ERR("extra_nonce mode is incompatible with Dero mining"); + return nullptr; + } + if (pool.mode() != Pool::MODE_DAEMON) { + LOG_ERR("extra_nonce mode can only be used when mining to daemon"); + return nullptr; + } + } + return new ExtraNonceSplitter(controller); +} + + +xmrig::ExtraNonceSplitter::ExtraNonceSplitter(Controller *controller) : Splitter(controller) +{ +} + + +xmrig::ExtraNonceSplitter::~ExtraNonceSplitter() +{ + delete m_upstream; +} + + +xmrig::Upstreams xmrig::ExtraNonceSplitter::upstreams() const +{ + return { m_upstream->isActive() ? 1U : 0U, m_upstream->isSuspended() ? 1U : 0U, 1U }; +} + + +void xmrig::ExtraNonceSplitter::connect() +{ + m_upstream = new ExtraNonceMapper(0, m_controller); + m_upstream->start(); +} + + +void xmrig::ExtraNonceSplitter::gc() +{ + m_upstream->gc(); +} + + +void xmrig::ExtraNonceSplitter::printConnections() +{ + const auto info = upstreams(); + const auto ratio = info.ratio(Counters::miners()); + + LOG_INFO("\x1B[01;32m* \x1B[01;37mupstreams\x1B[0m" LABEL("active") "%s%" PRIu64 "\x1B[0m" LABEL("sleep") "\x1B[01;37m%" PRIu64 "\x1B[0m" LABEL("error") "%s%" PRIu64 "\x1B[0m" LABEL("total") "\x1B[01;37m%" PRIu64, + info.active ? "\x1B[01;32m" : "\x1B[01;31m", info.active, info.sleep, info.error ? "\x1B[01;31m" : "\x1B[01;37m", info.error, info.total); + + LOG_INFO("\x1B[01;32m* \x1B[01;37mminers \x1B[0m" LABEL("active") "%s%" PRIu64 "\x1B[0m" LABEL("max") "\x1B[01;37m%" PRIu64 "\x1B[0m" LABEL("ratio") "%s1:%3.1f", + Counters::miners() ? "\x1B[01;32m" : "\x1B[01;31m", Counters::miners(), Counters::maxMiners(), (ratio > 200 ? "\x1B[01;32m" : "\x1B[01;33m"), ratio); +} + + +void xmrig::ExtraNonceSplitter::tick(uint64_t ticks) +{ + const uint64_t now = uv_now(uv_default_loop()); + m_upstream->tick(ticks, now); +} + + +#ifdef APP_DEVEL +void xmrig::ExtraNonceSplitter::printState() +{ + m_upstream->printState(); +} +#endif + + +void xmrig::ExtraNonceSplitter::onConfigChanged(Config *config, Config *previousConfig) +{ + if (config->pools() != previousConfig->pools()) { + config->pools().print(); + m_upstream->reload(config->pools()); + } +} + + +void xmrig::ExtraNonceSplitter::onEvent(IEvent *event) +{ + switch (event->type()) + { + case IEvent::CloseType: + remove(static_cast(event)->miner()); + break; + + case IEvent::LoginType: + login(static_cast(event)); + break; + + case IEvent::SubmitType: + submit(static_cast(event)); + break; + + default: + break; + } +} + + +void xmrig::ExtraNonceSplitter::login(LoginEvent *event) +{ + if (event->miner()->routeId() != -1) { + return; + } + + if (m_upstream->add(event->miner())) { + return; + } + + connect(); + login(event); +} + + +void xmrig::ExtraNonceSplitter::remove(Miner *miner) +{ + m_upstream->remove(miner); +} + + +void xmrig::ExtraNonceSplitter::submit(SubmitEvent *event) +{ + m_upstream->submit(event); +} diff --git a/src/proxy/splitters/extra_nonce/ExtraNonceSplitter.h b/src/proxy/splitters/extra_nonce/ExtraNonceSplitter.h new file mode 100644 index 000000000..4dbefb786 --- /dev/null +++ b/src/proxy/splitters/extra_nonce/ExtraNonceSplitter.h @@ -0,0 +1,81 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2020 SChernykh + * Copyright 2016-2020 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef XMRIG_EXTRANONCESPLITTER_H +#define XMRIG_EXTRANONCESPLITTER_H + + +#include "base/tools/Object.h" +#include "proxy/splitters/Splitter.h" + + +namespace xmrig { + + +class Controller; +class LoginEvent; +class Miner; +class ExtraNonceMapper; +class Stats; +class SubmitEvent; + + +class ExtraNonceSplitter : public Splitter +{ +public: + XMRIG_DISABLE_COPY_MOVE_DEFAULT(ExtraNonceSplitter) + + static ExtraNonceSplitter* Create(Controller* controller); + ~ExtraNonceSplitter() override; + +protected: + ExtraNonceSplitter(Controller* controller); + + Upstreams upstreams() const override; + void connect() override; + void gc() override; + void printConnections() override; + void tick(uint64_t ticks) override; + +# ifdef APP_DEVEL + void printState() override; +# endif + + inline void onRejectedEvent(IEvent *) override {} + void onConfigChanged(Config *config, Config *previousConfig) override; + void onEvent(IEvent *event) override; + +private: + void login(LoginEvent *event); + void remove(Miner *miner); + void submit(SubmitEvent *event); + + ExtraNonceMapper* m_upstream = nullptr; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_EXTRANONCESPLITTER_H */ diff --git a/src/proxy/splitters/extra_nonce/ExtraNonceStorage.cpp b/src/proxy/splitters/extra_nonce/ExtraNonceStorage.cpp new file mode 100644 index 000000000..5ad9b66e2 --- /dev/null +++ b/src/proxy/splitters/extra_nonce/ExtraNonceStorage.cpp @@ -0,0 +1,108 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2020 SChernykh + * Copyright 2016-2020 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "base/io/log/Log.h" +#include "proxy/Counters.h" +#include "proxy/Miner.h" +#include "proxy/splitters/extra_nonce/ExtraNonceStorage.h" + + +bool xmrig::ExtraNonceStorage::add(Miner *miner) +{ + m_miners[miner->id()] = miner; + + if (isActive()) { + miner->setJob(m_job, m_miners.size() - 1); + } + + return true; +} + + +bool xmrig::ExtraNonceStorage::isValidJobId(const String &id) const +{ + if (m_job.id() == id) { + return true; + } + + if (m_prevJob.isValid() && m_prevJob.id() == id) { + Counters::expired++; + return true; + } + + return false; +} + + +xmrig::Miner *xmrig::ExtraNonceStorage::miner(int64_t id) +{ + auto it = m_miners.find(id); + if (it != m_miners.end()) { + return it->second; + } + + return nullptr; +} + + +void xmrig::ExtraNonceStorage::remove(const Miner *miner) +{ + auto it = m_miners.find(miner->id()); + if (it != m_miners.end()) { + m_miners.erase(it); + } +} + + +void xmrig::ExtraNonceStorage::reset() +{ +} + + +void xmrig::ExtraNonceStorage::setJob(const Job &job) +{ + if (m_job.clientId() == job.clientId()) { + m_prevJob = m_job; + } + else { + m_prevJob.reset(); + } + + m_job = job; + + int64_t extra_nonce = 0; + + for (const auto& m : m_miners) { + m.second->setJob(m_job, extra_nonce); + ++extra_nonce; + } +} + + +#ifdef APP_DEVEL +void xmrig::ExtraNonceStorage::printState(size_t id) +{ + LOG_INFO("#%03u - \x1B[35m%03u\x1B[0m", id, m_miners.size()); +} +#endif diff --git a/src/proxy/splitters/extra_nonce/ExtraNonceStorage.h b/src/proxy/splitters/extra_nonce/ExtraNonceStorage.h new file mode 100644 index 000000000..34f87dbd0 --- /dev/null +++ b/src/proxy/splitters/extra_nonce/ExtraNonceStorage.h @@ -0,0 +1,75 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2020 SChernykh + * Copyright 2016-2020 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef XMRIG_EXTRANONCESTORAGE_H +#define XMRIG_EXTRANONCESTORAGE_H + + +#include + + +#include "base/net/stratum/Job.h" +#include "base/tools/Object.h" + + +namespace xmrig { + + +class Miner; + + +class ExtraNonceStorage +{ +public: + XMRIG_DISABLE_COPY_MOVE(ExtraNonceStorage) + + ExtraNonceStorage() = default; + + bool add(Miner *miner); + bool isValidJobId(const String &id) const; + Miner *miner(int64_t id); + void remove(const Miner *miner); + void reset(); + void setJob(const Job &job); + + inline bool isActive() const { return m_active; } + inline const Job &job() const { return m_job; } + inline void setActive(bool active) { m_active = active; } + +# ifdef APP_DEVEL + void printState(size_t id); +# endif + +private: + bool m_active = false; + Job m_job; + Job m_prevJob; + std::map m_miners; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_EXTRANONCESTORAGE_H */