diff --git a/src/base/io/log/Tags.cpp b/src/base/io/log/Tags.cpp index 3960a14d..75b4ca8a 100644 --- a/src/base/io/log/Tags.cpp +++ b/src/base/io/log/Tags.cpp @@ -36,6 +36,12 @@ const char *xmrig::Tags::network() return tag; } +const char* xmrig::Tags::origin() +{ + static const char* tag = YELLOW_BG_BOLD(WHITE_BOLD_S " origin "); + + return tag; +} const char *xmrig::Tags::signal() { diff --git a/src/base/io/log/Tags.h b/src/base/io/log/Tags.h index 9f690ef2..fc2c7485 100644 --- a/src/base/io/log/Tags.h +++ b/src/base/io/log/Tags.h @@ -32,6 +32,7 @@ class Tags public: static const char *config(); static const char *network(); + static const char *origin(); static const char *signal(); # ifdef XMRIG_MINER_PROJECT diff --git a/src/base/kernel/config/BaseTransform.cpp b/src/base/kernel/config/BaseTransform.cpp index 7df0a795..e6dc4e2e 100644 --- a/src/base/kernel/config/BaseTransform.cpp +++ b/src/base/kernel/config/BaseTransform.cpp @@ -260,6 +260,7 @@ void xmrig::BaseTransform::transform(rapidjson::Document &doc, int key, const ch case IConfig::DryRunKey: /* --dry-run */ case IConfig::HttpEnabledKey: /* --http-enabled */ case IConfig::DaemonKey: /* --daemon */ + case IConfig::SubmitToOriginKey: /* --submit-to-origin */ case IConfig::VerboseKey: /* --verbose */ case IConfig::PauseOnBatteryKey: /* --pause-on-battery */ case IConfig::PauseOnActiveKey: /* --pause-on-active */ @@ -291,6 +292,8 @@ void xmrig::BaseTransform::transformBoolean(rapidjson::Document &doc, int key, b case IConfig::TlsKey: /* --tls */ return add(doc, Pools::kPools, Pool::kTls, enable); + case IConfig::SubmitToOriginKey: /* --submit-to-origin */ + return add(doc, Pools::kPools, Pool::kSubmitToOrigin, enable); # ifdef XMRIG_FEATURE_HTTP case IConfig::DaemonKey: /* --daemon */ return add(doc, Pools::kPools, Pool::kDaemon, enable); diff --git a/src/base/kernel/interfaces/IConfig.h b/src/base/kernel/interfaces/IConfig.h index d5fdb5c9..0e03f786 100644 --- a/src/base/kernel/interfaces/IConfig.h +++ b/src/base/kernel/interfaces/IConfig.h @@ -73,6 +73,7 @@ public: DaemonKey = 1018, DaemonPollKey = 1019, SelfSelectKey = 1028, + SubmitToOriginKey = 1049, DataDirKey = 1035, TitleKey = 1037, NoTitleKey = 1038, diff --git a/src/base/net/stratum/Pool.cpp b/src/base/net/stratum/Pool.cpp index f80f1f62..8148deae 100644 --- a/src/base/net/stratum/Pool.cpp +++ b/src/base/net/stratum/Pool.cpp @@ -79,6 +79,7 @@ const char *Pool::kNicehash = "nicehash"; const char *Pool::kPass = "pass"; const char *Pool::kRigId = "rig-id"; const char *Pool::kSelfSelect = "self-select"; +const char* Pool::kSubmitToOrigin = "submit-to-origin"; const char *Pool::kSOCKS5 = "socks5"; const char *Pool::kTls = "tls"; const char *Pool::kUrl = "url"; @@ -138,6 +139,7 @@ xmrig::Pool::Pool(const rapidjson::Value &object) : if (m_daemon.isValid()) { m_mode = MODE_SELF_SELECT; + m_submit_to_origin = Json::getBool(object, kSubmitToOrigin, false); } else if (Json::getBool(object, kDaemon)) { m_mode = MODE_DAEMON; @@ -237,7 +239,7 @@ xmrig::IClient *xmrig::Pool::createClient(int id, IClientListener *listener) con client = new DaemonClient(id, listener); } else if (m_mode == MODE_SELF_SELECT) { - client = new SelfSelectClient(id, Platform::userAgent(), listener); + client = new SelfSelectClient(id, Platform::userAgent(), listener, m_submit_to_origin); } # endif # ifdef XMRIG_ALGO_KAWPOW @@ -301,6 +303,7 @@ rapidjson::Value xmrig::Pool::toJSON(rapidjson::Document &doc) const } else { obj.AddMember(StringRef(kSelfSelect), m_daemon.url().toJSON(), allocator); + obj.AddMember(StringRef(kSubmitToOrigin), m_submit_to_origin, allocator); } return obj; @@ -319,7 +322,7 @@ std::string xmrig::Pool::printableName() const } if (m_mode == MODE_SELF_SELECT) { - out += std::string(" self-select ") + CSI "1;" + std::to_string(m_daemon.isTLS() ? 32 : 36) + "m" + m_daemon.url().data() + CLEAR; + out += std::string(" self-select ") + CSI "1;" + std::to_string(m_daemon.isTLS() ? 32 : 36) + "m" + m_daemon.url().data() + WHITE_BOLD_S + (m_submit_to_origin ? " submit-to-origin" : "") + CLEAR; } return out; diff --git a/src/base/net/stratum/Pool.h b/src/base/net/stratum/Pool.h index dc0fec87..97ef3fac 100644 --- a/src/base/net/stratum/Pool.h +++ b/src/base/net/stratum/Pool.h @@ -72,6 +72,7 @@ public: static const char *kPass; static const char *kRigId; static const char *kSelfSelect; + static const char* kSubmitToOrigin; static const char *kSOCKS5; static const char *kTls; static const char *kUrl; @@ -156,6 +157,7 @@ private: uint64_t m_pollInterval = kDefaultPollInterval; Url m_daemon; Url m_url; + bool m_submit_to_origin = false; # ifdef XMRIG_FEATURE_BENCHMARK std::shared_ptr m_benchmark; diff --git a/src/base/net/stratum/SelfSelectClient.cpp b/src/base/net/stratum/SelfSelectClient.cpp index fc4cea6e..b2bb291e 100644 --- a/src/base/net/stratum/SelfSelectClient.cpp +++ b/src/base/net/stratum/SelfSelectClient.cpp @@ -31,9 +31,12 @@ #include "base/io/json/Json.h" #include "base/io/json/JsonRequest.h" #include "base/io/log/Log.h" +#include "base/io/log/Tags.h" #include "base/net/http/Fetch.h" #include "base/net/http/HttpData.h" #include "base/net/stratum/Client.h" +#include "net/JobResult.h" +#include "base/tools/Cvt.h" namespace xmrig { @@ -54,8 +57,8 @@ static const char * const required_fields[] = { kBlocktemplateBlob, kBlockhashin } /* namespace xmrig */ -xmrig::SelfSelectClient::SelfSelectClient(int id, const char *agent, IClientListener *listener) : - m_listener(listener) +xmrig::SelfSelectClient::SelfSelectClient(int id, const char *agent, IClientListener *listener, bool submit_to_origin) : + m_listener(listener), m_submit_to_origin(submit_to_origin) { m_httpListener = std::make_shared(this); m_client = new Client(id, agent, this); @@ -201,6 +204,9 @@ void xmrig::SelfSelectClient::submitBlockTemplate(rapidjson::Value &result) Document doc(kObjectType); auto &allocator = doc.GetAllocator(); + m_blocktemplate = Json::getString(result,kBlocktemplateBlob); + m_blockdiff = Json::getUint64(result, kDifficulty); + Value params(kObjectType); params.AddMember(StringRef(kId), m_job.clientId().toJSON(), allocator); params.AddMember(StringRef(kJobId), m_job.id().toJSON(), allocator); @@ -235,6 +241,50 @@ void xmrig::SelfSelectClient::submitBlockTemplate(rapidjson::Value &result) }); } +int64_t xmrig::SelfSelectClient::submit(const JobResult& result) +{ + if (m_submit_to_origin) { + submitOriginDaemon(result); + } + return m_client->submit(result); +} + +void xmrig::SelfSelectClient::submitOriginDaemon(const JobResult& result) +{ + if (result.diff == 0 || m_blockdiff == 0) { + return; + } + + if (result.actualDiff() < m_blockdiff) { + m_origin_not_submitted++; + LOG_DEBUG("%s " RED_BOLD("not submitted to origin daemon, difficulty too low") " (%" PRId64 "/%" PRId64 ") " + BLACK_BOLD(" diff ") BLACK_BOLD("%" PRIu64) BLACK_BOLD(" vs. ") BLACK_BOLD("%" PRIu64), + Tags::origin(), m_origin_submitted, m_origin_not_submitted, m_blockdiff, result.actualDiff()); + return; + } + char *data = m_blocktemplate.data(); + Cvt::toHex(data + 78, 8, reinterpret_cast(&result.nonce), 4); + + using namespace rapidjson; + Document doc(kObjectType); + + Value params(kArrayType); + params.PushBack(m_blocktemplate.toJSON(), doc.GetAllocator()); + + JsonRequest::create(doc, m_sequence, "submitblock", params); + m_results[m_sequence] = SubmitResult(m_sequence, result.diff, result.actualDiff(), 0, result.backend); + + FetchRequest req(HTTP_POST, pool().daemon().host(), pool().daemon().port(), "/json_rpc", doc, pool().daemon().isTLS(), isQuiet()); + fetch(tag(), std::move(req), m_httpListener); + + m_origin_submitted++; + LOG_INFO("%s " GREEN_BOLD("submitted to origin daemon") " (%" PRId64 "/%" PRId64 ") " + " diff " WHITE("%" PRIu64) " vs. " WHITE("%" PRIu64), + Tags::origin(), m_origin_submitted, m_origin_not_submitted, m_blockdiff, result.actualDiff(), result.diff); + + // Ensure that the latest block template is available after block submission + getBlockTemplate(); +} void xmrig::SelfSelectClient::onHttpData(const HttpData &data) { diff --git a/src/base/net/stratum/SelfSelectClient.h b/src/base/net/stratum/SelfSelectClient.h index b02e5212..2cf0e6d0 100644 --- a/src/base/net/stratum/SelfSelectClient.h +++ b/src/base/net/stratum/SelfSelectClient.h @@ -35,6 +35,7 @@ #include +#include namespace xmrig { @@ -45,7 +46,7 @@ class SelfSelectClient : public IClient, public IClientListener, public IHttpLis public: XMRIG_DISABLE_COPY_MOVE_DEFAULT(SelfSelectClient) - SelfSelectClient(int id, const char *agent, IClientListener *listener); + SelfSelectClient(int id, const char *agent, IClientListener *listener, bool submit_to_origin); ~SelfSelectClient() override; protected: @@ -65,7 +66,7 @@ protected: inline int64_t send(const rapidjson::Value &obj, Callback callback) override { return m_client->send(obj, callback); } inline int64_t send(const rapidjson::Value &obj) override { return m_client->send(obj); } inline int64_t sequence() const override { return m_client->sequence(); } - inline int64_t submit(const JobResult &result) override { return m_client->submit(result); } + inline int64_t submit(const JobResult &result) override; inline void connect() override { m_client->connect(); } inline void connect(const Pool &pool) override { m_client->connect(pool); } inline void deleteLater() override { m_client->deleteLater(); } @@ -105,7 +106,7 @@ private: void retry(); void setState(State state); void submitBlockTemplate(rapidjson::Value &result); - + inline void submitOriginDaemon(const JobResult &result); bool m_active = false; bool m_quiet = false; IClient *m_client; @@ -115,9 +116,15 @@ private: int64_t m_sequence = 1; Job m_job; State m_state = IdleState; + String m_blocktemplate; + bool m_submit_to_origin = false; + std::map m_results; std::shared_ptr m_httpListener; uint64_t m_retryPause = 5000; uint64_t m_timestamp = 0; + uint64_t m_blockdiff = 0; + uint64_t m_origin_submitted = 0; + uint64_t m_origin_not_submitted = 0; }; diff --git a/src/config.json b/src/config.json index 7722d19b..e25c5767 100644 --- a/src/config.json +++ b/src/config.json @@ -75,7 +75,8 @@ "tls-fingerprint": null, "daemon": false, "socks5": null, - "self-select": null + "self-select": null, + "submit-to-origin": false } ], "print-time": 60, diff --git a/src/core/config/Config_default.h b/src/core/config/Config_default.h index c16d421e..6c46fa95 100644 --- a/src/core/config/Config_default.h +++ b/src/core/config/Config_default.h @@ -105,7 +105,8 @@ R"===( "tls-fingerprint": null, "daemon": false, "socks5": null, - "self-select": null + "self-select": null, + "submit-to-origin": false } ], "print-time": 60, diff --git a/src/core/config/Config_platform.h b/src/core/config/Config_platform.h index d1b17345..f97d6fd8 100644 --- a/src/core/config/Config_platform.h +++ b/src/core/config/Config_platform.h @@ -57,6 +57,7 @@ static const option options[] = { { "daemon", 0, nullptr, IConfig::DaemonKey }, { "daemon-poll-interval", 1, nullptr, IConfig::DaemonPollKey }, { "self-select", 1, nullptr, IConfig::SelfSelectKey }, + { "submit-to-origin", 0, nullptr, IConfig::SubmitToOriginKey }, # endif { "av", 1, nullptr, IConfig::AVKey }, { "background", 0, nullptr, IConfig::BackgroundKey }, diff --git a/src/core/config/usage.h b/src/core/config/usage.h index bf908848..8078e1a4 100644 --- a/src/core/config/usage.h +++ b/src/core/config/usage.h @@ -64,6 +64,7 @@ static inline const std::string &usage() u += " --daemon use daemon RPC instead of pool for solo mining\n"; u += " --daemon-poll-interval=N daemon poll interval in milliseconds (default: 1000)\n"; u += " --self-select=URL self-select block templates from URL\n"; + u += " --submit-to-origin also submit solution back to self-select URL\n"; # endif u += " -r, --retries=N number of times to retry before switch to backup server (default: 5)\n";