From 62012a1a50a6bcddd905e60c88f330ec41e5c6e3 Mon Sep 17 00:00:00 2001 From: XMRig Date: Fri, 12 Apr 2019 03:25:21 +0700 Subject: [PATCH] Added DaemonClient. --- src/base/base.cmake | 2 + src/base/kernel/interfaces/IStrategy.h | 4 +- src/base/net/http/HttpClient.cpp | 28 +- src/base/net/http/HttpClient.h | 4 +- src/base/net/http/HttpContext.cpp | 10 +- src/base/net/http/HttpContext.h | 2 +- src/base/net/http/HttpsClient.cpp | 4 +- src/base/net/stratum/BaseClient.cpp | 27 +- src/base/net/stratum/BaseClient.h | 21 ++ src/base/net/stratum/Client.cpp | 122 +++--- src/base/net/stratum/Client.h | 25 +- src/base/net/stratum/DaemonClient.cpp | 350 ++++++++++++++++++ src/base/net/stratum/DaemonClient.h | 78 ++++ src/base/net/stratum/Pool.h | 1 + .../stratum/strategies/FailoverStrategy.cpp | 21 +- .../net/stratum/strategies/FailoverStrategy.h | 6 +- .../stratum/strategies/SinglePoolStrategy.cpp | 15 + .../stratum/strategies/SinglePoolStrategy.h | 4 +- src/net/strategies/DonateStrategy.cpp | 4 +- src/net/strategies/DonateStrategy.h | 4 +- 20 files changed, 610 insertions(+), 122 deletions(-) create mode 100644 src/base/net/stratum/DaemonClient.cpp create mode 100644 src/base/net/stratum/DaemonClient.h diff --git a/src/base/base.cmake b/src/base/base.cmake index 3c7ee1b3..6d478b39 100644 --- a/src/base/base.cmake +++ b/src/base/base.cmake @@ -108,6 +108,7 @@ if (WITH_HTTP) src/base/net/http/HttpData.h src/base/net/http/HttpResponse.h src/base/net/http/HttpServer.h + src/base/net/stratum/DaemonClient.h src/base/net/tools/TcpServer.h ) @@ -118,6 +119,7 @@ if (WITH_HTTP) src/base/net/http/HttpContext.cpp src/base/net/http/HttpResponse.cpp src/base/net/http/HttpServer.cpp + src/base/net/stratum/DaemonClient.cpp src/base/net/tools/TcpServer.cpp ) diff --git a/src/base/kernel/interfaces/IStrategy.h b/src/base/kernel/interfaces/IStrategy.h index 31798b9c..f2e58408 100644 --- a/src/base/kernel/interfaces/IStrategy.h +++ b/src/base/kernel/interfaces/IStrategy.h @@ -33,7 +33,7 @@ namespace xmrig { class Algorithm; -class Client; +class IClient; class JobResult; @@ -43,7 +43,7 @@ public: virtual ~IStrategy() = default; virtual bool isActive() const = 0; - virtual Client *client() const = 0; + virtual IClient *client() const = 0; virtual int64_t submit(const JobResult &result) = 0; virtual void connect() = 0; virtual void resume() = 0; diff --git a/src/base/net/http/HttpClient.cpp b/src/base/net/http/HttpClient.cpp index a512989c..d58e7299 100644 --- a/src/base/net/http/HttpClient.cpp +++ b/src/base/net/http/HttpClient.cpp @@ -40,11 +40,11 @@ namespace xmrig { static const char *kCRLF = "\r\n"; -class WriteBaton : public Baton +class ClientWriteBaton : public Baton { public: - inline WriteBaton(const std::string &header, std::string &&body) : - m_body(body), + inline ClientWriteBaton(const std::string &header, std::string &&body) : + m_body(std::move(body)), m_header(header) { bufs[0].len = m_header.size(); @@ -63,7 +63,7 @@ public: inline size_t count() const { return bufs[1].base == nullptr ? 1 : 2; } inline size_t size() const { return bufs[0].len + bufs[1].len; } - inline static void onWrite(uv_write_t *req, int) { delete reinterpret_cast(req->data); } + inline static void onWrite(uv_write_t *req, int) { delete reinterpret_cast(req->data); } uv_buf_t bufs[2]; @@ -117,7 +117,9 @@ void xmrig::HttpClient::onResolved(const Dns &dns, int status) this->status = status; if (status < 0 && dns.isEmpty()) { - LOG_ERR("[%s:%d] DNS error: \"%s\"", dns.host().data(), m_port, uv_strerror(status)); + if (!m_quiet) { + LOG_ERR("[%s:%d] DNS error: \"%s\"", dns.host().data(), m_port, uv_strerror(status)); + } return; } @@ -159,15 +161,15 @@ void xmrig::HttpClient::handshake() void xmrig::HttpClient::read(const char *data, size_t size) { if (parse(data, size) < size) { - close(); + close(UV_EPROTO); } } void xmrig::HttpClient::write(const std::string &header) { - WriteBaton *baton = new WriteBaton(header, std::move(body)); - uv_write(&baton->req, stream(), baton->bufs, baton->count(), WriteBaton::onWrite); + ClientWriteBaton *baton = new ClientWriteBaton(header, std::move(body)); + uv_write(&baton->req, stream(), baton->bufs, baton->count(), ClientWriteBaton::onWrite); } @@ -180,10 +182,12 @@ void xmrig::HttpClient::onConnect(uv_connect_t *req, int status) } if (status < 0) { - LOG_ERR("[%s:%d] connect error: \"%s\"", client->m_dns->host().data(), client->m_port, uv_strerror(status)); + if (!client->m_quiet) { + LOG_ERR("[%s:%d] connect error: \"%s\"", client->m_dns->host().data(), client->m_port, uv_strerror(status)); + } delete req; - client->close(); + client->close(status); return; } @@ -205,11 +209,11 @@ void xmrig::HttpClient::onConnect(uv_connect_t *req, int status) if (nread >= 0) { client->read(buf->base, static_cast(nread)); } else { - if (nread != UV_EOF) { + if (!client->m_quiet && nread != UV_EOF) { LOG_ERR("[%s:%d] read error: \"%s\"", client->m_dns->host().data(), client->m_port, uv_strerror(static_cast(nread))); } - client->close(); + client->close(static_cast(nread)); } delete [] buf->base; diff --git a/src/base/net/http/HttpClient.h b/src/base/net/http/HttpClient.h index b92c4733..e9866483 100644 --- a/src/base/net/http/HttpClient.h +++ b/src/base/net/http/HttpClient.h @@ -44,7 +44,8 @@ public: HttpClient(int method, const String &url, IHttpListener *listener, const char *data = nullptr, size_t size = 0); ~HttpClient() override; - inline uint16_t port() const { return m_port; } + inline uint16_t port() const { return m_port; } + inline void setQuiet(bool quiet) { m_quiet = quiet; } bool connect(const String &host, uint16_t port); const String &host() const; @@ -59,6 +60,7 @@ protected: private: static void onConnect(uv_connect_t *req, int status); + bool m_quiet; Dns *m_dns; uint16_t m_port; }; diff --git a/src/base/net/http/HttpContext.cpp b/src/base/net/http/HttpContext.cpp index c98b4ad2..e97f989b 100644 --- a/src/base/net/http/HttpContext.cpp +++ b/src/base/net/http/HttpContext.cpp @@ -96,8 +96,13 @@ std::string xmrig::HttpContext::ip() const } -void xmrig::HttpContext::close() +void xmrig::HttpContext::close(int status) { + if (status < 0 && m_listener) { + this->status = status; + m_listener->onHttpData(*this); + } + auto it = storage.find(id()); if (it != storage.end()) { storage.erase(it); @@ -203,8 +208,9 @@ void xmrig::HttpContext::attach(http_parser_settings *settings) settings->on_message_complete = [](http_parser *parser) -> int { - const HttpContext *ctx = static_cast(parser->data); + HttpContext *ctx = static_cast(parser->data); ctx->m_listener->onHttpData(*ctx); + ctx->m_listener = nullptr; return 0; }; diff --git a/src/base/net/http/HttpContext.h b/src/base/net/http/HttpContext.h index ee11a072..fbb453aa 100644 --- a/src/base/net/http/HttpContext.h +++ b/src/base/net/http/HttpContext.h @@ -56,7 +56,7 @@ public: size_t parse(const char *data, size_t size); std::string ip() const; - void close(); + void close(int status = 0); static HttpContext *get(uint64_t id); static void closeAll(); diff --git a/src/base/net/http/HttpsClient.cpp b/src/base/net/http/HttpsClient.cpp index 8fd26a2e..ff103a34 100644 --- a/src/base/net/http/HttpsClient.cpp +++ b/src/base/net/http/HttpsClient.cpp @@ -96,7 +96,7 @@ void xmrig::HttpsClient::read(const char *data, size_t size) X509 *cert = SSL_get_peer_certificate(m_ssl); if (!verify(cert)) { X509_free(cert); - return close(); + return close(UV_EPROTO); } X509_free(cert); @@ -142,7 +142,7 @@ void xmrig::HttpsClient::flush() result = uv_try_write(stream(), &buf, 1) == buf.len; if (!result) { - close(); + close(UV_EIO); } } diff --git a/src/base/net/stratum/BaseClient.cpp b/src/base/net/stratum/BaseClient.cpp index 9107116b..f44415d5 100644 --- a/src/base/net/stratum/BaseClient.cpp +++ b/src/base/net/stratum/BaseClient.cpp @@ -23,7 +23,16 @@ */ +#include "base/kernel/interfaces/IClientListener.h" #include "base/net/stratum/BaseClient.h" +#include "base/net/stratum/SubmitResult.h" + + +namespace xmrig { + +int64_t BaseClient::m_sequence = 1; + +} /* namespace xmrig */ xmrig::BaseClient::BaseClient(int id, IClientListener *listener) : @@ -31,7 +40,23 @@ xmrig::BaseClient::BaseClient(int id, IClientListener *listener) : m_listener(listener), m_id(id), m_retries(5), + m_failures(0), + m_state(UnconnectedState), m_retryPause(5000) { - +} + + +bool xmrig::BaseClient::handleSubmitResponse(int64_t id, const char *error) +{ + auto it = m_results.find(id); + if (it != m_results.end()) { + it->second.done(); + m_listener->onResultAccepted(this, it->second, error); + m_results.erase(it); + + return true; + } + + return false; } diff --git a/src/base/net/stratum/BaseClient.h b/src/base/net/stratum/BaseClient.h index 27432a2f..9e1c7ffb 100644 --- a/src/base/net/stratum/BaseClient.h +++ b/src/base/net/stratum/BaseClient.h @@ -26,6 +26,9 @@ #define XMRIG_BASECLIENT_H +#include + + #include "base/kernel/interfaces/IClient.h" #include "base/net/stratum/Job.h" #include "base/net/stratum/Pool.h" @@ -35,6 +38,7 @@ namespace xmrig { class IClientListener; +class SubmitResult; class BaseClient : public IClient @@ -55,15 +59,32 @@ public: inline void setRetryPause(uint64_t ms) override { m_retryPause = ms; } protected: + enum SocketState { + UnconnectedState, + HostLookupState, + ConnectingState, + ConnectedState, + ClosingState + }; + + inline bool isQuiet() const { return m_quiet || m_failures >= m_retries; } + + bool handleSubmitResponse(int64_t id, const char *error = nullptr); + bool m_quiet; IClientListener *m_listener; int m_id; int m_retries; + int64_t m_failures; Job m_job; Pool m_pool; + SocketState m_state; + std::map m_results; String m_ip; uint64_t m_retryPause; + static int64_t m_sequence; + private: bool m_enabled; }; diff --git a/src/base/net/stratum/Client.cpp b/src/base/net/stratum/Client.cpp index 27075d48..df6f4837 100644 --- a/src/base/net/stratum/Client.cpp +++ b/src/base/net/stratum/Client.cpp @@ -57,7 +57,6 @@ namespace xmrig { -int64_t Client::m_sequence = 1; Storage Client::m_storage; } /* namespace xmrig */ @@ -77,8 +76,6 @@ static const char *states[] = { xmrig::Client::Client(int id, const char *agent, IClientListener *listener) : BaseClient(id, listener), m_agent(agent), - m_failures(0), - m_state(UnconnectedState), m_tls(nullptr), m_expire(0), m_jobs(0), @@ -99,57 +96,6 @@ xmrig::Client::~Client() } -void xmrig::Client::connect() -{ -# ifdef XMRIG_FEATURE_TLS - if (m_pool.isTLS()) { - m_tls = new Tls(this); - } -# endif - - resolve(m_pool.host()); -} - - -void xmrig::Client::connect(const Pool &pool) -{ - setPool(pool); - connect(); -} - - -void xmrig::Client::deleteLater() -{ - if (!m_listener) { - return; - } - - m_listener = nullptr; - - if (!disconnect()) { - m_storage.remove(m_key); - } -} - - -void xmrig::Client::tick(uint64_t now) -{ - if (m_state == ConnectedState) { - if (m_expire && now > m_expire) { - LOG_DEBUG_ERR("[%s] timeout", url()); - close(); - } - else if (m_keepAlive && now > m_keepAlive) { - ping(); - } - } - - if (m_expire && now > m_expire && m_state == ConnectingState) { - connect(); - } -} - - bool xmrig::Client::disconnect() { m_keepAlive = 0; @@ -247,6 +193,57 @@ int64_t xmrig::Client::submit(const JobResult &result) } +void xmrig::Client::connect() +{ +# ifdef XMRIG_FEATURE_TLS + if (m_pool.isTLS()) { + m_tls = new Tls(this); + } +# endif + + resolve(m_pool.host()); +} + + +void xmrig::Client::connect(const Pool &pool) +{ + setPool(pool); + connect(); +} + + +void xmrig::Client::deleteLater() +{ + if (!m_listener) { + return; + } + + m_listener = nullptr; + + if (!disconnect()) { + m_storage.remove(m_key); + } +} + + +void xmrig::Client::tick(uint64_t now) +{ + if (m_state == ConnectedState) { + if (m_expire && now > m_expire) { + LOG_DEBUG_ERR("[%s] timeout", url()); + close(); + } + else if (m_keepAlive && now > m_keepAlive) { + ping(); + } + } + + if (m_expire && now > m_expire && m_state == ConnectingState) { + connect(); + } +} + + void xmrig::Client::onResolved(const Dns &dns, int status) { assert(m_listener != nullptr); @@ -749,14 +746,8 @@ void xmrig::Client::parseResponse(int64_t id, const rapidjson::Value &result, co if (error.IsObject()) { const char *message = error["message"].GetString(); - auto it = m_results.find(id); - if (it != m_results.end()) { - it->second.done(); - m_listener->onResultAccepted(this, it->second, message); - m_results.erase(it); - } - else if (!isQuiet()) { - LOG_ERR("[%s] error: \"%s\", code: %d", url(), message, error["code"].GetInt()); + if (!handleSubmitResponse(id, message) && !isQuiet()) { + LOG_ERR("[%s] error: " RED_BOLD("\"%s\"") RED_S ", code: %d", url(), message, error["code"].GetInt()); } if (isCriticalError(message)) { @@ -787,12 +778,7 @@ void xmrig::Client::parseResponse(int64_t id, const rapidjson::Value &result, co return; } - auto it = m_results.find(id); - if (it != m_results.end()) { - it->second.done(); - m_listener->onResultAccepted(this, it->second, nullptr); - m_results.erase(it); - } + handleSubmitResponse(id); } diff --git a/src/base/net/stratum/Client.h b/src/base/net/stratum/Client.h index b42fa613..c7aeabfe 100644 --- a/src/base/net/stratum/Client.h +++ b/src/base/net/stratum/Client.h @@ -43,7 +43,6 @@ #include "common/crypto/Algorithm.h" - typedef struct bio_st BIO; @@ -68,6 +67,7 @@ public: Client(int id, const char *agent, IClientListener *listener); ~Client() override; +protected: bool disconnect() override; bool isTLS() const override; const char *tlsFingerprint() const override; @@ -78,23 +78,13 @@ public: void deleteLater() override; void tick(uint64_t now) override; - inline bool hasExtension(Extension extension) const noexcept override { return m_extensions.test(extension); } - inline const char *mode() const override { return "pool"; } - -protected: - inline void onLine(char *line, size_t size) override { parse(line, size); } - void onResolved(const Dns &dns, int status) override; -private: - enum SocketState { - UnconnectedState, - HostLookupState, - ConnectingState, - ConnectedState, - ClosingState - }; + inline bool hasExtension(Extension extension) const noexcept override { return m_extensions.test(extension); } + inline const char *mode() const override { return "pool"; } + inline void onLine(char *line, size_t size) override { parse(line, size); } +private: class Tls; bool close(); @@ -120,7 +110,6 @@ private: void setState(SocketState state); void startTimeout(); - inline bool isQuiet() const { return m_quiet || m_failures >= m_retries; } inline const char *url() const { return m_pool.url(); } inline SocketState state() const { return m_state; } inline void setExtension(Extension ext, bool enable) noexcept { m_extensions.set(ext, enable); } @@ -136,11 +125,8 @@ private: char m_sendBuf[2048]; const char *m_agent; Dns *m_dns; - int64_t m_failures; RecvBuf m_recvBuf; - SocketState m_state; std::bitset m_extensions; - std::map m_results; String m_rpcId; Tls *m_tls; uint64_t m_expire; @@ -150,7 +136,6 @@ private: uv_stream_t *m_stream; uv_tcp_t *m_socket; - static int64_t m_sequence; static Storage m_storage; }; diff --git a/src/base/net/stratum/DaemonClient.cpp b/src/base/net/stratum/DaemonClient.cpp new file mode 100644 index 00000000..a5aef54d --- /dev/null +++ b/src/base/net/stratum/DaemonClient.cpp @@ -0,0 +1,350 @@ +/* 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-2019 SChernykh + * Copyright 2019 Howard Chu + * Copyright 2016-2019 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 "3rdparty/http-parser/http_parser.h" +#include "base/io/Json.h" +#include "base/io/log/Log.h" +#include "base/kernel/interfaces/IClientListener.h" +#include "base/net/http/HttpClient.h" +#include "base/net/stratum/DaemonClient.h" +#include "base/net/stratum/SubmitResult.h" +#include "base/tools/Buffer.h" +#include "base/tools/Timer.h" +#include "net/JobResult.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 + + +xmrig::DaemonClient::DaemonClient(int id, IClientListener *listener) : + BaseClient(id, listener) +{ + m_timer = new Timer(this); +} + + +xmrig::DaemonClient::~DaemonClient() +{ + delete m_timer; +} + + +bool xmrig::DaemonClient::disconnect() +{ + setState(UnconnectedState); + + return true; +} + + +bool xmrig::DaemonClient::isTLS() const +{ + return false; +} + + +const char *xmrig::DaemonClient::tlsFingerprint() const +{ + return nullptr; +} + + +const char *xmrig::DaemonClient::tlsVersion() const +{ + return nullptr; +} + + +int64_t xmrig::DaemonClient::submit(const JobResult &result) +{ + if (result.jobId != (m_blocktemplate.data() + m_blocktemplate.size() - 48)) { + return -1; + } + + Buffer::toHex(reinterpret_cast(&result.nonce), 4, m_blocktemplate.data() + 78); + + using namespace rapidjson; + Document doc(kObjectType); + auto &allocator = doc.GetAllocator(); + + doc.AddMember("id", m_sequence, allocator); + doc.AddMember("jsonrpc", "2.0", allocator); + doc.AddMember("method", "submitblock", allocator); + + Value params(kArrayType); + params.PushBack(m_blocktemplate.toJSON(), allocator); + + doc.AddMember("params", params, allocator); + +# ifdef XMRIG_PROXY_PROJECT + m_results[m_sequence] = SubmitResult(m_sequence, result.diff, result.actualDiff(), result.id); +# else + m_results[m_sequence] = SubmitResult(m_sequence, result.diff, result.actualDiff()); +# endif + + send(HTTP_POST, "/json_rpc", doc); + + return m_sequence++; +} + + +void xmrig::DaemonClient::connect() +{ + setState(ConnectingState); + getBlockTemplate(); +} + + +void xmrig::DaemonClient::connect(const Pool &pool) +{ + setPool(pool); + connect(); +} + + +void xmrig::DaemonClient::onHttpData(const HttpData &data) +{ + if (data.status != HTTP_STATUS_OK) { + return retry(); + } + + LOG_DEBUG("[%s:%d] received (%d bytes): \"%.*s\"", m_pool.host().data(), m_pool.port(), static_cast(data.body.size()), static_cast(data.body.size()), data.body.c_str()); + + m_ip = static_cast(data).ip().c_str(); + + rapidjson::Document doc; + if (doc.Parse(data.body.c_str()).HasParseError()) { + if (!isQuiet()) { + LOG_ERR("[%s:%d] JSON decode failed: \"%s\"", m_pool.host().data(), m_pool.port(), rapidjson::GetParseError_En(doc.GetParseError())); + } + + return retry(); + } + + if (data.method == HTTP_GET && data.url == "/getheight") { + if (m_job.height() != Json::getUint64(doc, "height")) { + getBlockTemplate(); + } + + return; + } + + if (!parseResponse(Json::getInt64(doc, "id", -1), Json::getObject(doc, "result"), Json::getObject(doc, "error"))) { + retry(); + } +} + + +void xmrig::DaemonClient::onTimer(const Timer *) +{ + if (m_state == ConnectingState) { + getBlockTemplate(); + } + else if (m_state == ConnectedState) { + send(HTTP_GET, "/getheight"); + } +} + + +bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code) +{ + Job job(m_id, false, m_pool.algorithm(), String()); + + String blocktemplate = Json::getString(params, "blocktemplate_blob"); + if (blocktemplate.isNull() || !job.setBlob(Json::getString(params, "blockhashing_blob"))) { + *code = 4; + return false; + } + + job.setHeight(Json::getUint64(params, "height")); + job.setDiff(Json::getUint64(params, "difficulty")); + job.setId(blocktemplate.data() + blocktemplate.size() - 48); + + m_job = std::move(job); + m_blocktemplate = std::move(blocktemplate); + + if (m_state == ConnectingState) { + setState(ConnectedState); + } + + m_listener->onJobReceived(this, m_job, params); + return true; +} + + +bool xmrig::DaemonClient::parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error) +{ + if (id == -1) { + return false; + } + + if (error.IsObject()) { + const char *message = error["message"].GetString(); + + if (!handleSubmitResponse(id, message) && !isQuiet()) { + LOG_ERR("[%s:%d] error: " RED_BOLD("\"%s\"") RED_S ", code: %d", m_pool.host().data(), m_pool.port(), message, error["code"].GetInt()); + } + + return false; + } + + if (!result.IsObject()) { + return false; + } + + int code = -1; + if (result.HasMember("blocktemplate_blob") && parseJob(result, &code)) { + return true; + } + + if (handleSubmitResponse(id)) { + getBlockTemplate(); + return true; + } + + + return false; +} + + +int64_t xmrig::DaemonClient::getBlockTemplate() +{ + using namespace rapidjson; + Document doc(kObjectType); + auto &allocator = doc.GetAllocator(); + + doc.AddMember("id", m_sequence, allocator); + doc.AddMember("jsonrpc", "2.0", allocator); + doc.AddMember("method", "getblocktemplate", allocator); + + Value params(kObjectType); + params.AddMember("wallet_address", m_pool.user().toJSON(), allocator); + params.AddMember("reserve_size", 8, allocator); + + doc.AddMember("params", params, allocator); + + send(HTTP_POST, "/json_rpc", doc); + + return m_sequence++; +} + + +void xmrig::DaemonClient::retry() +{ + m_failures++; + m_listener->onClose(this, static_cast(m_failures)); + + if (m_failures == -1) { + return; + } + + if (m_state == ConnectedState) { + setState(ConnectingState); + } + + m_timer->stop(); + m_timer->start(m_retryPause, 0); +} + + +void xmrig::DaemonClient::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\"", + m_pool.host().data(), + m_pool.port(), + http_method_str(static_cast(method)), + url, + size, + static_cast(size), + data); + + HttpClient *client; +# ifdef XMRIG_FEATURE_TLS + if (m_pool.isTLS()) { + client = new HttpsClient(method, url, this, data, size); + } + else +# endif + { + client = new HttpClient(method, url, this, data, size); + } + + client->setQuiet(isQuiet()); + client->connect(m_pool.host(), m_pool.port()); +} + + +void xmrig::DaemonClient::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::DaemonClient::setState(SocketState state) +{ + assert(m_state != state); + if (m_state == state) { + return; + } + + m_state = state; + + switch (state) { + case ConnectedState: + { + m_failures = 0; + m_listener->onLoginSuccess(this); + + const uint64_t interval = std::max(20, m_pool.pollInterval()); + m_timer->start(interval, interval); + } + break; + + case UnconnectedState: + m_failures = -1; + m_timer->stop(); + break; + + default: + break; + } +} diff --git a/src/base/net/stratum/DaemonClient.h b/src/base/net/stratum/DaemonClient.h new file mode 100644 index 00000000..740af6d0 --- /dev/null +++ b/src/base/net/stratum/DaemonClient.h @@ -0,0 +1,78 @@ +/* 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-2019 SChernykh + * Copyright 2019 Howard Chu + * Copyright 2016-2019 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_DAEMONCLIENT_H +#define XMRIG_DAEMONCLIENT_H + + +#include "base/net/stratum/BaseClient.h" +#include "base/kernel/interfaces/ITimerListener.h" +#include "base/kernel/interfaces/IHttpListener.h" + + +namespace xmrig { + + +class DaemonClient : public BaseClient, public ITimerListener, public IHttpListener +{ +public: + DaemonClient(int id, IClientListener *listener); + ~DaemonClient() override; + +protected: + bool disconnect() override; + bool isTLS() const override; + const char *tlsFingerprint() const override; + const char *tlsVersion() const override; + int64_t submit(const JobResult &result) override; + void connect() override; + void connect(const Pool &pool) override; + + void onHttpData(const HttpData &data) override; + void onTimer(const Timer *timer) override; + + inline bool hasExtension(Extension) const noexcept override { return false; } + inline const char *mode() const override { return "daemon"; } + inline void deleteLater() override { delete this; } + inline void tick(uint64_t) override {} + +private: + bool parseJob(const rapidjson::Value ¶ms, int *code); + bool parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error); + int64_t 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 setState(SocketState state); + + String m_blocktemplate; + Timer *m_timer; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_DAEMONCLIENT_H */ diff --git a/src/base/net/stratum/Pool.h b/src/base/net/stratum/Pool.h index d3c9b141..6eec7aa5 100644 --- a/src/base/net/stratum/Pool.h +++ b/src/base/net/stratum/Pool.h @@ -84,6 +84,7 @@ public: inline const String &user() const { return !m_user.isNull() ? m_user : kDefaultUser; } inline int keepAlive() const { return m_keepAlive; } inline uint16_t port() const { return m_port; } + inline uint64_t pollInterval() const { return m_pollInterval; } inline bool operator!=(const Pool &other) const { return !isEqual(other); } inline bool operator==(const Pool &other) const { return isEqual(other); } diff --git a/src/base/net/stratum/strategies/FailoverStrategy.cpp b/src/base/net/stratum/strategies/FailoverStrategy.cpp index 0c574a5d..b89cd955 100644 --- a/src/base/net/stratum/strategies/FailoverStrategy.cpp +++ b/src/base/net/stratum/strategies/FailoverStrategy.cpp @@ -29,6 +29,11 @@ #include "common/Platform.h" +#ifdef XMRIG_FEATURE_HTTP +# include "base/net/stratum/DaemonClient.h" +#endif + + xmrig::FailoverStrategy::FailoverStrategy(const std::vector &pools, int retryPause, int retries, IStrategyListener *listener, bool quiet) : m_quiet(quiet), m_retries(retries), @@ -56,7 +61,7 @@ xmrig::FailoverStrategy::FailoverStrategy(int retryPause, int retries, IStrategy xmrig::FailoverStrategy::~FailoverStrategy() { - for (Client *client : m_pools) { + for (IClient *client : m_pools) { client->deleteLater(); } } @@ -64,7 +69,15 @@ xmrig::FailoverStrategy::~FailoverStrategy() void xmrig::FailoverStrategy::add(const Pool &pool) { - Client *client = new Client(static_cast(m_pools.size()), Platform::userAgent(), this); + const int id = static_cast(m_pools.size()); + +# ifdef XMRIG_FEATURE_HTTP + IClient *client = !pool.isDaemon() ? static_cast(new Client(id, Platform::userAgent(), this)) + : static_cast(new DaemonClient(id, this)); +# else + IClient *client = new Client(id, Platform::userAgent(), this); +# endif + client->setPool(pool); client->setRetries(m_retries); client->setRetryPause(m_retryPause * 1000); @@ -102,7 +115,7 @@ void xmrig::FailoverStrategy::resume() void xmrig::FailoverStrategy::setAlgo(const xmrig::Algorithm &algo) { - for (Client *client : m_pools) { + for (IClient *client : m_pools) { client->setAlgo(algo); } } @@ -123,7 +136,7 @@ void xmrig::FailoverStrategy::stop() void xmrig::FailoverStrategy::tick(uint64_t now) { - for (Client *client : m_pools) { + for (IClient *client : m_pools) { client->tick(now); } } diff --git a/src/base/net/stratum/strategies/FailoverStrategy.h b/src/base/net/stratum/strategies/FailoverStrategy.h index 748eddda..344d815c 100644 --- a/src/base/net/stratum/strategies/FailoverStrategy.h +++ b/src/base/net/stratum/strategies/FailoverStrategy.h @@ -52,7 +52,7 @@ public: protected: inline bool isActive() const override { return m_active >= 0; } - inline Client *client() const override { return active(); } + inline IClient *client() const override { return active(); } inline void onLogin(IClient *, rapidjson::Document &, rapidjson::Value &) override {} int64_t submit(const JobResult &result) override; @@ -68,7 +68,7 @@ protected: void onResultAccepted(IClient *client, const SubmitResult &result, const char *error) override; private: - inline Client *active() const { return m_pools[static_cast(m_active)]; } + inline IClient *active() const { return m_pools[static_cast(m_active)]; } const bool m_quiet; const int m_retries; @@ -76,7 +76,7 @@ private: int m_active; IStrategyListener *m_listener; size_t m_index; - std::vector m_pools; + std::vector m_pools; }; diff --git a/src/base/net/stratum/strategies/SinglePoolStrategy.cpp b/src/base/net/stratum/strategies/SinglePoolStrategy.cpp index 45dce5d9..f432514e 100644 --- a/src/base/net/stratum/strategies/SinglePoolStrategy.cpp +++ b/src/base/net/stratum/strategies/SinglePoolStrategy.cpp @@ -29,11 +29,26 @@ #include "common/Platform.h" +#ifdef XMRIG_FEATURE_HTTP +# include "base/net/stratum/DaemonClient.h" +#endif + + xmrig::SinglePoolStrategy::SinglePoolStrategy(const Pool &pool, int retryPause, int retries, IStrategyListener *listener, bool quiet) : m_active(false), m_listener(listener) { +# ifdef XMRIG_FEATURE_HTTP + if (!pool.isDaemon()) { + m_client = new Client(0, Platform::userAgent(), this); + } + else { + m_client = new DaemonClient(0, this); + } +# else m_client = new Client(0, Platform::userAgent(), this); +# endif + m_client->setPool(pool); m_client->setRetries(retries); m_client->setRetryPause(retryPause * 1000); diff --git a/src/base/net/stratum/strategies/SinglePoolStrategy.h b/src/base/net/stratum/strategies/SinglePoolStrategy.h index b8a4fe1e..af0bd7d6 100644 --- a/src/base/net/stratum/strategies/SinglePoolStrategy.h +++ b/src/base/net/stratum/strategies/SinglePoolStrategy.h @@ -46,7 +46,7 @@ public: protected: inline bool isActive() const override { return m_active; } - inline Client *client() const override { return m_client; } + inline IClient *client() const override { return m_client; } inline void onLogin(IClient *, rapidjson::Document &, rapidjson::Value &) override {} int64_t submit(const JobResult &result) override; @@ -63,7 +63,7 @@ protected: private: bool m_active; - Client *m_client; + IClient *m_client; IStrategyListener *m_listener; }; diff --git a/src/net/strategies/DonateStrategy.cpp b/src/net/strategies/DonateStrategy.cpp index 7a9f9e60..fb958a4c 100644 --- a/src/net/strategies/DonateStrategy.cpp +++ b/src/net/strategies/DonateStrategy.cpp @@ -231,8 +231,8 @@ xmrig::Client *xmrig::DonateStrategy::createProxy() return nullptr; } - const Client *client = strategy->client(); - m_tls = client->hasExtension(IClient::EXT_TLS); + const IClient *client = strategy->client(); + m_tls = client->hasExtension(IClient::EXT_TLS); Pool pool(client->ip(), client->pool().port(), m_userId, client->pool().password(), 0, true, client->isTLS()); pool.setAlgo(client->pool().algorithm()); diff --git a/src/net/strategies/DonateStrategy.h b/src/net/strategies/DonateStrategy.h index 69bf8dbd..c9fc312d 100644 --- a/src/net/strategies/DonateStrategy.h +++ b/src/net/strategies/DonateStrategy.h @@ -52,7 +52,7 @@ public: protected: inline bool isActive() const override { return state() == STATE_ACTIVE; } - inline Client *client() const override { return m_proxy ? m_proxy : m_strategy->client(); } + inline IClient *client() const override { return m_proxy ? m_proxy : m_strategy->client(); } inline void onJob(IStrategy *, IClient *client, const Job &job) override { setJob(client, job); } inline void onJobReceived(IClient *client, const Job &job, const rapidjson::Value &) override { setJob(client, job); } inline void onResultAccepted(IClient *client, const SubmitResult &result, const char *error) override { setResult(client, result, error); } @@ -93,7 +93,7 @@ private: bool m_tls; char m_userId[65]; - Client *m_proxy; + IClient *m_proxy; const uint64_t m_donateTime; const uint64_t m_idleTime; Controller *m_controller;