From 3752551e5351479aa942cb520bf91cc482d923a3 Mon Sep 17 00:00:00 2001 From: XMRig Date: Sat, 12 Oct 2019 19:48:18 +0700 Subject: [PATCH] Self-select initial working implementation. --- src/base/kernel/interfaces/IClient.h | 5 +- src/base/net/stratum/BaseClient.cpp | 8 +- src/base/net/stratum/BaseClient.h | 13 +- src/base/net/stratum/Client.cpp | 71 +++++--- src/base/net/stratum/Client.h | 3 +- src/base/net/stratum/DaemonClient.h | 1 + src/base/net/stratum/Pool.cpp | 1 + src/base/net/stratum/SelfSelectClient.cpp | 194 ++++++++++++++++++++++ src/base/net/stratum/SelfSelectClient.h | 80 +++++---- 9 files changed, 302 insertions(+), 74 deletions(-) diff --git a/src/base/kernel/interfaces/IClient.h b/src/base/kernel/interfaces/IClient.h index 78e36348..06e12968 100644 --- a/src/base/kernel/interfaces/IClient.h +++ b/src/base/kernel/interfaces/IClient.h @@ -26,7 +26,7 @@ #define XMRIG_ICLIENT_H -#include +#include "rapidjson/fwd.h" namespace xmrig { @@ -64,6 +64,8 @@ public: virtual const Pool &pool() const = 0; virtual const String &ip() const = 0; virtual int id() const = 0; + virtual int64_t send(const rapidjson::Value &obj) = 0; + virtual int64_t sequence() const = 0; virtual int64_t submit(const JobResult &result) = 0; virtual void connect() = 0; virtual void connect(const Pool &pool) = 0; @@ -75,7 +77,6 @@ public: virtual void setRetries(int retries) = 0; virtual void setRetryPause(uint64_t ms) = 0; virtual void tick(uint64_t now) = 0; - }; diff --git a/src/base/net/stratum/BaseClient.cpp b/src/base/net/stratum/BaseClient.cpp index 325fce1c..ccffa7ce 100644 --- a/src/base/net/stratum/BaseClient.cpp +++ b/src/base/net/stratum/BaseClient.cpp @@ -36,14 +36,8 @@ int64_t BaseClient::m_sequence = 1; xmrig::BaseClient::BaseClient(int id, IClientListener *listener) : - m_quiet(false), m_listener(listener), - m_id(id), - m_retries(5), - m_failures(0), - m_state(UnconnectedState), - m_retryPause(5000), - m_enabled(true) + m_id(id) { } diff --git a/src/base/net/stratum/BaseClient.h b/src/base/net/stratum/BaseClient.h index 51da1113..0d73dc9b 100644 --- a/src/base/net/stratum/BaseClient.h +++ b/src/base/net/stratum/BaseClient.h @@ -52,6 +52,7 @@ protected: inline const Pool &pool() const override { return m_pool; } inline const String &ip() const override { return m_ip; } inline int id() const override { return m_id; } + inline int64_t sequence() const override { return m_sequence; } inline void setAlgo(const Algorithm &algo) override { m_pool.setAlgo(algo); } inline void setEnabled(bool enabled) override { m_enabled = enabled; } inline void setPool(const Pool &pool) override { if (pool.isValid()) { m_pool = pool; } } @@ -73,22 +74,22 @@ protected: bool handleSubmitResponse(int64_t id, const char *error = nullptr); - bool m_quiet; + bool m_quiet = false; IClientListener *m_listener; int m_id; - int m_retries; - int64_t m_failures; + int m_retries = 5; + int64_t m_failures = 0; Job m_job; Pool m_pool; - SocketState m_state; + SocketState m_state = UnconnectedState; std::map m_results; String m_ip; - uint64_t m_retryPause; + uint64_t m_retryPause = 5000; static int64_t m_sequence; private: - bool m_enabled; + bool m_enabled = true; }; diff --git a/src/base/net/stratum/Client.cpp b/src/base/net/stratum/Client.cpp index 70362bb8..54349533 100644 --- a/src/base/net/stratum/Client.cpp +++ b/src/base/net/stratum/Client.cpp @@ -6,6 +6,7 @@ * Copyright 2016 Jay D Dee * Copyright 2017-2018 XMR-Stak , * Copyright 2018-2019 SChernykh + * Copyright 2019 jtgrassie * Copyright 2016-2019 XMRig , * * This program is free software: you can redistribute it and/or modify @@ -136,6 +137,31 @@ const char *xmrig::Client::tlsVersion() const } +int64_t xmrig::Client::send(const rapidjson::Value &obj) +{ + using namespace rapidjson; + + Value value; + + StringBuffer buffer(nullptr, 512); + Writer writer(buffer); + obj.Accept(writer); + + const size_t size = buffer.GetSize(); + if (size > (sizeof(m_sendBuf) - 2)) { + LOG_ERR("[%s] send failed: \"send buffer overflow: %zu > %zu\"", url(), size, (sizeof(m_sendBuf) - 2)); + close(); + return -1; + } + + memcpy(m_sendBuf, buffer.GetString(), size); + m_sendBuf[size] = '\n'; + m_sendBuf[size + 1] = '\0'; + + return send(size + 1); +} + + int64_t xmrig::Client::submit(const JobResult &result) { # ifndef XMRIG_PROXY_PROJECT @@ -320,9 +346,23 @@ bool xmrig::Client::parseJob(const rapidjson::Value ¶ms, int *code) return false; } - if (!job.setBlob(params["blob"].GetString())) { - *code = 4; - return false; +# ifdef XMRIG_FEATURE_HTTP + if (m_pool.mode() == Pool::MODE_SELF_SELECT) { + job.setExtraNonce(Json::getString(params, "extra_nonce")); + job.setPoolWallet(Json::getString(params, "pool_wallet")); + + if (job.extraNonce().isNull() || job.poolWallet().isNull()) { + *code = 4; + return false; + } + } + else +# endif + { + if (!job.setBlob(params["blob"].GetString())) { + *code = 4; + return false; + } } if (!job.setTarget(params["target"].GetString())) { @@ -345,7 +385,7 @@ bool xmrig::Client::parseJob(const rapidjson::Value ¶ms, int *code) return false; } - if (job.algorithm().family() == Algorithm::RANDOM_X && !job.setSeedHash(Json::getString(params, "seed_hash"))) { + if (m_pool.mode() != Pool::MODE_SELF_SELECT && job.algorithm().family() == Algorithm::RANDOM_X && !job.setSeedHash(Json::getString(params, "seed_hash"))) { if (!isQuiet()) { LOG_ERR("[%s] failed to parse field \"seed_hash\" required by RandomX", url(), algo); } @@ -473,29 +513,6 @@ int xmrig::Client::resolve(const String &host) } -int64_t xmrig::Client::send(const rapidjson::Document &doc) -{ - using namespace rapidjson; - - StringBuffer buffer(nullptr, 512); - Writer writer(buffer); - doc.Accept(writer); - - const size_t size = buffer.GetSize(); - if (size > (sizeof(m_sendBuf) - 2)) { - LOG_ERR("[%s] send failed: \"send buffer overflow: %zu > %zu\"", url(), size, (sizeof(m_sendBuf) - 2)); - close(); - return -1; - } - - memcpy(m_sendBuf, buffer.GetString(), size); - m_sendBuf[size] = '\n'; - m_sendBuf[size + 1] = '\0'; - - return send(size + 1); -} - - int64_t xmrig::Client::send(size_t size) { LOG_DEBUG("[%s] send (%d bytes): \"%.*s\"", url(), size, static_cast(size) - 1, m_sendBuf); diff --git a/src/base/net/stratum/Client.h b/src/base/net/stratum/Client.h index 32121aa1..8ff58c63 100644 --- a/src/base/net/stratum/Client.h +++ b/src/base/net/stratum/Client.h @@ -6,6 +6,7 @@ * Copyright 2016 Jay D Dee * Copyright 2017-2018 XMR-Stak , * Copyright 2018-2019 SChernykh + * Copyright 2019 jtgrassie * Copyright 2016-2019 XMRig , * * This program is free software: you can redistribute it and/or modify @@ -76,6 +77,7 @@ protected: bool isTLS() const override; const char *tlsFingerprint() const override; const char *tlsVersion() const override; + int64_t send(const rapidjson::Value &obj) override; int64_t submit(const JobResult &result) override; void connect() override; void connect(const Pool &pool) override; @@ -98,7 +100,6 @@ private: bool send(BIO *bio); bool verifyAlgorithm(const Algorithm &algorithm, const char *algo) const; int resolve(const String &host); - int64_t send(const rapidjson::Document &doc); int64_t send(size_t size); void connect(sockaddr *addr); void handshake(); diff --git a/src/base/net/stratum/DaemonClient.h b/src/base/net/stratum/DaemonClient.h index 781ea1b6..0932b2be 100644 --- a/src/base/net/stratum/DaemonClient.h +++ b/src/base/net/stratum/DaemonClient.h @@ -58,6 +58,7 @@ protected: inline const char *mode() const override { return "daemon"; } inline const char *tlsFingerprint() const override { return m_tlsFingerprint; } inline const char *tlsVersion() const override { return m_tlsVersion; } + inline int64_t send(const rapidjson::Value &) override { return -1; } inline void deleteLater() override { delete this; } inline void tick(uint64_t) override {} diff --git a/src/base/net/stratum/Pool.cpp b/src/base/net/stratum/Pool.cpp index 1570d5fb..71152fd0 100644 --- a/src/base/net/stratum/Pool.cpp +++ b/src/base/net/stratum/Pool.cpp @@ -168,6 +168,7 @@ bool xmrig::Pool::isEqual(const Pool &other) const && m_url == other.m_url && m_user == other.m_user && m_pollInterval == other.m_pollInterval + && m_daemon == other.m_daemon ); } diff --git a/src/base/net/stratum/SelfSelectClient.cpp b/src/base/net/stratum/SelfSelectClient.cpp index 95a9ae95..7398d6ee 100644 --- a/src/base/net/stratum/SelfSelectClient.cpp +++ b/src/base/net/stratum/SelfSelectClient.cpp @@ -6,6 +6,7 @@ * Copyright 2016 Jay D Dee * Copyright 2017-2018 XMR-Stak , * Copyright 2018-2019 SChernykh + * Copyright 2019 jtgrassie * Copyright 2016-2019 XMRig , * * This program is free software: you can redistribute it and/or modify @@ -24,7 +25,39 @@ #include "base/net/stratum/SelfSelectClient.h" +#include "3rdparty/http-parser/http_parser.h" +#include "base/io/json/Json.h" +#include "base/io/json/JsonRequest.h" +#include "base/io/log/Log.h" +#include "base/net/http/HttpClient.h" #include "base/net/stratum/Client.h" +#include "rapidjson/document.h" +#include "rapidjson/error/en.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" + + +#ifdef XMRIG_FEATURE_TLS +# include "base/net/http/HttpsClient.h" +#endif + + +namespace xmrig { + +static const char *kBlob = "blob"; +static const char *kBlockhashingBlob = "blockhashing_blob"; +static const char *kBlocktemplateBlob = "blocktemplate_blob"; +static const char *kDifficulty = "difficulty"; +static const char *kHeight = "height"; +static const char *kId = "id"; +static const char *kJobId = "job_id"; +static const char *kNextSeedHash = "next_seed_hash"; +static const char *kPrevHash = "prev_hash"; +static const char *kSeedHash = "seed_hash"; + +static const char * const required_fields[] = { kBlocktemplateBlob, kBlockhashingBlob, kHeight, kDifficulty, kPrevHash }; + +} /* namespace xmrig */ xmrig::SelfSelectClient::SelfSelectClient(int id, const char *agent, IClientListener *listener) : @@ -38,3 +71,164 @@ xmrig::SelfSelectClient::~SelfSelectClient() { delete m_client; } + + +void xmrig::SelfSelectClient::onJobReceived(IClient *, const Job &job, const rapidjson::Value &) +{ + m_job = job; + + getBlockTemplate(); +} + + +void xmrig::SelfSelectClient::onLogin(IClient *, rapidjson::Document &doc, rapidjson::Value ¶ms) +{ + params.AddMember("mode", "self-select", doc.GetAllocator()); + + m_listener->onLogin(this, doc, params); +} + + +bool xmrig::SelfSelectClient::parseResponse(int64_t id, rapidjson::Value &result, const rapidjson::Value &error) +{ + if (id == -1) { + return false; + } + + if (error.IsObject()) { + LOG_ERR("[%s:%d] error: " RED_BOLD("\"%s\"") RED_S ", code: %d", pool().daemon().host().data(), pool().daemon().port(), Json::getString(error, "message"), Json::getInt(error, "code")); + + return false; + } + + if (!result.IsObject()) { + return false; + } + + for (auto field : required_fields) { + if (!result.HasMember(field)) { + LOG_ERR("[%s:%d] required field " RED_BOLD("\"%s\"") RED_S " not found", pool().daemon().host().data(), pool().daemon().port(), field); + + return false; + } + } + + if (!m_job.setBlob(result[kBlockhashingBlob].GetString())) { + return false; + } + + m_job.setHeight(Json::getUint64(result, kHeight)); + m_job.setSeedHash(Json::getString(result, kSeedHash)); + + submitBlockTemplate(result); + + m_listener->onJobReceived(this, m_job, rapidjson::Value{}); + + return true; +} + + +void xmrig::SelfSelectClient::getBlockTemplate() +{ + using namespace rapidjson; + Document doc(kObjectType); + auto &allocator = doc.GetAllocator(); + + Value params(kObjectType); + params.AddMember("wallet_address", m_job.poolWallet().toJSON(), allocator); + params.AddMember("extra_nonce", m_job.extraNonce().toJSON(), allocator); + + JsonRequest::create(doc, sequence(), "getblocktemplate", params); + + send(HTTP_POST, "/json_rpc", doc); +} + + +void xmrig::SelfSelectClient::retry() +{ + // FIXME +} + + +void xmrig::SelfSelectClient::send(int method, const char *url, const char *data, size_t size) +{ + LOG_DEBUG("[%s:%d] " MAGENTA_BOLD("\"%s %s\"") BLACK_BOLD_S " send (%zu bytes): \"%.*s\"", + pool().daemon().host().data(), + pool().daemon().port(), + http_method_str(static_cast(method)), + url, + size, + static_cast(size), + data); + + HttpClient *client; +# ifdef XMRIG_FEATURE_TLS + if (pool().daemon().isTLS()) { + client = new HttpsClient(method, url, this, data, size, String()); + } + else +# endif + { + client = new HttpClient(method, url, this, data, size); + } + + client->setQuiet(m_quiet); + client->connect(pool().daemon().host(), pool().daemon().port()); +} + + +void xmrig::SelfSelectClient::send(int method, const char *url, const rapidjson::Document &doc) +{ + using namespace rapidjson; + + StringBuffer buffer(nullptr, 512); + Writer writer(buffer); + doc.Accept(writer); + + send(method, url, buffer.GetString(), buffer.GetSize()); +} + + +void xmrig::SelfSelectClient::submitBlockTemplate(rapidjson::Value &result) +{ + using namespace rapidjson; + Document doc(kObjectType); + auto &allocator = doc.GetAllocator(); + + Value params(kObjectType); + params.AddMember(StringRef(kId), m_job.clientId().toJSON(), allocator); + params.AddMember(StringRef(kJobId), m_job.id().toJSON(), allocator); + params.AddMember(StringRef(kBlob), result[kBlocktemplateBlob], allocator); + params.AddMember(StringRef(kHeight), m_job.height(), allocator); + params.AddMember(StringRef(kDifficulty), result[kDifficulty], allocator); + params.AddMember(StringRef(kPrevHash), result[kPrevHash], allocator); + params.AddMember(StringRef(kSeedHash), result[kSeedHash], allocator); + params.AddMember(StringRef(kNextSeedHash), result[kNextSeedHash], allocator); + + JsonRequest::create(doc, sequence(), "block_template", params); + + send(doc); +} + + +void xmrig::SelfSelectClient::onHttpData(const HttpData &data) +{ + if (data.status != HTTP_STATUS_OK) { + return retry(); + } + + LOG_DEBUG("[%s:%d] received (%d bytes): \"%.*s\"", pool().daemon().host().data(), pool().daemon().port(), static_cast(data.body.size()), static_cast(data.body.size()), data.body.c_str()); + + rapidjson::Document doc; + if (doc.Parse(data.body.c_str()).HasParseError()) { + if (!m_quiet) { + LOG_ERR("[%s:%d] JSON decode failed: \"%s\"", pool().daemon().host().data(), pool().daemon().port(), rapidjson::GetParseError_En(doc.GetParseError())); + } + + return retry(); + } + + if (!parseResponse(Json::getInt64(doc, "id", -1), doc["result"], Json::getObject(doc, "error"))) { + retry(); + } +} diff --git a/src/base/net/stratum/SelfSelectClient.h b/src/base/net/stratum/SelfSelectClient.h index b4549ebe..aa39d430 100644 --- a/src/base/net/stratum/SelfSelectClient.h +++ b/src/base/net/stratum/SelfSelectClient.h @@ -6,6 +6,7 @@ * Copyright 2016 Jay D Dee * Copyright 2017-2018 XMR-Stak , * Copyright 2018-2019 SChernykh + * Copyright 2019 jtgrassie * Copyright 2016-2019 XMRig , * * This program is free software: you can redistribute it and/or modify @@ -26,15 +27,17 @@ #define XMRIG_SELFSELECTCLIENT_H -#include "base/kernel/interfaces/IClientListener.h" -#include "base/tools/Object.h" #include "base/kernel/interfaces/IClient.h" +#include "base/kernel/interfaces/IClientListener.h" +#include "base/kernel/interfaces/IHttpListener.h" +#include "base/net/stratum/Job.h" +#include "base/tools/Object.h" namespace xmrig { -class SelfSelectClient : public IClient, public IClientListener +class SelfSelectClient : public IClient, public IClientListener, public IHttpListener { public: XMRIG_DISABLE_COPY_MOVE_DEFAULT(SelfSelectClient) @@ -44,40 +47,55 @@ public: protected: // IClient - bool disconnect() override { return m_client->disconnect(); } - bool hasExtension(Extension extension) const noexcept override { return m_client->hasExtension(extension); } - bool isEnabled() const override { return m_client->isEnabled(); } - bool isTLS() const override { return m_client->isTLS(); } - const char *mode() const override { return m_client->mode(); } - const char *tlsFingerprint() const override { return m_client->tlsFingerprint(); } - const char *tlsVersion() const override { return m_client->tlsVersion(); } - const Job &job() const override { return m_client->job(); } - const Pool &pool() const override { return m_client->pool(); } - const String &ip() const override { return m_client->ip(); } - int id() const override { return m_client->id(); } - int64_t submit(const JobResult &result) override { return m_client->submit(result); } - void connect() override { m_client->connect(); } - void connect(const Pool &pool) override { m_client->connect(pool); } - void deleteLater() override { m_client->deleteLater(); } - void setAlgo(const Algorithm &algo) override { m_client->setAlgo(algo); } - void setEnabled(bool enabled) override { m_client->setEnabled(enabled); } - void setPool(const Pool &pool) override { m_client->setPool(pool); } - void setQuiet(bool quiet) override { m_client->setQuiet(quiet); } - void setRetries(int retries) override { m_client->setRetries(retries); } - void setRetryPause(uint64_t ms) override { m_client->setRetryPause(ms); } - void tick(uint64_t now) override { m_client->tick(now); } + inline bool disconnect() override { return m_client->disconnect(); } + inline bool hasExtension(Extension extension) const noexcept override { return m_client->hasExtension(extension); } + inline bool isEnabled() const override { return m_client->isEnabled(); } + inline bool isTLS() const override { return m_client->isTLS(); } + inline const char *mode() const override { return m_client->mode(); } + inline const char *tlsFingerprint() const override { return m_client->tlsFingerprint(); } + inline const char *tlsVersion() const override { return m_client->tlsVersion(); } + inline const Job &job() const override { return m_client->job(); } + inline const Pool &pool() const override { return m_client->pool(); } + inline const String &ip() const override { return m_client->ip(); } + inline int id() const override { return m_client->id(); } + 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 void connect() override { m_client->connect(); } + inline void connect(const Pool &pool) override { m_client->connect(pool); } + inline void deleteLater() override { m_client->deleteLater(); } + inline void setAlgo(const Algorithm &algo) override { m_client->setAlgo(algo); } + inline void setEnabled(bool enabled) override { m_client->setEnabled(enabled); } + inline void setPool(const Pool &pool) override { m_client->setPool(pool); } + inline void setQuiet(bool quiet) override { m_client->setQuiet(quiet); m_quiet = quiet; } + inline void setRetries(int retries) override { m_client->setRetries(retries); } + inline void setRetryPause(uint64_t ms) override { m_client->setRetryPause(ms); } + inline void tick(uint64_t now) override { m_client->tick(now); } // IClientListener - void onClose(IClient *, int failures) override { m_listener->onClose(this, failures); } - void onJobReceived(IClient *, const Job &job, const rapidjson::Value ¶ms) override { m_listener->onJobReceived(this, job, params); } - void onLogin(IClient *, rapidjson::Document &doc, rapidjson::Value ¶ms) override { m_listener->onLogin(this, doc, params); } - void onLoginSuccess(IClient *) override { m_listener->onLoginSuccess(this); } - void onResultAccepted(IClient *, const SubmitResult &result, const char *error) override { m_listener->onResultAccepted(this, result, error); } - void onVerifyAlgorithm(const IClient *, const Algorithm &algorithm, bool *ok) override { m_listener->onVerifyAlgorithm(this, algorithm, ok); } + inline void onClose(IClient *, int failures) override { m_listener->onClose(this, failures); } + inline void onLoginSuccess(IClient *) override { m_listener->onLoginSuccess(this); } + inline void onResultAccepted(IClient *, const SubmitResult &result, const char *error) override { m_listener->onResultAccepted(this, result, error); } + inline void onVerifyAlgorithm(const IClient *, const Algorithm &algorithm, bool *ok) override { m_listener->onVerifyAlgorithm(this, algorithm, ok); } + + void onJobReceived(IClient *, const Job &job, const rapidjson::Value ¶ms) override; + void onLogin(IClient *, rapidjson::Document &doc, rapidjson::Value ¶ms) override; + + // IHttpListener + void onHttpData(const HttpData &data) override; private: + bool parseResponse(int64_t id, rapidjson::Value &result, const rapidjson::Value &error); + void getBlockTemplate(); + void retry(); + void send(int method, const char *url, const char *data = nullptr, size_t size = 0); + void send(int method, const char *url, const rapidjson::Document &doc); + void submitBlockTemplate(rapidjson::Value &result); + + bool m_quiet = false; IClient *m_client; IClientListener *m_listener; + Job m_job; };