mirror of
https://github.com/xmrig/xmrig.git
synced 2024-11-18 18:11:05 +00:00
Merge branch 'feature-self-select' into evo
This commit is contained in:
commit
a02ee96651
34 changed files with 1054 additions and 375 deletions
|
@ -41,6 +41,7 @@ set(HEADERS_BASE
|
||||||
src/base/net/stratum/strategies/FailoverStrategy.h
|
src/base/net/stratum/strategies/FailoverStrategy.h
|
||||||
src/base/net/stratum/strategies/SinglePoolStrategy.h
|
src/base/net/stratum/strategies/SinglePoolStrategy.h
|
||||||
src/base/net/stratum/SubmitResult.h
|
src/base/net/stratum/SubmitResult.h
|
||||||
|
src/base/net/stratum/Url.h
|
||||||
src/base/net/tools/RecvBuf.h
|
src/base/net/tools/RecvBuf.h
|
||||||
src/base/net/tools/Storage.h
|
src/base/net/tools/Storage.h
|
||||||
src/base/tools/Arguments.h
|
src/base/tools/Arguments.h
|
||||||
|
@ -78,6 +79,7 @@ set(SOURCES_BASE
|
||||||
src/base/net/stratum/Pools.cpp
|
src/base/net/stratum/Pools.cpp
|
||||||
src/base/net/stratum/strategies/FailoverStrategy.cpp
|
src/base/net/stratum/strategies/FailoverStrategy.cpp
|
||||||
src/base/net/stratum/strategies/SinglePoolStrategy.cpp
|
src/base/net/stratum/strategies/SinglePoolStrategy.cpp
|
||||||
|
src/base/net/stratum/Url.cpp
|
||||||
src/base/tools/Arguments.cpp
|
src/base/tools/Arguments.cpp
|
||||||
src/base/tools/Buffer.cpp
|
src/base/tools/Buffer.cpp
|
||||||
src/base/tools/String.cpp
|
src/base/tools/String.cpp
|
||||||
|
@ -137,6 +139,7 @@ if (WITH_HTTP)
|
||||||
src/base/net/http/HttpResponse.h
|
src/base/net/http/HttpResponse.h
|
||||||
src/base/net/http/HttpServer.h
|
src/base/net/http/HttpServer.h
|
||||||
src/base/net/stratum/DaemonClient.h
|
src/base/net/stratum/DaemonClient.h
|
||||||
|
src/base/net/stratum/SelfSelectClient.h
|
||||||
src/base/net/tools/TcpServer.h
|
src/base/net/tools/TcpServer.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -152,6 +155,7 @@ if (WITH_HTTP)
|
||||||
src/base/net/http/HttpResponse.cpp
|
src/base/net/http/HttpResponse.cpp
|
||||||
src/base/net/http/HttpServer.cpp
|
src/base/net/http/HttpServer.cpp
|
||||||
src/base/net/stratum/DaemonClient.cpp
|
src/base/net/stratum/DaemonClient.cpp
|
||||||
|
src/base/net/stratum/SelfSelectClient.cpp
|
||||||
src/base/net/tools/TcpServer.cpp
|
src/base/net/tools/TcpServer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -183,6 +183,9 @@ void xmrig::BaseTransform::transform(rapidjson::Document &doc, int key, const ch
|
||||||
case IConfig::FingerprintKey: /* --tls-fingerprint */
|
case IConfig::FingerprintKey: /* --tls-fingerprint */
|
||||||
return add(doc, kPools, "tls-fingerprint", arg);
|
return add(doc, kPools, "tls-fingerprint", arg);
|
||||||
|
|
||||||
|
case IConfig::SelfSelectKey: /* --self-select */
|
||||||
|
return add(doc, kPools, "self-select", arg);
|
||||||
|
|
||||||
case IConfig::LogFileKey: /* --log-file */
|
case IConfig::LogFileKey: /* --log-file */
|
||||||
return set(doc, "log-file", arg);
|
return set(doc, "log-file", arg);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,10 @@
|
||||||
#define XMRIG_ICLIENT_H
|
#define XMRIG_ICLIENT_H
|
||||||
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include "rapidjson/fwd.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
namespace xmrig {
|
namespace xmrig {
|
||||||
|
@ -51,6 +54,8 @@ public:
|
||||||
EXT_MAX
|
EXT_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using Callback = std::function<void(const rapidjson::Value &result, bool success, uint64_t elapsed)>;
|
||||||
|
|
||||||
virtual ~IClient() = default;
|
virtual ~IClient() = default;
|
||||||
|
|
||||||
virtual bool disconnect() = 0;
|
virtual bool disconnect() = 0;
|
||||||
|
@ -64,6 +69,9 @@ public:
|
||||||
virtual const Pool &pool() const = 0;
|
virtual const Pool &pool() const = 0;
|
||||||
virtual const String &ip() const = 0;
|
virtual const String &ip() const = 0;
|
||||||
virtual int id() const = 0;
|
virtual int id() const = 0;
|
||||||
|
virtual int64_t send(const rapidjson::Value &obj, Callback callback) = 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 int64_t submit(const JobResult &result) = 0;
|
||||||
virtual void connect() = 0;
|
virtual void connect() = 0;
|
||||||
virtual void connect(const Pool &pool) = 0;
|
virtual void connect(const Pool &pool) = 0;
|
||||||
|
@ -75,7 +83,6 @@ public:
|
||||||
virtual void setRetries(int retries) = 0;
|
virtual void setRetries(int retries) = 0;
|
||||||
virtual void setRetryPause(uint64_t ms) = 0;
|
virtual void setRetryPause(uint64_t ms) = 0;
|
||||||
virtual void tick(uint64_t now) = 0;
|
virtual void tick(uint64_t now) = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#define XMRIG_ICLIENTLISTENER_H
|
#define XMRIG_ICLIENTLISTENER_H
|
||||||
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <cstdint>
|
||||||
|
|
||||||
|
|
||||||
#include "rapidjson/fwd.h"
|
#include "rapidjson/fwd.h"
|
||||||
|
|
|
@ -72,6 +72,7 @@ public:
|
||||||
ProxyDonateKey = 1017,
|
ProxyDonateKey = 1017,
|
||||||
DaemonKey = 1018,
|
DaemonKey = 1018,
|
||||||
DaemonPollKey = 1019,
|
DaemonPollKey = 1019,
|
||||||
|
SelfSelectKey = 1028,
|
||||||
|
|
||||||
// xmrig common
|
// xmrig common
|
||||||
CPUPriorityKey = 1021,
|
CPUPriorityKey = 1021,
|
||||||
|
|
|
@ -78,9 +78,7 @@ private:
|
||||||
|
|
||||||
|
|
||||||
xmrig::HttpClient::HttpClient(int method, const String &url, IHttpListener *listener, const char *data, size_t size) :
|
xmrig::HttpClient::HttpClient(int method, const String &url, IHttpListener *listener, const char *data, size_t size) :
|
||||||
HttpContext(HTTP_RESPONSE, listener),
|
HttpContext(HTTP_RESPONSE, listener)
|
||||||
m_quiet(false),
|
|
||||||
m_port(0)
|
|
||||||
{
|
{
|
||||||
this->method = method;
|
this->method = method;
|
||||||
this->url = url;
|
this->url = url;
|
||||||
|
@ -127,7 +125,7 @@ void xmrig::HttpClient::onResolved(const Dns &dns, int status)
|
||||||
|
|
||||||
sockaddr *addr = dns.get().addr(m_port);
|
sockaddr *addr = dns.get().addr(m_port);
|
||||||
|
|
||||||
uv_connect_t *req = new uv_connect_t;
|
auto req = new uv_connect_t;
|
||||||
req->data = this;
|
req->data = this;
|
||||||
|
|
||||||
uv_tcp_connect(req, m_tcp, addr, onConnect);
|
uv_tcp_connect(req, m_tcp, addr, onConnect);
|
||||||
|
@ -140,7 +138,7 @@ void xmrig::HttpClient::handshake()
|
||||||
headers.insert({ "Connection", "close" });
|
headers.insert({ "Connection", "close" });
|
||||||
headers.insert({ "User-Agent", Platform::userAgent() });
|
headers.insert({ "User-Agent", Platform::userAgent() });
|
||||||
|
|
||||||
if (body.size()) {
|
if (!body.empty()) {
|
||||||
headers.insert({ "Content-Length", std::to_string(body.size()) });
|
headers.insert({ "Content-Length", std::to_string(body.size()) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,14 +167,14 @@ void xmrig::HttpClient::read(const char *data, size_t size)
|
||||||
|
|
||||||
void xmrig::HttpClient::write(const std::string &header)
|
void xmrig::HttpClient::write(const std::string &header)
|
||||||
{
|
{
|
||||||
ClientWriteBaton *baton = new ClientWriteBaton(header, std::move(body));
|
auto baton = new ClientWriteBaton(header, std::move(body));
|
||||||
uv_write(&baton->req, stream(), baton->bufs, baton->count(), ClientWriteBaton::onWrite);
|
uv_write(&baton->req, stream(), baton->bufs, baton->count(), ClientWriteBaton::onWrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void xmrig::HttpClient::onConnect(uv_connect_t *req, int status)
|
void xmrig::HttpClient::onConnect(uv_connect_t *req, int status)
|
||||||
{
|
{
|
||||||
HttpClient *client = static_cast<HttpClient *>(req->data);
|
auto client = static_cast<HttpClient *>(req->data);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
delete req;
|
delete req;
|
||||||
return;
|
return;
|
||||||
|
@ -205,7 +203,7 @@ void xmrig::HttpClient::onConnect(uv_connect_t *req, int status)
|
||||||
},
|
},
|
||||||
[](uv_stream_t *tcp, ssize_t nread, const uv_buf_t *buf)
|
[](uv_stream_t *tcp, ssize_t nread, const uv_buf_t *buf)
|
||||||
{
|
{
|
||||||
HttpClient *client = static_cast<HttpClient*>(tcp->data);
|
auto client = static_cast<HttpClient*>(tcp->data);
|
||||||
|
|
||||||
if (nread >= 0) {
|
if (nread >= 0) {
|
||||||
client->read(buf->base, static_cast<size_t>(nread));
|
client->read(buf->base, static_cast<size_t>(nread));
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include "base/net/http/HttpContext.h"
|
#include "base/net/http/HttpContext.h"
|
||||||
#include "base/kernel/interfaces/IDnsListener.h"
|
#include "base/kernel/interfaces/IDnsListener.h"
|
||||||
|
#include "base/tools/Object.h"
|
||||||
|
|
||||||
|
|
||||||
namespace xmrig {
|
namespace xmrig {
|
||||||
|
@ -41,6 +42,8 @@ class String;
|
||||||
class HttpClient : public HttpContext, public IDnsListener
|
class HttpClient : public HttpContext, public IDnsListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
XMRIG_DISABLE_COPY_MOVE_DEFAULT(HttpClient);
|
||||||
|
|
||||||
HttpClient(int method, const String &url, IHttpListener *listener, const char *data = nullptr, size_t size = 0);
|
HttpClient(int method, const String &url, IHttpListener *listener, const char *data = nullptr, size_t size = 0);
|
||||||
~HttpClient() override;
|
~HttpClient() override;
|
||||||
|
|
||||||
|
@ -57,13 +60,13 @@ protected:
|
||||||
virtual void read(const char *data, size_t size);
|
virtual void read(const char *data, size_t size);
|
||||||
virtual void write(const std::string &header);
|
virtual void write(const std::string &header);
|
||||||
|
|
||||||
bool m_quiet;
|
bool m_quiet = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void onConnect(uv_connect_t *req, int status);
|
static void onConnect(uv_connect_t *req, int status);
|
||||||
|
|
||||||
Dns *m_dns;
|
Dns *m_dns;
|
||||||
uint16_t m_port;
|
uint16_t m_port = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ void xmrig::HttpContext::closeAll()
|
||||||
|
|
||||||
int xmrig::HttpContext::onHeaderField(http_parser *parser, const char *at, size_t length)
|
int xmrig::HttpContext::onHeaderField(http_parser *parser, const char *at, size_t length)
|
||||||
{
|
{
|
||||||
HttpContext *ctx = static_cast<HttpContext*>(parser->data);
|
auto ctx = static_cast<HttpContext*>(parser->data);
|
||||||
|
|
||||||
if (ctx->m_wasHeaderValue) {
|
if (ctx->m_wasHeaderValue) {
|
||||||
if (!ctx->m_lastHeaderField.empty()) {
|
if (!ctx->m_lastHeaderField.empty()) {
|
||||||
|
@ -155,7 +155,7 @@ int xmrig::HttpContext::onHeaderField(http_parser *parser, const char *at, size_
|
||||||
|
|
||||||
int xmrig::HttpContext::onHeaderValue(http_parser *parser, const char *at, size_t length)
|
int xmrig::HttpContext::onHeaderValue(http_parser *parser, const char *at, size_t length)
|
||||||
{
|
{
|
||||||
HttpContext *ctx = static_cast<HttpContext*>(parser->data);
|
auto ctx = static_cast<HttpContext*>(parser->data);
|
||||||
|
|
||||||
if (!ctx->m_wasHeaderValue) {
|
if (!ctx->m_wasHeaderValue) {
|
||||||
ctx->m_lastHeaderValue = std::string(at, length);
|
ctx->m_lastHeaderValue = std::string(at, length);
|
||||||
|
@ -185,7 +185,7 @@ void xmrig::HttpContext::attach(http_parser_settings *settings)
|
||||||
settings->on_header_value = onHeaderValue;
|
settings->on_header_value = onHeaderValue;
|
||||||
|
|
||||||
settings->on_headers_complete = [](http_parser* parser) -> int {
|
settings->on_headers_complete = [](http_parser* parser) -> int {
|
||||||
HttpContext *ctx = static_cast<HttpContext*>(parser->data);
|
auto ctx = static_cast<HttpContext*>(parser->data);
|
||||||
ctx->status = parser->status_code;
|
ctx->status = parser->status_code;
|
||||||
|
|
||||||
if (parser->type == HTTP_REQUEST) {
|
if (parser->type == HTTP_REQUEST) {
|
||||||
|
@ -208,7 +208,7 @@ void xmrig::HttpContext::attach(http_parser_settings *settings)
|
||||||
|
|
||||||
settings->on_message_complete = [](http_parser *parser) -> int
|
settings->on_message_complete = [](http_parser *parser) -> int
|
||||||
{
|
{
|
||||||
HttpContext *ctx = static_cast<HttpContext*>(parser->data);
|
auto ctx = static_cast<HttpContext*>(parser->data);
|
||||||
ctx->m_listener->onHttpData(*ctx);
|
ctx->m_listener->onHttpData(*ctx);
|
||||||
ctx->m_listener = nullptr;
|
ctx->m_listener = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -28,15 +28,16 @@
|
||||||
#define XMRIG_HTTPCONTEXT_H
|
#define XMRIG_HTTPCONTEXT_H
|
||||||
|
|
||||||
|
|
||||||
typedef struct http_parser http_parser;
|
using http_parser = struct http_parser;
|
||||||
typedef struct http_parser_settings http_parser_settings;
|
using http_parser_settings = struct http_parser_settings;
|
||||||
typedef struct uv_connect_s uv_connect_t;
|
using uv_connect_t = struct uv_connect_s;
|
||||||
typedef struct uv_handle_s uv_handle_t;
|
using uv_handle_t = struct uv_handle_s;
|
||||||
typedef struct uv_stream_s uv_stream_t;
|
using uv_stream_t = struct uv_stream_s;
|
||||||
typedef struct uv_tcp_s uv_tcp_t;
|
using uv_tcp_t = struct uv_tcp_s;
|
||||||
|
|
||||||
|
|
||||||
#include "base/net/http/HttpData.h"
|
#include "base/net/http/HttpData.h"
|
||||||
|
#include "base/tools/Object.h"
|
||||||
|
|
||||||
|
|
||||||
namespace xmrig {
|
namespace xmrig {
|
||||||
|
@ -48,6 +49,8 @@ class IHttpListener;
|
||||||
class HttpContext : public HttpData
|
class HttpContext : public HttpData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
XMRIG_DISABLE_COPY_MOVE_DEFAULT(HttpContext)
|
||||||
|
|
||||||
HttpContext(int parser_type, IHttpListener *listener);
|
HttpContext(int parser_type, IHttpListener *listener);
|
||||||
virtual ~HttpContext();
|
virtual ~HttpContext();
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <cassert>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <uv.h>
|
#include <uv.h>
|
||||||
|
|
||||||
|
|
|
@ -28,10 +28,10 @@
|
||||||
#define XMRIG_HTTPSCLIENT_H
|
#define XMRIG_HTTPSCLIENT_H
|
||||||
|
|
||||||
|
|
||||||
typedef struct bio_st BIO;
|
using BIO = struct bio_st;
|
||||||
typedef struct ssl_ctx_st SSL_CTX;
|
using SSL_CTX = struct ssl_ctx_st;
|
||||||
typedef struct ssl_st SSL;
|
using SSL = struct ssl_st;
|
||||||
typedef struct x509_st X509;
|
using X509 = struct x509_st;
|
||||||
|
|
||||||
|
|
||||||
#include "base/net/http/HttpClient.h"
|
#include "base/net/http/HttpClient.h"
|
||||||
|
@ -44,6 +44,8 @@ namespace xmrig {
|
||||||
class HttpsClient : public HttpClient
|
class HttpsClient : public HttpClient
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
XMRIG_DISABLE_COPY_MOVE_DEFAULT(HttpsClient)
|
||||||
|
|
||||||
HttpsClient(int method, const String &url, IHttpListener *listener, const char *data, size_t size, const String &fingerprint);
|
HttpsClient(int method, const String &url, IHttpListener *listener, const char *data, size_t size, const String &fingerprint);
|
||||||
~HttpsClient() override;
|
~HttpsClient() override;
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "base/kernel/interfaces/IClientListener.h"
|
#include "base/kernel/interfaces/IClientListener.h"
|
||||||
#include "base/net/stratum/BaseClient.h"
|
#include "base/net/stratum/BaseClient.h"
|
||||||
#include "base/net/stratum/SubmitResult.h"
|
#include "base/net/stratum/SubmitResult.h"
|
||||||
|
#include "rapidjson/document.h"
|
||||||
|
|
||||||
|
|
||||||
namespace xmrig {
|
namespace xmrig {
|
||||||
|
@ -36,18 +37,38 @@ int64_t BaseClient::m_sequence = 1;
|
||||||
|
|
||||||
|
|
||||||
xmrig::BaseClient::BaseClient(int id, IClientListener *listener) :
|
xmrig::BaseClient::BaseClient(int id, IClientListener *listener) :
|
||||||
m_quiet(false),
|
|
||||||
m_listener(listener),
|
m_listener(listener),
|
||||||
m_id(id),
|
m_id(id)
|
||||||
m_retries(5),
|
|
||||||
m_failures(0),
|
|
||||||
m_state(UnconnectedState),
|
|
||||||
m_retryPause(5000),
|
|
||||||
m_enabled(true)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool xmrig::BaseClient::handleResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error)
|
||||||
|
{
|
||||||
|
if (id == 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = m_callbacks.find(id);
|
||||||
|
if (it != m_callbacks.end()) {
|
||||||
|
const uint64_t elapsed = Chrono::steadyMSecs() - it->second.ts;
|
||||||
|
|
||||||
|
if (error.IsObject()) {
|
||||||
|
it->second.callback(error, false, elapsed);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
it->second.callback(result, true, elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_callbacks.erase(it);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool xmrig::BaseClient::handleSubmitResponse(int64_t id, const char *error)
|
bool xmrig::BaseClient::handleSubmitResponse(int64_t id, const char *error)
|
||||||
{
|
{
|
||||||
auto it = m_results.find(id);
|
auto it = m_results.find(id);
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "base/kernel/interfaces/IClient.h"
|
#include "base/kernel/interfaces/IClient.h"
|
||||||
#include "base/net/stratum/Job.h"
|
#include "base/net/stratum/Job.h"
|
||||||
#include "base/net/stratum/Pool.h"
|
#include "base/net/stratum/Pool.h"
|
||||||
|
#include "base/tools/Chrono.h"
|
||||||
|
|
||||||
|
|
||||||
namespace xmrig {
|
namespace xmrig {
|
||||||
|
@ -46,11 +47,13 @@ class BaseClient : public IClient
|
||||||
public:
|
public:
|
||||||
BaseClient(int id, IClientListener *listener);
|
BaseClient(int id, IClientListener *listener);
|
||||||
|
|
||||||
|
protected:
|
||||||
inline bool isEnabled() const override { return m_enabled; }
|
inline bool isEnabled() const override { return m_enabled; }
|
||||||
inline const Job &job() const override { return m_job; }
|
inline const Job &job() const override { return m_job; }
|
||||||
inline const Pool &pool() const override { return m_pool; }
|
inline const Pool &pool() const override { return m_pool; }
|
||||||
inline const String &ip() const override { return m_ip; }
|
inline const String &ip() const override { return m_ip; }
|
||||||
inline int id() const override { return m_id; }
|
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 setAlgo(const Algorithm &algo) override { m_pool.setAlgo(algo); }
|
||||||
inline void setEnabled(bool enabled) override { m_enabled = enabled; }
|
inline void setEnabled(bool enabled) override { m_enabled = enabled; }
|
||||||
inline void setPool(const Pool &pool) override { if (pool.isValid()) { m_pool = pool; } }
|
inline void setPool(const Pool &pool) override { if (pool.isValid()) { m_pool = pool; } }
|
||||||
|
@ -68,26 +71,36 @@ protected:
|
||||||
ReconnectingState
|
ReconnectingState
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SendResult
|
||||||
|
{
|
||||||
|
inline SendResult(Callback &&callback) : callback(callback), ts(Chrono::steadyMSecs()) {}
|
||||||
|
|
||||||
|
Callback callback;
|
||||||
|
const uint64_t ts;
|
||||||
|
};
|
||||||
|
|
||||||
inline bool isQuiet() const { return m_quiet || m_failures >= m_retries; }
|
inline bool isQuiet() const { return m_quiet || m_failures >= m_retries; }
|
||||||
|
|
||||||
|
bool handleResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error);
|
||||||
bool handleSubmitResponse(int64_t id, const char *error = nullptr);
|
bool handleSubmitResponse(int64_t id, const char *error = nullptr);
|
||||||
|
|
||||||
bool m_quiet;
|
bool m_quiet = false;
|
||||||
IClientListener *m_listener;
|
IClientListener *m_listener;
|
||||||
int m_id;
|
int m_id;
|
||||||
int m_retries;
|
int m_retries = 5;
|
||||||
int64_t m_failures;
|
int64_t m_failures = 0;
|
||||||
Job m_job;
|
Job m_job;
|
||||||
Pool m_pool;
|
Pool m_pool;
|
||||||
SocketState m_state;
|
SocketState m_state = UnconnectedState;
|
||||||
|
std::map<int64_t, SendResult> m_callbacks;
|
||||||
std::map<int64_t, SubmitResult> m_results;
|
std::map<int64_t, SubmitResult> m_results;
|
||||||
String m_ip;
|
String m_ip;
|
||||||
uint64_t m_retryPause;
|
uint64_t m_retryPause = 5000;
|
||||||
|
|
||||||
static int64_t m_sequence;
|
static int64_t m_sequence;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_enabled;
|
bool m_enabled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||||
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||||
|
* Copyright 2019 jtgrassie <https://github.com/jtgrassie>
|
||||||
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
@ -22,11 +23,11 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#include <cassert>
|
||||||
#include <inttypes.h>
|
#include <cinttypes>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <stdio.h>
|
#include <cstdio>
|
||||||
#include <string.h>
|
#include <cstring>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,6 +137,41 @@ const char *xmrig::Client::tlsVersion() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int64_t xmrig::Client::send(const rapidjson::Value &obj, Callback callback)
|
||||||
|
{
|
||||||
|
assert(obj["id"] == sequence());
|
||||||
|
|
||||||
|
m_callbacks.insert({ sequence(), std::move(callback) });
|
||||||
|
|
||||||
|
return send(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int64_t xmrig::Client::send(const rapidjson::Value &obj)
|
||||||
|
{
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
Value value;
|
||||||
|
|
||||||
|
StringBuffer buffer(nullptr, 512);
|
||||||
|
Writer<StringBuffer> 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)
|
int64_t xmrig::Client::submit(const JobResult &result)
|
||||||
{
|
{
|
||||||
# ifndef XMRIG_PROXY_PROJECT
|
# ifndef XMRIG_PROXY_PROJECT
|
||||||
|
@ -320,10 +356,24 @@ bool xmrig::Client::parseJob(const rapidjson::Value ¶ms, int *code)
|
||||||
return false;
|
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())) {
|
if (!job.setBlob(params["blob"].GetString())) {
|
||||||
*code = 4;
|
*code = 4;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!job.setTarget(params["target"].GetString())) {
|
if (!job.setTarget(params["target"].GetString())) {
|
||||||
*code = 5;
|
*code = 5;
|
||||||
|
@ -345,7 +395,7 @@ bool xmrig::Client::parseJob(const rapidjson::Value ¶ms, int *code)
|
||||||
return false;
|
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()) {
|
if (!isQuiet()) {
|
||||||
LOG_ERR("[%s] failed to parse field \"seed_hash\" required by RandomX", url(), algo);
|
LOG_ERR("[%s] failed to parse field \"seed_hash\" required by RandomX", url(), algo);
|
||||||
}
|
}
|
||||||
|
@ -473,29 +523,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<StringBuffer> 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)
|
int64_t xmrig::Client::send(size_t size)
|
||||||
{
|
{
|
||||||
LOG_DEBUG("[%s] send (%d bytes): \"%.*s\"", url(), size, static_cast<int>(size) - 1, m_sendBuf);
|
LOG_DEBUG("[%s] send (%d bytes): \"%.*s\"", url(), size, static_cast<int>(size) - 1, m_sendBuf);
|
||||||
|
@ -719,6 +746,10 @@ void xmrig::Client::parseNotification(const char *method, const rapidjson::Value
|
||||||
|
|
||||||
void xmrig::Client::parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error)
|
void xmrig::Client::parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error)
|
||||||
{
|
{
|
||||||
|
if (handleResponse(id, result, error)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (error.IsObject()) {
|
if (error.IsObject()) {
|
||||||
const char *message = error["message"].GetString();
|
const char *message = error["message"].GetString();
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||||
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||||
|
* Copyright 2019 jtgrassie <https://github.com/jtgrassie>
|
||||||
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
@ -40,10 +41,11 @@
|
||||||
#include "base/net/stratum/SubmitResult.h"
|
#include "base/net/stratum/SubmitResult.h"
|
||||||
#include "base/net/tools/RecvBuf.h"
|
#include "base/net/tools/RecvBuf.h"
|
||||||
#include "base/net/tools/Storage.h"
|
#include "base/net/tools/Storage.h"
|
||||||
|
#include "base/tools/Object.h"
|
||||||
#include "crypto/common/Algorithm.h"
|
#include "crypto/common/Algorithm.h"
|
||||||
|
|
||||||
|
|
||||||
typedef struct bio_st BIO;
|
using BIO = struct bio_st;
|
||||||
|
|
||||||
|
|
||||||
namespace xmrig {
|
namespace xmrig {
|
||||||
|
@ -56,6 +58,8 @@ class JobResult;
|
||||||
class Client : public BaseClient, public IDnsListener, public ILineListener
|
class Client : public BaseClient, public IDnsListener, public ILineListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
XMRIG_DISABLE_COPY_MOVE_DEFAULT(Client)
|
||||||
|
|
||||||
constexpr static uint64_t kConnectTimeout = 20 * 1000;
|
constexpr static uint64_t kConnectTimeout = 20 * 1000;
|
||||||
constexpr static uint64_t kResponseTimeout = 20 * 1000;
|
constexpr static uint64_t kResponseTimeout = 20 * 1000;
|
||||||
|
|
||||||
|
@ -73,6 +77,8 @@ protected:
|
||||||
bool isTLS() const override;
|
bool isTLS() const override;
|
||||||
const char *tlsFingerprint() const override;
|
const char *tlsFingerprint() const override;
|
||||||
const char *tlsVersion() const override;
|
const char *tlsVersion() const override;
|
||||||
|
int64_t send(const rapidjson::Value &obj, Callback callback) override;
|
||||||
|
int64_t send(const rapidjson::Value &obj) override;
|
||||||
int64_t submit(const JobResult &result) override;
|
int64_t submit(const JobResult &result) override;
|
||||||
void connect() override;
|
void connect() override;
|
||||||
void connect(const Pool &pool) override;
|
void connect(const Pool &pool) override;
|
||||||
|
@ -95,7 +101,6 @@ private:
|
||||||
bool send(BIO *bio);
|
bool send(BIO *bio);
|
||||||
bool verifyAlgorithm(const Algorithm &algorithm, const char *algo) const;
|
bool verifyAlgorithm(const Algorithm &algorithm, const char *algo) const;
|
||||||
int resolve(const String &host);
|
int resolve(const String &host);
|
||||||
int64_t send(const rapidjson::Document &doc);
|
|
||||||
int64_t send(size_t size);
|
int64_t send(size_t size);
|
||||||
void connect(sockaddr *addr);
|
void connect(sockaddr *addr);
|
||||||
void handshake();
|
void handshake();
|
||||||
|
|
|
@ -27,9 +27,10 @@
|
||||||
#define 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"
|
#include "base/kernel/interfaces/IHttpListener.h"
|
||||||
|
#include "base/kernel/interfaces/ITimerListener.h"
|
||||||
|
#include "base/net/stratum/BaseClient.h"
|
||||||
|
#include "base/tools/Object.h"
|
||||||
|
|
||||||
|
|
||||||
namespace xmrig {
|
namespace xmrig {
|
||||||
|
@ -38,6 +39,8 @@ namespace xmrig {
|
||||||
class DaemonClient : public BaseClient, public ITimerListener, public IHttpListener
|
class DaemonClient : public BaseClient, public ITimerListener, public IHttpListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
XMRIG_DISABLE_COPY_MOVE_DEFAULT(DaemonClient)
|
||||||
|
|
||||||
DaemonClient(int id, IClientListener *listener);
|
DaemonClient(int id, IClientListener *listener);
|
||||||
~DaemonClient() override;
|
~DaemonClient() override;
|
||||||
|
|
||||||
|
@ -55,6 +58,8 @@ protected:
|
||||||
inline const char *mode() const override { return "daemon"; }
|
inline const char *mode() const override { return "daemon"; }
|
||||||
inline const char *tlsFingerprint() const override { return m_tlsFingerprint; }
|
inline const char *tlsFingerprint() const override { return m_tlsFingerprint; }
|
||||||
inline const char *tlsVersion() const override { return m_tlsVersion; }
|
inline const char *tlsVersion() const override { return m_tlsVersion; }
|
||||||
|
inline int64_t send(const rapidjson::Value &, Callback) override { return -1; }
|
||||||
|
inline int64_t send(const rapidjson::Value &) override { return -1; }
|
||||||
inline void deleteLater() override { delete this; }
|
inline void deleteLater() override { delete this; }
|
||||||
inline void tick(uint64_t) override {}
|
inline void tick(uint64_t) override {}
|
||||||
|
|
||||||
|
|
|
@ -162,6 +162,8 @@ void xmrig::Job::copy(const Job &other)
|
||||||
m_target = other.m_target;
|
m_target = other.m_target;
|
||||||
m_index = other.m_index;
|
m_index = other.m_index;
|
||||||
m_seed = other.m_seed;
|
m_seed = other.m_seed;
|
||||||
|
m_extraNonce = other.m_extraNonce;
|
||||||
|
m_poolWallet = other.m_poolWallet;
|
||||||
|
|
||||||
memcpy(m_blob, other.m_blob, sizeof(m_blob));
|
memcpy(m_blob, other.m_blob, sizeof(m_blob));
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,9 @@ public:
|
||||||
inline const Algorithm &algorithm() const { return m_algorithm; }
|
inline const Algorithm &algorithm() const { return m_algorithm; }
|
||||||
inline const Buffer &seed() const { return m_seed; }
|
inline const Buffer &seed() const { return m_seed; }
|
||||||
inline const String &clientId() const { return m_clientId; }
|
inline const String &clientId() const { return m_clientId; }
|
||||||
|
inline const String &extraNonce() const { return m_extraNonce; }
|
||||||
inline const String &id() const { return m_id; }
|
inline const String &id() const { return m_id; }
|
||||||
|
inline const String &poolWallet() const { return m_poolWallet; }
|
||||||
inline const uint32_t *nonce() const { return reinterpret_cast<const uint32_t*>(m_blob + 39); }
|
inline const uint32_t *nonce() const { return reinterpret_cast<const uint32_t*>(m_blob + 39); }
|
||||||
inline const uint8_t *blob() const { return m_blob; }
|
inline const uint8_t *blob() const { return m_blob; }
|
||||||
inline size_t size() const { return m_size; }
|
inline size_t size() const { return m_size; }
|
||||||
|
@ -78,8 +80,10 @@ public:
|
||||||
inline void setAlgorithm(const Algorithm::Id id) { m_algorithm = id; }
|
inline void setAlgorithm(const Algorithm::Id id) { m_algorithm = id; }
|
||||||
inline void setAlgorithm(const char *algo) { m_algorithm = algo; }
|
inline void setAlgorithm(const char *algo) { m_algorithm = algo; }
|
||||||
inline void setClientId(const String &id) { m_clientId = id; }
|
inline void setClientId(const String &id) { m_clientId = id; }
|
||||||
|
inline void setExtraNonce(const String &extraNonce) { m_extraNonce = extraNonce; }
|
||||||
inline void setHeight(uint64_t height) { m_height = height; }
|
inline void setHeight(uint64_t height) { m_height = height; }
|
||||||
inline void setIndex(uint8_t index) { m_index = index; }
|
inline void setIndex(uint8_t index) { m_index = index; }
|
||||||
|
inline void setPoolWallet(const String &poolWallet) { m_poolWallet = poolWallet; }
|
||||||
|
|
||||||
# ifdef XMRIG_PROXY_PROJECT
|
# ifdef XMRIG_PROXY_PROJECT
|
||||||
inline char *rawBlob() { return m_rawBlob; }
|
inline char *rawBlob() { return m_rawBlob; }
|
||||||
|
@ -103,7 +107,9 @@ private:
|
||||||
Buffer m_seed;
|
Buffer m_seed;
|
||||||
size_t m_size = 0;
|
size_t m_size = 0;
|
||||||
String m_clientId;
|
String m_clientId;
|
||||||
|
String m_extraNonce;
|
||||||
String m_id;
|
String m_id;
|
||||||
|
String m_poolWallet;
|
||||||
uint64_t m_diff = 0;
|
uint64_t m_diff = 0;
|
||||||
uint64_t m_height = 0;
|
uint64_t m_height = 0;
|
||||||
uint64_t m_target = 0;
|
uint64_t m_target = 0;
|
||||||
|
|
|
@ -24,24 +24,23 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <cassert>
|
||||||
#include <string.h>
|
#include <cstring>
|
||||||
#include <stdlib.h>
|
#include <cstdlib>
|
||||||
#include <stdio.h>
|
#include <cstdio>
|
||||||
|
|
||||||
|
|
||||||
#include "base/io/json/Json.h"
|
|
||||||
#include "base/net/stratum/Pool.h"
|
#include "base/net/stratum/Pool.h"
|
||||||
|
#include "base/io/json/Json.h"
|
||||||
|
#include "base/io/log/Log.h"
|
||||||
|
#include "base/kernel/Platform.h"
|
||||||
|
#include "base/net/stratum/Client.h"
|
||||||
#include "rapidjson/document.h"
|
#include "rapidjson/document.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef APP_DEBUG
|
#ifdef XMRIG_FEATURE_HTTP
|
||||||
# include "base/io/log/Log.h"
|
# include "base/net/stratum/DaemonClient.h"
|
||||||
#endif
|
# include "base/net/stratum/SelfSelectClient.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
# define strncasecmp _strnicmp
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,6 +56,7 @@ static const char *kKeepalive = "keepalive";
|
||||||
static const char *kNicehash = "nicehash";
|
static const char *kNicehash = "nicehash";
|
||||||
static const char *kPass = "pass";
|
static const char *kPass = "pass";
|
||||||
static const char *kRigId = "rig-id";
|
static const char *kRigId = "rig-id";
|
||||||
|
static const char *kSelfSelect = "self-select";
|
||||||
static const char *kTls = "tls";
|
static const char *kTls = "tls";
|
||||||
static const char *kUrl = "url";
|
static const char *kUrl = "url";
|
||||||
static const char *kUser = "user";
|
static const char *kUser = "user";
|
||||||
|
@ -64,54 +64,23 @@ static const char *kUser = "user";
|
||||||
const String Pool::kDefaultPassword = "x";
|
const String Pool::kDefaultPassword = "x";
|
||||||
const String Pool::kDefaultUser = "x";
|
const String Pool::kDefaultUser = "x";
|
||||||
|
|
||||||
static const char kStratumTcp[] = "stratum+tcp://";
|
|
||||||
static const char kStratumSsl[] = "stratum+ssl://";
|
|
||||||
|
|
||||||
#ifdef XMRIG_FEATURE_HTTP
|
|
||||||
static const char kDaemonHttp[] = "daemon+http://";
|
|
||||||
static const char kDaemonHttps[] = "daemon+https://";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
xmrig::Pool::Pool() :
|
|
||||||
m_keepAlive(0),
|
|
||||||
m_flags(0),
|
|
||||||
m_port(kDefaultPort),
|
|
||||||
m_pollInterval(kDefaultPollInterval)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Parse url.
|
|
||||||
*
|
|
||||||
* Valid urls:
|
|
||||||
* example.com
|
|
||||||
* example.com:3333
|
|
||||||
* stratum+tcp://example.com
|
|
||||||
* stratum+tcp://example.com:3333
|
|
||||||
*
|
|
||||||
* @param url
|
|
||||||
*/
|
|
||||||
xmrig::Pool::Pool(const char *url) :
|
xmrig::Pool::Pool(const char *url) :
|
||||||
m_keepAlive(0),
|
m_flags(1 << FLAG_ENABLED),
|
||||||
m_flags(1),
|
m_pollInterval(kDefaultPollInterval),
|
||||||
m_port(kDefaultPort),
|
m_url(url)
|
||||||
m_pollInterval(kDefaultPollInterval)
|
|
||||||
{
|
{
|
||||||
parse(url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
xmrig::Pool::Pool(const rapidjson::Value &object) :
|
xmrig::Pool::Pool(const rapidjson::Value &object) :
|
||||||
m_keepAlive(0),
|
m_flags(1 << FLAG_ENABLED),
|
||||||
m_flags(1),
|
m_pollInterval(kDefaultPollInterval),
|
||||||
m_port(kDefaultPort),
|
m_url(Json::getString(object, kUrl))
|
||||||
m_pollInterval(kDefaultPollInterval)
|
|
||||||
{
|
{
|
||||||
if (!parse(Json::getString(object, kUrl))) {
|
if (!m_url.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,11 +91,18 @@ xmrig::Pool::Pool(const rapidjson::Value &object) :
|
||||||
m_pollInterval = Json::getUint64(object, kDaemonPollInterval, kDefaultPollInterval);
|
m_pollInterval = Json::getUint64(object, kDaemonPollInterval, kDefaultPollInterval);
|
||||||
m_algorithm = Json::getString(object, kAlgo);
|
m_algorithm = Json::getString(object, kAlgo);
|
||||||
m_coin = Json::getString(object, kCoin);
|
m_coin = Json::getString(object, kCoin);
|
||||||
|
m_daemon = Json::getString(object, kSelfSelect);
|
||||||
|
|
||||||
m_flags.set(FLAG_ENABLED, Json::getBool(object, kEnabled, true));
|
m_flags.set(FLAG_ENABLED, Json::getBool(object, kEnabled, true));
|
||||||
m_flags.set(FLAG_NICEHASH, Json::getBool(object, kNicehash));
|
m_flags.set(FLAG_NICEHASH, Json::getBool(object, kNicehash));
|
||||||
m_flags.set(FLAG_TLS, Json::getBool(object, kTls, m_flags.test(FLAG_TLS)));
|
m_flags.set(FLAG_TLS, Json::getBool(object, kTls) || m_url.isTLS());
|
||||||
m_flags.set(FLAG_DAEMON, Json::getBool(object, kDaemon, m_flags.test(FLAG_DAEMON)));
|
|
||||||
|
if (m_daemon.isValid()) {
|
||||||
|
m_mode = MODE_SELF_SELECT;
|
||||||
|
}
|
||||||
|
else if (Json::getBool(object, kDaemon)) {
|
||||||
|
m_mode = MODE_DAEMON;
|
||||||
|
}
|
||||||
|
|
||||||
const rapidjson::Value &keepalive = Json::getValue(object, kKeepalive);
|
const rapidjson::Value &keepalive = Json::getValue(object, kKeepalive);
|
||||||
if (keepalive.IsInt()) {
|
if (keepalive.IsInt()) {
|
||||||
|
@ -140,21 +116,12 @@ xmrig::Pool::Pool(const rapidjson::Value &object) :
|
||||||
|
|
||||||
xmrig::Pool::Pool(const char *host, uint16_t port, const char *user, const char *password, int keepAlive, bool nicehash, bool tls) :
|
xmrig::Pool::Pool(const char *host, uint16_t port, const char *user, const char *password, int keepAlive, bool nicehash, bool tls) :
|
||||||
m_keepAlive(keepAlive),
|
m_keepAlive(keepAlive),
|
||||||
m_flags(1),
|
m_flags(1 << FLAG_ENABLED),
|
||||||
m_host(host),
|
|
||||||
m_password(password),
|
m_password(password),
|
||||||
m_user(user),
|
m_user(user),
|
||||||
m_port(port),
|
m_pollInterval(kDefaultPollInterval),
|
||||||
m_pollInterval(kDefaultPollInterval)
|
m_url(host, port, tls)
|
||||||
{
|
{
|
||||||
const size_t size = m_host.size() + 8;
|
|
||||||
assert(size > 8);
|
|
||||||
|
|
||||||
char *url = new char[size]();
|
|
||||||
snprintf(url, size - 1, "%s:%d", m_host.data(), m_port);
|
|
||||||
|
|
||||||
m_url = url;
|
|
||||||
|
|
||||||
m_flags.set(FLAG_NICEHASH, nicehash);
|
m_flags.set(FLAG_NICEHASH, nicehash);
|
||||||
m_flags.set(FLAG_TLS, tls);
|
m_flags.set(FLAG_TLS, tls);
|
||||||
}
|
}
|
||||||
|
@ -169,12 +136,18 @@ bool xmrig::Pool::isEnabled() const
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# ifndef XMRIG_FEATURE_HTTP
|
# ifndef XMRIG_FEATURE_HTTP
|
||||||
if (isDaemon()) {
|
if (m_mode == MODE_DAEMON) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
if (isDaemon() && (!algorithm().isValid() && !coin().isValid())) {
|
# ifndef XMRIG_FEATURE_HTTP
|
||||||
|
if (m_mode == MODE_SELF_SELECT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
if (m_mode == MODE_DAEMON && (!algorithm().isValid() && !coin().isValid())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,79 +159,43 @@ bool xmrig::Pool::isEqual(const Pool &other) const
|
||||||
{
|
{
|
||||||
return (m_flags == other.m_flags
|
return (m_flags == other.m_flags
|
||||||
&& m_keepAlive == other.m_keepAlive
|
&& m_keepAlive == other.m_keepAlive
|
||||||
&& m_port == other.m_port
|
|
||||||
&& m_algorithm == other.m_algorithm
|
&& m_algorithm == other.m_algorithm
|
||||||
&& m_coin == other.m_coin
|
&& m_coin == other.m_coin
|
||||||
|
&& m_mode == other.m_mode
|
||||||
&& m_fingerprint == other.m_fingerprint
|
&& m_fingerprint == other.m_fingerprint
|
||||||
&& m_host == other.m_host
|
|
||||||
&& m_password == other.m_password
|
&& m_password == other.m_password
|
||||||
&& m_rigId == other.m_rigId
|
&& m_rigId == other.m_rigId
|
||||||
&& m_url == other.m_url
|
&& m_url == other.m_url
|
||||||
&& m_user == other.m_user
|
&& m_user == other.m_user
|
||||||
&& m_pollInterval == other.m_pollInterval
|
&& m_pollInterval == other.m_pollInterval
|
||||||
|
&& m_daemon == other.m_daemon
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool xmrig::Pool::parse(const char *url)
|
xmrig::IClient *xmrig::Pool::createClient(int id, IClientListener *listener) const
|
||||||
{
|
{
|
||||||
assert(url != nullptr);
|
IClient *client = nullptr;
|
||||||
if (url == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *p = strstr(url, "://");
|
if (m_mode == MODE_POOL) {
|
||||||
const char *base = url;
|
client = new Client(id, Platform::userAgent(), listener);
|
||||||
|
|
||||||
if (p) {
|
|
||||||
if (strncasecmp(url, kStratumTcp, sizeof(kStratumTcp) - 1) == 0) {
|
|
||||||
m_flags.set(FLAG_DAEMON, false);
|
|
||||||
m_flags.set(FLAG_TLS, false);
|
|
||||||
}
|
|
||||||
else if (strncasecmp(url, kStratumSsl, sizeof(kStratumSsl) - 1) == 0) {
|
|
||||||
m_flags.set(FLAG_DAEMON, false);
|
|
||||||
m_flags.set(FLAG_TLS, true);
|
|
||||||
}
|
}
|
||||||
# ifdef XMRIG_FEATURE_HTTP
|
# ifdef XMRIG_FEATURE_HTTP
|
||||||
else if (strncasecmp(url, kDaemonHttps, sizeof(kDaemonHttps) - 1) == 0) {
|
else if (m_mode == MODE_DAEMON) {
|
||||||
m_flags.set(FLAG_DAEMON, true);
|
client = new DaemonClient(id, listener);
|
||||||
m_flags.set(FLAG_TLS, true);
|
|
||||||
}
|
}
|
||||||
else if (strncasecmp(url, kDaemonHttp, sizeof(kDaemonHttp) - 1) == 0) {
|
else if (m_mode == MODE_SELF_SELECT) {
|
||||||
m_flags.set(FLAG_DAEMON, true);
|
client = new SelfSelectClient(id, Platform::userAgent(), listener);
|
||||||
m_flags.set(FLAG_TLS, false);
|
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
else {
|
|
||||||
return false;
|
assert(client != nullptr);
|
||||||
|
|
||||||
|
if (client) {
|
||||||
|
client->setPool(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
base = p + 3;
|
return client;
|
||||||
}
|
|
||||||
|
|
||||||
if (!strlen(base) || *base == '/') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_url = url;
|
|
||||||
if (base[0] == '[') {
|
|
||||||
return parseIPv6(base);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *port = strchr(base, ':');
|
|
||||||
if (!port) {
|
|
||||||
m_host = base;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t size = static_cast<size_t>(port++ - base + 1);
|
|
||||||
char *host = new char[size]();
|
|
||||||
memcpy(host, base, size - 1);
|
|
||||||
|
|
||||||
m_host = host;
|
|
||||||
m_port = static_cast<uint16_t>(strtol(port, nullptr, 10));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -272,10 +209,10 @@ rapidjson::Value xmrig::Pool::toJSON(rapidjson::Document &doc) const
|
||||||
|
|
||||||
obj.AddMember(StringRef(kAlgo), m_algorithm.toJSON(), allocator);
|
obj.AddMember(StringRef(kAlgo), m_algorithm.toJSON(), allocator);
|
||||||
obj.AddMember(StringRef(kCoin), m_coin.toJSON(), allocator);
|
obj.AddMember(StringRef(kCoin), m_coin.toJSON(), allocator);
|
||||||
obj.AddMember(StringRef(kUrl), m_url.toJSON(), allocator);
|
obj.AddMember(StringRef(kUrl), url().toJSON(), allocator);
|
||||||
obj.AddMember(StringRef(kUser), m_user.toJSON(), allocator);
|
obj.AddMember(StringRef(kUser), m_user.toJSON(), allocator);
|
||||||
|
|
||||||
if (!isDaemon()) {
|
if (m_mode != MODE_DAEMON) {
|
||||||
obj.AddMember(StringRef(kPass), m_password.toJSON(), allocator);
|
obj.AddMember(StringRef(kPass), m_password.toJSON(), allocator);
|
||||||
obj.AddMember(StringRef(kRigId), m_rigId.toJSON(), allocator);
|
obj.AddMember(StringRef(kRigId), m_rigId.toJSON(), allocator);
|
||||||
|
|
||||||
|
@ -294,22 +231,43 @@ rapidjson::Value xmrig::Pool::toJSON(rapidjson::Document &doc) const
|
||||||
obj.AddMember(StringRef(kEnabled), m_flags.test(FLAG_ENABLED), allocator);
|
obj.AddMember(StringRef(kEnabled), m_flags.test(FLAG_ENABLED), allocator);
|
||||||
obj.AddMember(StringRef(kTls), isTLS(), allocator);
|
obj.AddMember(StringRef(kTls), isTLS(), allocator);
|
||||||
obj.AddMember(StringRef(kFingerprint), m_fingerprint.toJSON(), allocator);
|
obj.AddMember(StringRef(kFingerprint), m_fingerprint.toJSON(), allocator);
|
||||||
obj.AddMember(StringRef(kDaemon), m_flags.test(FLAG_DAEMON), allocator);
|
obj.AddMember(StringRef(kDaemon), m_mode == MODE_DAEMON, allocator);
|
||||||
|
|
||||||
if (isDaemon()) {
|
if (m_mode == MODE_DAEMON) {
|
||||||
obj.AddMember(StringRef(kDaemonPollInterval), m_pollInterval, allocator);
|
obj.AddMember(StringRef(kDaemonPollInterval), m_pollInterval, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obj.AddMember(StringRef(kSelfSelect), m_daemon.url().toJSON(), allocator);
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string xmrig::Pool::printableName() const
|
||||||
|
{
|
||||||
|
std::string out(CSI "1;" + std::to_string(isEnabled() ? (isTLS() ? 32 : 36) : 31) + "m" + url().data() + CLEAR);
|
||||||
|
|
||||||
|
if (m_coin.isValid()) {
|
||||||
|
out += std::string(" coin ") + WHITE_BOLD_S + m_coin.name() + CLEAR;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
out += std::string(" algo ") + WHITE_BOLD_S + (m_algorithm.isValid() ? m_algorithm.shortName() : "auto") + CLEAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef APP_DEBUG
|
#ifdef APP_DEBUG
|
||||||
void xmrig::Pool::print() const
|
void xmrig::Pool::print() const
|
||||||
{
|
{
|
||||||
LOG_NOTICE("url: %s", m_url.data());
|
LOG_NOTICE("url: %s", url().data());
|
||||||
LOG_DEBUG ("host: %s", m_host.data());
|
LOG_DEBUG ("host: %s", host().data());
|
||||||
LOG_DEBUG ("port: %d", static_cast<int>(m_port));
|
LOG_DEBUG ("port: %d", static_cast<int>(port()));
|
||||||
LOG_DEBUG ("user: %s", m_user.data());
|
LOG_DEBUG ("user: %s", m_user.data());
|
||||||
LOG_DEBUG ("pass: %s", m_password.data());
|
LOG_DEBUG ("pass: %s", m_password.data());
|
||||||
LOG_DEBUG ("rig-id %s", m_rigId.data());
|
LOG_DEBUG ("rig-id %s", m_rigId.data());
|
||||||
|
@ -318,26 +276,3 @@ void xmrig::Pool::print() const
|
||||||
LOG_DEBUG ("keepAlive: %d", m_keepAlive);
|
LOG_DEBUG ("keepAlive: %d", m_keepAlive);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
bool xmrig::Pool::parseIPv6(const char *addr)
|
|
||||||
{
|
|
||||||
const char *end = strchr(addr, ']');
|
|
||||||
if (!end) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *port = strchr(end, ':');
|
|
||||||
if (!port) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t size = static_cast<size_t>(end - addr);
|
|
||||||
char *host = new char[size]();
|
|
||||||
memcpy(host, addr + 1, size - 1);
|
|
||||||
|
|
||||||
m_host = host;
|
|
||||||
m_port = static_cast<uint16_t>(strtol(port + 1, nullptr, 10));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
#include "base/tools/String.h"
|
#include "base/net/stratum/Url.h"
|
||||||
#include "crypto/common/Coin.h"
|
#include "crypto/common/Coin.h"
|
||||||
#include "rapidjson/fwd.h"
|
#include "rapidjson/fwd.h"
|
||||||
|
|
||||||
|
@ -39,15 +39,17 @@
|
||||||
namespace xmrig {
|
namespace xmrig {
|
||||||
|
|
||||||
|
|
||||||
|
class IClient;
|
||||||
|
class IClientListener;
|
||||||
|
|
||||||
|
|
||||||
class Pool
|
class Pool
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum Flags {
|
enum Mode {
|
||||||
FLAG_ENABLED,
|
MODE_POOL,
|
||||||
FLAG_NICEHASH,
|
MODE_DAEMON,
|
||||||
FLAG_TLS,
|
MODE_SELF_SELECT
|
||||||
FLAG_DAEMON,
|
|
||||||
FLAG_MAX
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const String kDefaultPassword;
|
static const String kDefaultPassword;
|
||||||
|
@ -57,7 +59,7 @@ public:
|
||||||
constexpr static uint16_t kDefaultPort = 3333;
|
constexpr static uint16_t kDefaultPort = 3333;
|
||||||
constexpr static uint64_t kDefaultPollInterval = 1000;
|
constexpr static uint64_t kDefaultPollInterval = 1000;
|
||||||
|
|
||||||
Pool();
|
Pool() = default;
|
||||||
Pool(const char *url);
|
Pool(const char *url);
|
||||||
Pool(const rapidjson::Value &object);
|
Pool(const rapidjson::Value &object);
|
||||||
Pool(const char *host,
|
Pool(const char *host,
|
||||||
|
@ -69,20 +71,21 @@ public:
|
||||||
bool tls = false
|
bool tls = false
|
||||||
);
|
);
|
||||||
|
|
||||||
inline bool isDaemon() const { return m_flags.test(FLAG_DAEMON); }
|
|
||||||
inline bool isNicehash() const { return m_flags.test(FLAG_NICEHASH); }
|
inline bool isNicehash() const { return m_flags.test(FLAG_NICEHASH); }
|
||||||
inline bool isTLS() const { return m_flags.test(FLAG_TLS); }
|
inline bool isTLS() const { return m_flags.test(FLAG_TLS); }
|
||||||
inline bool isValid() const { return !m_host.isNull() && m_port > 0; }
|
inline bool isValid() const { return m_url.isValid(); }
|
||||||
inline const Algorithm &algorithm() const { return m_algorithm; }
|
inline const Algorithm &algorithm() const { return m_algorithm; }
|
||||||
inline const Coin &coin() const { return m_coin; }
|
inline const Coin &coin() const { return m_coin; }
|
||||||
inline const String &fingerprint() const { return m_fingerprint; }
|
inline const String &fingerprint() const { return m_fingerprint; }
|
||||||
inline const String &host() const { return m_host; }
|
inline const String &host() const { return m_url.host(); }
|
||||||
inline const String &password() const { return !m_password.isNull() ? m_password : kDefaultPassword; }
|
inline const String &password() const { return !m_password.isNull() ? m_password : kDefaultPassword; }
|
||||||
inline const String &rigId() const { return m_rigId; }
|
inline const String &rigId() const { return m_rigId; }
|
||||||
inline const String &url() const { return m_url; }
|
inline const String &url() const { return m_url.url(); }
|
||||||
inline const String &user() const { return !m_user.isNull() ? m_user : kDefaultUser; }
|
inline const String &user() const { return !m_user.isNull() ? m_user : kDefaultUser; }
|
||||||
|
inline const Url &daemon() const { return m_daemon; }
|
||||||
inline int keepAlive() const { return m_keepAlive; }
|
inline int keepAlive() const { return m_keepAlive; }
|
||||||
inline uint16_t port() const { return m_port; }
|
inline Mode mode() const { return m_mode; }
|
||||||
|
inline uint16_t port() const { return m_url.port(); }
|
||||||
inline uint64_t pollInterval() const { return m_pollInterval; }
|
inline uint64_t pollInterval() const { return m_pollInterval; }
|
||||||
inline void setAlgo(const Algorithm &algorithm) { m_algorithm = algorithm; }
|
inline void setAlgo(const Algorithm &algorithm) { m_algorithm = algorithm; }
|
||||||
inline void setPassword(const String &password) { m_password = password; }
|
inline void setPassword(const String &password) { m_password = password; }
|
||||||
|
@ -94,31 +97,37 @@ public:
|
||||||
|
|
||||||
bool isEnabled() const;
|
bool isEnabled() const;
|
||||||
bool isEqual(const Pool &other) const;
|
bool isEqual(const Pool &other) const;
|
||||||
bool parse(const char *url);
|
IClient *createClient(int id, IClientListener *listener) const;
|
||||||
rapidjson::Value toJSON(rapidjson::Document &doc) const;
|
rapidjson::Value toJSON(rapidjson::Document &doc) const;
|
||||||
|
std::string printableName() const;
|
||||||
|
|
||||||
# ifdef APP_DEBUG
|
# ifdef APP_DEBUG
|
||||||
void print() const;
|
void print() const;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum Flags {
|
||||||
|
FLAG_ENABLED,
|
||||||
|
FLAG_NICEHASH,
|
||||||
|
FLAG_TLS,
|
||||||
|
FLAG_MAX
|
||||||
|
};
|
||||||
|
|
||||||
inline void setKeepAlive(bool enable) { setKeepAlive(enable ? kKeepAliveTimeout : 0); }
|
inline void setKeepAlive(bool enable) { setKeepAlive(enable ? kKeepAliveTimeout : 0); }
|
||||||
inline void setKeepAlive(int keepAlive) { m_keepAlive = keepAlive >= 0 ? keepAlive : 0; }
|
inline void setKeepAlive(int keepAlive) { m_keepAlive = keepAlive >= 0 ? keepAlive : 0; }
|
||||||
|
|
||||||
bool parseIPv6(const char *addr);
|
|
||||||
|
|
||||||
Algorithm m_algorithm;
|
Algorithm m_algorithm;
|
||||||
Coin m_coin;
|
Coin m_coin;
|
||||||
int m_keepAlive;
|
int m_keepAlive = 0;
|
||||||
std::bitset<FLAG_MAX> m_flags;
|
Mode m_mode = MODE_POOL;
|
||||||
|
std::bitset<FLAG_MAX> m_flags = 0;
|
||||||
String m_fingerprint;
|
String m_fingerprint;
|
||||||
String m_host;
|
|
||||||
String m_password;
|
String m_password;
|
||||||
String m_rigId;
|
String m_rigId;
|
||||||
String m_url;
|
|
||||||
String m_user;
|
String m_user;
|
||||||
uint16_t m_port;
|
uint64_t m_pollInterval = kDefaultPollInterval;
|
||||||
uint64_t m_pollInterval;
|
Url m_daemon;
|
||||||
|
Url m_url;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ xmrig::IStrategy *xmrig::Pools::createStrategy(IStrategyListener *listener) cons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FailoverStrategy *strategy = new FailoverStrategy(retryPause(), retries(), listener);
|
auto strategy = new FailoverStrategy(retryPause(), retries(), listener);
|
||||||
for (const Pool &pool : m_data) {
|
for (const Pool &pool : m_data) {
|
||||||
if (pool.isEnabled()) {
|
if (pool.isEnabled()) {
|
||||||
strategy->add(pool);
|
strategy->add(pool);
|
||||||
|
@ -135,13 +135,7 @@ void xmrig::Pools::print() const
|
||||||
{
|
{
|
||||||
size_t i = 1;
|
size_t i = 1;
|
||||||
for (const Pool &pool : m_data) {
|
for (const Pool &pool : m_data) {
|
||||||
Log::print(GREEN_BOLD(" * ") WHITE_BOLD("POOL #%-7zu") CSI "1;%dm%s" CLEAR " %s " WHITE_BOLD("%s"),
|
Log::print(GREEN_BOLD(" * ") WHITE_BOLD("POOL #%-7zu") "%s", i, pool.printableName().c_str());
|
||||||
i,
|
|
||||||
(pool.isEnabled() ? (pool.isTLS() ? 32 : 36) : 31),
|
|
||||||
pool.url().data(),
|
|
||||||
pool.coin().isValid() ? "coin" : "algo",
|
|
||||||
pool.coin().isValid() ? pool.coin().name() : (pool.algorithm().isValid() ? pool.algorithm().shortName() : "auto")
|
|
||||||
);
|
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
302
src/base/net/stratum/SelfSelectClient.cpp
Normal file
302
src/base/net/stratum/SelfSelectClient.cpp
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
/* XMRig
|
||||||
|
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
|
||||||
|
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
|
||||||
|
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
|
||||||
|
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
|
||||||
|
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||||
|
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||||
|
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||||
|
* Copyright 2019 jtgrassie <https://github.com/jtgrassie>
|
||||||
|
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#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) :
|
||||||
|
m_listener(listener)
|
||||||
|
{
|
||||||
|
m_client = new Client(id, agent, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
xmrig::SelfSelectClient::~SelfSelectClient()
|
||||||
|
{
|
||||||
|
delete m_client;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void xmrig::SelfSelectClient::tick(uint64_t now)
|
||||||
|
{
|
||||||
|
m_client->tick(now);
|
||||||
|
|
||||||
|
if (m_state == RetryState) {
|
||||||
|
if (Chrono::steadyMSecs() - m_timestamp < m_retryPause) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBlockTemplate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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] error: " RED_BOLD("\"%s\"") RED_S ", code: %d", pool().daemon().url().data(), 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] required field " RED_BOLD("\"%s\"") RED_S " not found", pool().daemon().url().data(), 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);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void xmrig::SelfSelectClient::getBlockTemplate()
|
||||||
|
{
|
||||||
|
setState(WaitState);
|
||||||
|
|
||||||
|
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, m_sequence++, "getblocktemplate", params);
|
||||||
|
|
||||||
|
send(HTTP_POST, "/json_rpc", doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void xmrig::SelfSelectClient::retry()
|
||||||
|
{
|
||||||
|
setState(RetryState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void xmrig::SelfSelectClient::send(int method, const char *url, const char *data, size_t size)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("[%s] " MAGENTA_BOLD("\"%s %s\"") BLACK_BOLD_S " send (%zu bytes): \"%.*s\"",
|
||||||
|
pool().daemon().url().data(),
|
||||||
|
http_method_str(static_cast<http_method>(method)),
|
||||||
|
url,
|
||||||
|
size,
|
||||||
|
static_cast<int>(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(isQuiet());
|
||||||
|
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<StringBuffer> writer(buffer);
|
||||||
|
doc.Accept(writer);
|
||||||
|
|
||||||
|
send(method, url, buffer.GetString(), buffer.GetSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void xmrig::SelfSelectClient::setState(State state)
|
||||||
|
{
|
||||||
|
if (m_state == state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case IdleState:
|
||||||
|
m_timestamp = 0;
|
||||||
|
m_failures = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WaitState:
|
||||||
|
m_timestamp = Chrono::steadyMSecs();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RetryState:
|
||||||
|
m_timestamp = Chrono::steadyMSecs();
|
||||||
|
|
||||||
|
if (m_failures > m_retries) {
|
||||||
|
m_listener->onClose(this, static_cast<int>(m_failures));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_failures++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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, [this](const rapidjson::Value &result, bool success, uint64_t elapsed) {
|
||||||
|
if (!success) {
|
||||||
|
if (!isQuiet()) {
|
||||||
|
LOG_ERR("[%s] error: " RED_BOLD("\"%s\"") RED_S ", code: %d", pool().daemon().url().data(), Json::getString(result, "message"), Json::getInt(result, "code"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return retry();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_failures > m_retries) {
|
||||||
|
m_listener->onLoginSuccess(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(IdleState);
|
||||||
|
m_listener->onJobReceived(this, m_job, rapidjson::Value{});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void xmrig::SelfSelectClient::onHttpData(const HttpData &data)
|
||||||
|
{
|
||||||
|
if (data.status != HTTP_STATUS_OK) {
|
||||||
|
return retry();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("[%s] received (%d bytes): \"%.*s\"", pool().daemon().url().data(), static_cast<int>(data.body.size()), static_cast<int>(data.body.size()), data.body.c_str());
|
||||||
|
|
||||||
|
rapidjson::Document doc;
|
||||||
|
if (doc.Parse(data.body.c_str()).HasParseError()) {
|
||||||
|
if (!isQuiet()) {
|
||||||
|
LOG_ERR("[%s] JSON decode failed: \"%s\"", pool().daemon().url().data(), rapidjson::GetParseError_En(doc.GetParseError()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return retry();
|
||||||
|
}
|
||||||
|
|
||||||
|
const int64_t id = Json::getInt64(doc, "id", -1);
|
||||||
|
if (id > 0 && m_sequence - id != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parseResponse(id, doc["result"], Json::getObject(doc, "error"))) {
|
||||||
|
retry();
|
||||||
|
}
|
||||||
|
}
|
123
src/base/net/stratum/SelfSelectClient.h
Normal file
123
src/base/net/stratum/SelfSelectClient.h
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
/* XMRig
|
||||||
|
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
|
||||||
|
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
|
||||||
|
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
|
||||||
|
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
|
||||||
|
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||||
|
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||||
|
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||||
|
* Copyright 2019 jtgrassie <https://github.com/jtgrassie>
|
||||||
|
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XMRIG_SELFSELECTCLIENT_H
|
||||||
|
#define XMRIG_SELFSELECTCLIENT_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, public IHttpListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
XMRIG_DISABLE_COPY_MOVE_DEFAULT(SelfSelectClient)
|
||||||
|
|
||||||
|
SelfSelectClient(int id, const char *agent, IClientListener *listener);
|
||||||
|
~SelfSelectClient() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// IClient
|
||||||
|
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, 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 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); m_retries = retries; }
|
||||||
|
inline void setRetryPause(uint64_t ms) override { m_client->setRetryPause(ms); m_retryPause = ms; }
|
||||||
|
|
||||||
|
void tick(uint64_t now) override;
|
||||||
|
|
||||||
|
// IClientListener
|
||||||
|
inline void onClose(IClient *, int failures) override { m_listener->onClose(this, failures); setState(IdleState); m_active = false; }
|
||||||
|
inline void onLoginSuccess(IClient *) override { m_listener->onLoginSuccess(this); setState(IdleState); m_active = true; }
|
||||||
|
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:
|
||||||
|
enum State {
|
||||||
|
IdleState,
|
||||||
|
WaitState,
|
||||||
|
RetryState
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool isQuiet() const { return m_quiet || m_failures >= m_retries; }
|
||||||
|
|
||||||
|
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 setState(State state);
|
||||||
|
void submitBlockTemplate(rapidjson::Value &result);
|
||||||
|
|
||||||
|
bool m_active = false;
|
||||||
|
bool m_quiet = false;
|
||||||
|
IClient *m_client;
|
||||||
|
IClientListener *m_listener;
|
||||||
|
int m_retries = 5;
|
||||||
|
int64_t m_failures = 0;
|
||||||
|
int64_t m_sequence = 1;
|
||||||
|
Job m_job;
|
||||||
|
State m_state = IdleState;
|
||||||
|
uint64_t m_retryPause = 5000;
|
||||||
|
uint64_t m_timestamp = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} /* namespace xmrig */
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* XMRIG_SELFSELECTCLIENT_H */
|
|
@ -35,34 +35,26 @@ namespace xmrig {
|
||||||
class SubmitResult
|
class SubmitResult
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline SubmitResult() :
|
SubmitResult() = default;
|
||||||
reqId(0),
|
|
||||||
seq(0),
|
|
||||||
actualDiff(0),
|
|
||||||
diff(0),
|
|
||||||
elapsed(0),
|
|
||||||
m_start(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
inline SubmitResult(int64_t seq, uint64_t diff, uint64_t actualDiff, int64_t reqId = 0) :
|
inline SubmitResult(int64_t seq, uint64_t diff, uint64_t actualDiff, int64_t reqId = 0) :
|
||||||
reqId(reqId),
|
reqId(reqId),
|
||||||
seq(seq),
|
seq(seq),
|
||||||
actualDiff(actualDiff),
|
actualDiff(actualDiff),
|
||||||
diff(diff),
|
diff(diff),
|
||||||
elapsed(0),
|
|
||||||
m_start(Chrono::steadyMSecs())
|
m_start(Chrono::steadyMSecs())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
inline void done() { elapsed = Chrono::steadyMSecs() - m_start; }
|
inline void done() { elapsed = Chrono::steadyMSecs() - m_start; }
|
||||||
|
|
||||||
int64_t reqId;
|
int64_t reqId = 0;
|
||||||
int64_t seq;
|
int64_t seq = 0;
|
||||||
uint64_t actualDiff;
|
uint64_t actualDiff = 0;
|
||||||
uint64_t diff;
|
uint64_t diff = 0;
|
||||||
uint64_t elapsed;
|
uint64_t elapsed = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t m_start;
|
uint64_t m_start = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
163
src/base/net/stratum/Url.cpp
Normal file
163
src/base/net/stratum/Url.cpp
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
/* XMRig
|
||||||
|
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
|
||||||
|
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
|
||||||
|
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
|
||||||
|
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
|
||||||
|
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||||
|
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||||
|
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||||
|
* Copyright 2019 Howard Chu <https://github.com/hyc>
|
||||||
|
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "base/net/stratum/Url.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# define strncasecmp _strnicmp
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace xmrig {
|
||||||
|
|
||||||
|
static const char kStratumTcp[] = "stratum+tcp://";
|
||||||
|
static const char kStratumSsl[] = "stratum+ssl://";
|
||||||
|
|
||||||
|
#ifdef XMRIG_FEATURE_HTTP
|
||||||
|
static const char kDaemonHttp[] = "daemon+http://";
|
||||||
|
static const char kDaemonHttps[] = "daemon+https://";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
xmrig::Url::Url(const char *url)
|
||||||
|
{
|
||||||
|
parse(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
xmrig::Url::Url(const char *host, uint16_t port, bool tls, Scheme scheme) :
|
||||||
|
m_tls(tls),
|
||||||
|
m_scheme(scheme),
|
||||||
|
m_host(host),
|
||||||
|
m_port(port)
|
||||||
|
{
|
||||||
|
const size_t size = m_host.size() + 8;
|
||||||
|
assert(size > 8);
|
||||||
|
|
||||||
|
char *url = new char[size]();
|
||||||
|
snprintf(url, size - 1, "%s:%d", m_host.data(), m_port);
|
||||||
|
|
||||||
|
m_url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool xmrig::Url::isEqual(const Url &other) const
|
||||||
|
{
|
||||||
|
return (m_tls == other.m_tls && m_scheme == other.m_scheme && m_host == other.m_host && m_url == other.m_url && m_port == other.m_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool xmrig::Url::parse(const char *url)
|
||||||
|
{
|
||||||
|
if (url == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *p = strstr(url, "://");
|
||||||
|
const char *base = url;
|
||||||
|
|
||||||
|
if (p) {
|
||||||
|
if (strncasecmp(url, kStratumTcp, sizeof(kStratumTcp) - 1) == 0) {
|
||||||
|
m_scheme = STRATUM;
|
||||||
|
m_tls = false;
|
||||||
|
}
|
||||||
|
else if (strncasecmp(url, kStratumSsl, sizeof(kStratumSsl) - 1) == 0) {
|
||||||
|
m_scheme = STRATUM;
|
||||||
|
m_tls = true;
|
||||||
|
}
|
||||||
|
# ifdef XMRIG_FEATURE_HTTP
|
||||||
|
else if (strncasecmp(url, kDaemonHttps, sizeof(kDaemonHttps) - 1) == 0) {
|
||||||
|
m_scheme = DAEMON;
|
||||||
|
m_tls = true;
|
||||||
|
}
|
||||||
|
else if (strncasecmp(url, kDaemonHttp, sizeof(kDaemonHttp) - 1) == 0) {
|
||||||
|
m_scheme = DAEMON;
|
||||||
|
m_tls = false;
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
base = p + 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strlen(base) || *base == '/') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_url = url;
|
||||||
|
if (base[0] == '[') {
|
||||||
|
return parseIPv6(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *port = strchr(base, ':');
|
||||||
|
if (!port) {
|
||||||
|
m_host = base;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto size = static_cast<size_t>(port++ - base + 1);
|
||||||
|
char *host = new char[size]();
|
||||||
|
memcpy(host, base, size - 1);
|
||||||
|
|
||||||
|
m_host = host;
|
||||||
|
m_port = static_cast<uint16_t>(strtol(port, nullptr, 10));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool xmrig::Url::parseIPv6(const char *addr)
|
||||||
|
{
|
||||||
|
const char *end = strchr(addr, ']');
|
||||||
|
if (!end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *port = strchr(end, ':');
|
||||||
|
if (!port) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto size = static_cast<size_t>(end - addr);
|
||||||
|
char *host = new char[size]();
|
||||||
|
memcpy(host, addr + 1, size - 1);
|
||||||
|
|
||||||
|
m_host = host;
|
||||||
|
m_port = static_cast<uint16_t>(strtol(port + 1, nullptr, 10));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
77
src/base/net/stratum/Url.h
Normal file
77
src/base/net/stratum/Url.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/* XMRig
|
||||||
|
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
|
||||||
|
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
|
||||||
|
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
|
||||||
|
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
|
||||||
|
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||||
|
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||||
|
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||||
|
* Copyright 2019 Howard Chu <https://github.com/hyc>
|
||||||
|
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XMRIG_URL_H
|
||||||
|
#define XMRIG_URL_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "base/tools/String.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace xmrig {
|
||||||
|
|
||||||
|
|
||||||
|
class Url
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Scheme {
|
||||||
|
UNSPECIFIED,
|
||||||
|
STRATUM,
|
||||||
|
DAEMON
|
||||||
|
};
|
||||||
|
|
||||||
|
Url() = default;
|
||||||
|
Url(const char *url);
|
||||||
|
Url(const char *host, uint16_t port, bool tls = false, Scheme scheme = UNSPECIFIED);
|
||||||
|
|
||||||
|
inline bool isTLS() const { return m_tls; }
|
||||||
|
inline bool isValid() const { return !m_host.isNull() && m_port > 0; }
|
||||||
|
inline const String &host() const { return m_host; }
|
||||||
|
inline const String &url() const { return m_url; }
|
||||||
|
inline Scheme scheme() const { return m_scheme; }
|
||||||
|
inline uint16_t port() const { return m_port; }
|
||||||
|
|
||||||
|
inline bool operator!=(const Url &other) const { return !isEqual(other); }
|
||||||
|
inline bool operator==(const Url &other) const { return isEqual(other); }
|
||||||
|
|
||||||
|
bool isEqual(const Url &other) const;
|
||||||
|
rapidjson::Value toJSON(rapidjson::Document &doc) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool parse(const char *url);
|
||||||
|
bool parseIPv6(const char *addr);
|
||||||
|
|
||||||
|
bool m_tls = false;
|
||||||
|
Scheme m_scheme = UNSPECIFIED;
|
||||||
|
String m_host;
|
||||||
|
String m_url;
|
||||||
|
uint16_t m_port = 3333;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} /* namespace xmrig */
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* XMRIG_URL_H */
|
|
@ -23,15 +23,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "base/net/stratum/strategies/FailoverStrategy.h"
|
||||||
|
#include "base/kernel/interfaces/IClient.h"
|
||||||
#include "base/kernel/interfaces/IStrategyListener.h"
|
#include "base/kernel/interfaces/IStrategyListener.h"
|
||||||
#include "base/kernel/Platform.h"
|
#include "base/kernel/Platform.h"
|
||||||
#include "base/net/stratum/Client.h"
|
|
||||||
#include "base/net/stratum/strategies/FailoverStrategy.h"
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef XMRIG_FEATURE_HTTP
|
|
||||||
# include "base/net/stratum/DaemonClient.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
xmrig::FailoverStrategy::FailoverStrategy(const std::vector<Pool> &pools, int retryPause, int retries, IStrategyListener *listener, bool quiet) :
|
xmrig::FailoverStrategy::FailoverStrategy(const std::vector<Pool> &pools, int retryPause, int retries, IStrategyListener *listener, bool quiet) :
|
||||||
|
@ -69,16 +64,8 @@ xmrig::FailoverStrategy::~FailoverStrategy()
|
||||||
|
|
||||||
void xmrig::FailoverStrategy::add(const Pool &pool)
|
void xmrig::FailoverStrategy::add(const Pool &pool)
|
||||||
{
|
{
|
||||||
const int id = static_cast<int>(m_pools.size());
|
IClient *client = pool.createClient(static_cast<int>(m_pools.size()), this);
|
||||||
|
|
||||||
# ifdef XMRIG_FEATURE_HTTP
|
|
||||||
IClient *client = !pool.isDaemon() ? static_cast<IClient *>(new Client(id, Platform::userAgent(), this))
|
|
||||||
: static_cast<IClient *>(new DaemonClient(id, this));
|
|
||||||
# else
|
|
||||||
IClient *client = new Client(id, Platform::userAgent(), this);
|
|
||||||
# endif
|
|
||||||
|
|
||||||
client->setPool(pool);
|
|
||||||
client->setRetries(m_retries);
|
client->setRetries(m_retries);
|
||||||
client->setRetryPause(m_retryPause * 1000);
|
client->setRetryPause(m_retryPause * 1000);
|
||||||
client->setQuiet(m_quiet);
|
client->setQuiet(m_quiet);
|
||||||
|
@ -123,8 +110,8 @@ void xmrig::FailoverStrategy::setAlgo(const Algorithm &algo)
|
||||||
|
|
||||||
void xmrig::FailoverStrategy::stop()
|
void xmrig::FailoverStrategy::stop()
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < m_pools.size(); ++i) {
|
for (auto &pool : m_pools) {
|
||||||
m_pools[i]->disconnect();
|
pool->disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_index = 0;
|
m_index = 0;
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "base/kernel/interfaces/IClientListener.h"
|
#include "base/kernel/interfaces/IClientListener.h"
|
||||||
#include "base/kernel/interfaces/IStrategy.h"
|
#include "base/kernel/interfaces/IStrategy.h"
|
||||||
#include "base/net/stratum/Pool.h"
|
#include "base/net/stratum/Pool.h"
|
||||||
|
#include "base/tools/Object.h"
|
||||||
|
|
||||||
|
|
||||||
namespace xmrig {
|
namespace xmrig {
|
||||||
|
@ -44,6 +45,8 @@ class IStrategyListener;
|
||||||
class FailoverStrategy : public IStrategy, public IClientListener
|
class FailoverStrategy : public IStrategy, public IClientListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
XMRIG_DISABLE_COPY_MOVE_DEFAULT(FailoverStrategy)
|
||||||
|
|
||||||
FailoverStrategy(const std::vector<Pool> &pool, int retryPause, int retries, IStrategyListener *listener, bool quiet = false);
|
FailoverStrategy(const std::vector<Pool> &pool, int retryPause, int retries, IStrategyListener *listener, bool quiet = false);
|
||||||
FailoverStrategy(int retryPause, int retries, IStrategyListener *listener, bool quiet = false);
|
FailoverStrategy(int retryPause, int retries, IStrategyListener *listener, bool quiet = false);
|
||||||
~FailoverStrategy() override;
|
~FailoverStrategy() override;
|
||||||
|
|
|
@ -23,33 +23,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "base/net/stratum/strategies/SinglePoolStrategy.h"
|
||||||
|
#include "base/kernel/interfaces/IClient.h"
|
||||||
#include "base/kernel/interfaces/IStrategyListener.h"
|
#include "base/kernel/interfaces/IStrategyListener.h"
|
||||||
#include "base/kernel/Platform.h"
|
#include "base/kernel/Platform.h"
|
||||||
#include "base/net/stratum/Client.h"
|
#include "base/net/stratum/Pool.h"
|
||||||
#include "base/net/stratum/strategies/SinglePoolStrategy.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) :
|
xmrig::SinglePoolStrategy::SinglePoolStrategy(const Pool &pool, int retryPause, int retries, IStrategyListener *listener, bool quiet) :
|
||||||
m_active(false),
|
m_active(false),
|
||||||
m_listener(listener)
|
m_listener(listener)
|
||||||
{
|
{
|
||||||
# ifdef XMRIG_FEATURE_HTTP
|
m_client = pool.createClient(0, this);
|
||||||
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->setRetries(retries);
|
||||||
m_client->setRetryPause(retryPause * 1000);
|
m_client->setRetryPause(retryPause * 1000);
|
||||||
m_client->setQuiet(quiet);
|
m_client->setQuiet(quiet);
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
#include "base/kernel/interfaces/IClientListener.h"
|
#include "base/kernel/interfaces/IClientListener.h"
|
||||||
#include "base/kernel/interfaces/IStrategy.h"
|
#include "base/kernel/interfaces/IStrategy.h"
|
||||||
|
#include "base/tools/Object.h"
|
||||||
|
|
||||||
|
|
||||||
namespace xmrig {
|
namespace xmrig {
|
||||||
|
@ -41,6 +42,8 @@ class Pool;
|
||||||
class SinglePoolStrategy : public IStrategy, public IClientListener
|
class SinglePoolStrategy : public IStrategy, public IClientListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
XMRIG_DISABLE_COPY_MOVE_DEFAULT(SinglePoolStrategy)
|
||||||
|
|
||||||
SinglePoolStrategy(const Pool &pool, int retryPause, int retries, IStrategyListener *listener, bool quiet = false);
|
SinglePoolStrategy(const Pool &pool, int retryPause, int retries, IStrategyListener *listener, bool quiet = false);
|
||||||
~SinglePoolStrategy() override;
|
~SinglePoolStrategy() override;
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ static const option options[] = {
|
||||||
{ "http-no-restricted", 0, nullptr, IConfig::HttpRestrictedKey },
|
{ "http-no-restricted", 0, nullptr, IConfig::HttpRestrictedKey },
|
||||||
{ "daemon", 0, nullptr, IConfig::DaemonKey },
|
{ "daemon", 0, nullptr, IConfig::DaemonKey },
|
||||||
{ "daemon-poll-interval", 1, nullptr, IConfig::DaemonPollKey },
|
{ "daemon-poll-interval", 1, nullptr, IConfig::DaemonPollKey },
|
||||||
|
{ "self-select", 1, nullptr, IConfig::SelfSelectKey },
|
||||||
# endif
|
# endif
|
||||||
{ "av", 1, nullptr, IConfig::AVKey },
|
{ "av", 1, nullptr, IConfig::AVKey },
|
||||||
{ "background", 0, nullptr, IConfig::BackgroundKey },
|
{ "background", 0, nullptr, IConfig::BackgroundKey },
|
||||||
|
|
|
@ -62,6 +62,7 @@ static inline const std::string &usage()
|
||||||
# ifdef XMRIG_FEATURE_HTTP
|
# ifdef XMRIG_FEATURE_HTTP
|
||||||
u += " --daemon use daemon RPC instead of pool for solo mining\n";
|
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 += " --daemon-poll-interval=N daemon poll interval in milliseconds (default: 1000)\n";
|
||||||
|
u += " --self-select=URL self-select block templates from URL\n";
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
u += " -r, --retries=N number of times to retry before switch to backup server (default: 5)\n";
|
u += " -r, --retries=N number of times to retry before switch to backup server (default: 5)\n";
|
||||||
|
|
|
@ -234,7 +234,7 @@ void xmrig::DonateStrategy::onTimer(const Timer *)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
xmrig::Client *xmrig::DonateStrategy::createProxy()
|
xmrig::IClient *xmrig::DonateStrategy::createProxy()
|
||||||
{
|
{
|
||||||
if (m_controller->config()->pools().proxyDonate() == Pools::PROXY_DONATE_NONE) {
|
if (m_controller->config()->pools().proxyDonate() == Pools::PROXY_DONATE_NONE) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -251,7 +251,7 @@ xmrig::Client *xmrig::DonateStrategy::createProxy()
|
||||||
Pool pool(client->ip(), client->pool().port(), m_userId, client->pool().password(), 0, true, client->isTLS());
|
Pool pool(client->ip(), client->pool().port(), m_userId, client->pool().password(), 0, true, client->isTLS());
|
||||||
pool.setAlgo(client->pool().algorithm());
|
pool.setAlgo(client->pool().algorithm());
|
||||||
|
|
||||||
auto proxy = new Client(-1, Platform::userAgent(), this);
|
IClient *proxy = new Client(-1, Platform::userAgent(), this);
|
||||||
proxy->setPool(pool);
|
proxy->setPool(pool);
|
||||||
proxy->setQuiet(true);
|
proxy->setQuiet(true);
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ private:
|
||||||
|
|
||||||
inline State state() const { return m_state; }
|
inline State state() const { return m_state; }
|
||||||
|
|
||||||
Client *createProxy();
|
IClient *createProxy();
|
||||||
void idle(double min, double max);
|
void idle(double min, double max);
|
||||||
void setAlgorithms(rapidjson::Document &doc, rapidjson::Value ¶ms);
|
void setAlgorithms(rapidjson::Document &doc, rapidjson::Value ¶ms);
|
||||||
void setJob(IClient *client, const Job &job);
|
void setJob(IClient *client, const Job &job);
|
||||||
|
|
Loading…
Reference in a new issue