implement stratum mode self-select
This commit is contained in:
parent
5439f2d7b8
commit
40c69d2019
12 changed files with 234 additions and 18 deletions
|
@ -165,6 +165,9 @@ void xmrig::BaseTransform::transform(rapidjson::Document &doc, int key, const ch
|
|||
case IConfig::FingerprintKey: /* --tls-fingerprint */
|
||||
return add(doc, kPools, "tls-fingerprint", arg);
|
||||
|
||||
case IConfig::SelfSelectKey: /* --self-select */
|
||||
return add(doc, kPools, "self-select", arg);
|
||||
|
||||
case IConfig::LogFileKey: /* --log-file */
|
||||
return set(doc, "log-file", arg);
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ public:
|
|||
ProxyDonateKey = 1017,
|
||||
DaemonKey = 1018,
|
||||
DaemonPollKey = 1019,
|
||||
SelfSelectKey = 1024,
|
||||
|
||||
// xmrig common
|
||||
CPUPriorityKey = 1021,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
* Copyright 2019 jtgrassie <https://github.com/jtgrassie>
|
||||
*
|
||||
* 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
|
||||
|
@ -37,12 +38,15 @@
|
|||
#endif
|
||||
|
||||
|
||||
#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/kernel/interfaces/IClientListener.h"
|
||||
#include "base/net/dns/Dns.h"
|
||||
#include "base/net/stratum/Client.h"
|
||||
#include "base/net/http/HttpClient.h"
|
||||
#include "base/net/http/HttpsClient.h"
|
||||
#include "base/tools/Buffer.h"
|
||||
#include "base/tools/Chrono.h"
|
||||
#include "net/JobResult.h"
|
||||
|
@ -264,6 +268,60 @@ void xmrig::Client::onResolved(const Dns &dns, int status)
|
|||
}
|
||||
|
||||
|
||||
void xmrig::Client::onHttpData(const HttpData &data)
|
||||
{
|
||||
using namespace rapidjson;
|
||||
LOG_DEBUG("[%s:%d] received (%d bytes): \"%.*s\"", m_pool.host().data(), m_pool.port(), static_cast<int>(data.body.size()), static_cast<int>(data.body.size()), data.body.c_str());
|
||||
if (m_job.blob()[0]) {
|
||||
LOG_ERR("Job already has a blob");
|
||||
return;
|
||||
}
|
||||
Document doc;
|
||||
if (doc.Parse(data.body.c_str()).HasParseError()) {
|
||||
LOG_ERR("[getblocktemplate] Failed to parse daemon response");
|
||||
return;
|
||||
}
|
||||
if (!doc.HasMember("result")) {
|
||||
LOG_ERR("[getblocktemplate] No result");
|
||||
return;
|
||||
}
|
||||
Value &result = doc["result"];
|
||||
if (!result.HasMember("blocktemplate_blob")) {
|
||||
LOG_ERR("[getblocktemplate] No blocktemplate_blob");
|
||||
return;
|
||||
}
|
||||
if (!result.HasMember("blockhashing_blob")) {
|
||||
LOG_ERR("[getblocktemplate] No blockhashing_blob");
|
||||
return;
|
||||
}
|
||||
if (!result.HasMember("height")) {
|
||||
LOG_ERR("[getblocktemplate] No height");
|
||||
return;
|
||||
}
|
||||
if (!result.HasMember("difficulty")) {
|
||||
LOG_ERR("[getblocktemplate] No difficulty");
|
||||
return;
|
||||
}
|
||||
if (!result.HasMember("prev_hash")) {
|
||||
LOG_ERR("[getblocktemplate] No prev_hash");
|
||||
return;
|
||||
}
|
||||
m_job.setHeight(Json::getUint64(result, "height"));
|
||||
Document docBt(kObjectType);
|
||||
auto &allocator = docBt.GetAllocator();
|
||||
Value params(kObjectType);
|
||||
params.AddMember("id", m_job.clientId().toJSON(), allocator);
|
||||
params.AddMember("job_id", m_job.id().toJSON(), allocator);
|
||||
params.AddMember("blob", result["blocktemplate_blob"], allocator);
|
||||
params.AddMember("height", m_job.height(), allocator);
|
||||
params.AddMember("difficulty", result["difficulty"], allocator);
|
||||
params.AddMember("prev_hash", result["prev_hash"], allocator);
|
||||
JsonRequest::create(docBt, 2, "block_template", params);
|
||||
send(docBt);
|
||||
m_job.setBlob(result["blockhashing_blob"].GetString());
|
||||
m_listener->onJobReceived(this, m_job, Value{});
|
||||
}
|
||||
|
||||
bool xmrig::Client::close()
|
||||
{
|
||||
if (m_state == ClosingState) {
|
||||
|
@ -320,7 +378,11 @@ bool xmrig::Client::parseJob(const rapidjson::Value ¶ms, int *code)
|
|||
return false;
|
||||
}
|
||||
|
||||
# ifdef XMRIG_FEATURE_HTTP
|
||||
if (m_pool.selfSelect().isNull() && !job.setBlob(params["blob"].GetString())) {
|
||||
# else
|
||||
if (!job.setBlob(params["blob"].GetString())) {
|
||||
# endif
|
||||
*code = 4;
|
||||
return false;
|
||||
}
|
||||
|
@ -353,6 +415,18 @@ bool xmrig::Client::parseJob(const rapidjson::Value ¶ms, int *code)
|
|||
|
||||
m_job.setClientId(m_rpcId);
|
||||
|
||||
# ifdef XMRIG_FEATURE_HTTP
|
||||
if (!m_pool.selfSelect().isNull() && m_job != job)
|
||||
{
|
||||
job.setPoolWallet(Json::getString(params, "pool_wallet"));
|
||||
job.setExtraNonce(Json::getString(params, "extra_nonce"));
|
||||
m_jobs++;
|
||||
m_job = std::move(job);
|
||||
getBlockTemplate();
|
||||
return true;
|
||||
}
|
||||
# endif
|
||||
|
||||
if (m_job != job) {
|
||||
m_jobs++;
|
||||
m_job = std::move(job);
|
||||
|
@ -469,7 +543,11 @@ int64_t xmrig::Client::send(const rapidjson::Document &doc)
|
|||
{
|
||||
using namespace rapidjson;
|
||||
|
||||
# ifdef XMRIG_FEATURE_HTTP
|
||||
StringBuffer buffer(nullptr, 8192);
|
||||
# else
|
||||
StringBuffer buffer(nullptr, 512);
|
||||
# endif
|
||||
Writer<StringBuffer> writer(buffer);
|
||||
doc.Accept(writer);
|
||||
|
||||
|
@ -479,7 +557,6 @@ int64_t xmrig::Client::send(const rapidjson::Document &doc)
|
|||
close();
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(m_sendBuf, buffer.GetString(), size);
|
||||
m_sendBuf[size] = '\n';
|
||||
m_sendBuf[size + 1] = '\0';
|
||||
|
@ -575,6 +652,14 @@ void xmrig::Client::login()
|
|||
params.AddMember("rigid", m_pool.rigId().toJSON(), allocator);
|
||||
}
|
||||
|
||||
if (!m_pool.selfSelect().isNull()) {
|
||||
# ifndef XMRIG_FEATURE_HTTP
|
||||
LOG_ERR("Cannot use mode self-select as not compiled with XMRIG_FEATURE_HTTP");
|
||||
# else
|
||||
params.AddMember("mode", "self-select", allocator);
|
||||
# endif
|
||||
}
|
||||
|
||||
m_listener->onLogin(this, doc, params);
|
||||
|
||||
JsonRequest::create(doc, 1, "login", params);
|
||||
|
@ -696,7 +781,9 @@ void xmrig::Client::parseNotification(const char *method, const rapidjson::Value
|
|||
if (strcmp(method, "job") == 0) {
|
||||
int code = -1;
|
||||
if (parseJob(params, &code)) {
|
||||
m_listener->onJobReceived(this, m_job, params);
|
||||
if (m_job.blob()[0]) {
|
||||
m_listener->onJobReceived(this, m_job, params);
|
||||
}
|
||||
}
|
||||
else {
|
||||
close();
|
||||
|
@ -742,7 +829,9 @@ void xmrig::Client::parseResponse(int64_t id, const rapidjson::Value &result, co
|
|||
|
||||
m_failures = 0;
|
||||
m_listener->onLoginSuccess(this);
|
||||
m_listener->onJobReceived(this, m_job, result["job"]);
|
||||
if (m_job.blob()[0]) {
|
||||
m_listener->onJobReceived(this, m_job, result["job"]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -859,6 +948,84 @@ void xmrig::Client::startTimeout()
|
|||
}
|
||||
|
||||
|
||||
void xmrig::Client::send(int method, const char *url, const char *data, size_t size)
|
||||
{
|
||||
# ifndef XMRIG_FEATURE_HTTP
|
||||
LOG_ERR("Not enabled without XMRIG_FEATURE_HTTP");
|
||||
return;
|
||||
# else
|
||||
LOG_DEBUG("[%s] " MAGENTA_BOLD("\"%s %s\"") BLACK_BOLD_S " send (%zu bytes): \"%.*s\"",
|
||||
m_pool.selfSelect().data(),
|
||||
http_method_str(static_cast<http_method>(method)),
|
||||
url,
|
||||
size,
|
||||
static_cast<int>(size),
|
||||
data);
|
||||
|
||||
if (daemonHost.isNull())
|
||||
{
|
||||
const char *hp = m_pool.selfSelect().data();
|
||||
char host[255] = {0};
|
||||
strncpy(host, hp, 255);
|
||||
char *p = strrchr(host, ':');
|
||||
if (!p)
|
||||
{
|
||||
LOG_ERR("Cannot get host/port");
|
||||
return;
|
||||
}
|
||||
*p = 0;
|
||||
int port = atoi(++p);
|
||||
daemonHost = (const char*)host;
|
||||
daemonPort = port;
|
||||
}
|
||||
|
||||
|
||||
HttpClient *client;
|
||||
# ifdef XMRIG_FEATURE_TLS
|
||||
if (strstr(daemonHost.data(), "https:")) {
|
||||
client = new HttpsClient(method, url, this, data, size, m_pool.fingerprint());
|
||||
}
|
||||
else
|
||||
# endif
|
||||
{
|
||||
client = new HttpClient(method, url, this, data, size);
|
||||
}
|
||||
|
||||
client->setQuiet(isQuiet());
|
||||
client->connect(daemonHost, daemonPort);
|
||||
# endif
|
||||
}
|
||||
|
||||
|
||||
void xmrig::Client::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());
|
||||
}
|
||||
|
||||
|
||||
int64_t xmrig::Client::getBlockTemplate()
|
||||
{
|
||||
using namespace rapidjson;
|
||||
Document doc(kObjectType);
|
||||
auto &allocator = doc.GetAllocator();
|
||||
|
||||
Value params(kObjectType);
|
||||
params.AddMember("wallet_address", m_job.poolWallet().toJSON(), allocator);
|
||||
params.AddMember("extra_nonce", m_job.extraNonce().toJSON(), allocator);
|
||||
|
||||
JsonRequest::create(doc, m_sequence, "getblocktemplate", params);
|
||||
|
||||
send(HTTP_POST, "/json_rpc", doc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xmrig::Client::onAllocBuffer(uv_handle_t *handle, size_t, uv_buf_t *buf)
|
||||
{
|
||||
auto client = getClient(handle->data);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
* Copyright 2019 jtgrassie <https://github.com/jtgrassie>
|
||||
*
|
||||
* 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
|
||||
|
@ -34,6 +35,7 @@
|
|||
|
||||
#include "base/kernel/interfaces/IDnsListener.h"
|
||||
#include "base/kernel/interfaces/ILineListener.h"
|
||||
#include "base/kernel/interfaces/IHttpListener.h"
|
||||
#include "base/net/stratum/BaseClient.h"
|
||||
#include "base/net/stratum/Job.h"
|
||||
#include "base/net/stratum/Pool.h"
|
||||
|
@ -53,7 +55,7 @@ class IClientListener;
|
|||
class JobResult;
|
||||
|
||||
|
||||
class Client : public BaseClient, public IDnsListener, public ILineListener
|
||||
class Client : public BaseClient, public IDnsListener, public ILineListener, public IHttpListener
|
||||
{
|
||||
public:
|
||||
constexpr static uint64_t kConnectTimeout = 20 * 1000;
|
||||
|
@ -84,6 +86,7 @@ protected:
|
|||
inline bool hasExtension(Extension extension) const noexcept override { return m_extensions.test(extension); }
|
||||
inline const char *mode() const override { return "pool"; }
|
||||
inline void onLine(char *line, size_t size) override { parse(line, size); }
|
||||
void onHttpData(const HttpData &data) override;
|
||||
|
||||
private:
|
||||
class Tls;
|
||||
|
@ -110,6 +113,9 @@ private:
|
|||
void reconnect();
|
||||
void setState(SocketState state);
|
||||
void startTimeout();
|
||||
void send(int method, const char *url, const char *data, size_t size);
|
||||
void send(int method, const char *url, const rapidjson::Document &doc);
|
||||
int64_t getBlockTemplate();
|
||||
|
||||
inline const char *url() const { return m_pool.url(); }
|
||||
inline SocketState state() const { return m_state; }
|
||||
|
@ -123,7 +129,11 @@ private:
|
|||
|
||||
static inline Client *getClient(void *data) { return m_storage.get(data); }
|
||||
|
||||
char m_sendBuf[2048] = { 0 };
|
||||
# ifdef XMRIG_FEATURE_HTTP
|
||||
char m_sendBuf[8192] = {0};
|
||||
# else
|
||||
char m_sendBuf[2048] = {0};
|
||||
# endif
|
||||
const char *m_agent;
|
||||
Dns *m_dns;
|
||||
RecvBuf<kInputBufferSize> m_recvBuf;
|
||||
|
@ -136,6 +146,8 @@ private:
|
|||
uintptr_t m_key = 0;
|
||||
uv_stream_t *m_stream = nullptr;
|
||||
uv_tcp_t *m_socket = nullptr;
|
||||
String daemonHost;
|
||||
int daemonPort;
|
||||
|
||||
static Storage<Client> m_storage;
|
||||
};
|
||||
|
|
|
@ -162,17 +162,31 @@ void xmrig::Job::setDiff(uint64_t diff)
|
|||
}
|
||||
|
||||
|
||||
void xmrig::Job::setPoolWallet(const String &poolWallet)
|
||||
{
|
||||
m_poolWallet = poolWallet;
|
||||
}
|
||||
|
||||
|
||||
void xmrig::Job::setExtraNonce(const String &extraNonce)
|
||||
{
|
||||
m_extraNonce = extraNonce;
|
||||
}
|
||||
|
||||
|
||||
void xmrig::Job::copy(const Job &other)
|
||||
{
|
||||
m_algorithm = other.m_algorithm;
|
||||
m_nicehash = other.m_nicehash;
|
||||
m_size = other.m_size;
|
||||
m_clientId = other.m_clientId;
|
||||
m_id = other.m_id;
|
||||
m_diff = other.m_diff;
|
||||
m_height = other.m_height;
|
||||
m_target = other.m_target;
|
||||
m_index = other.m_index;
|
||||
m_algorithm = other.m_algorithm;
|
||||
m_nicehash = other.m_nicehash;
|
||||
m_size = other.m_size;
|
||||
m_clientId = other.m_clientId;
|
||||
m_id = other.m_id;
|
||||
m_diff = other.m_diff;
|
||||
m_height = other.m_height;
|
||||
m_target = other.m_target;
|
||||
m_index = other.m_index;
|
||||
m_poolWallet = other.m_poolWallet;
|
||||
m_extraNonce = other.m_extraNonce;
|
||||
|
||||
memcpy(m_blob, other.m_blob, sizeof(m_blob));
|
||||
memcpy(m_seedHash, other.m_seedHash, sizeof(m_seedHash));
|
||||
|
|
|
@ -55,6 +55,8 @@ public:
|
|||
bool setSeedHash(const char *hash);
|
||||
bool setTarget(const char *target);
|
||||
void setDiff(uint64_t diff);
|
||||
void setPoolWallet(const String &poolWallet);
|
||||
void setExtraNonce(const String &extraNonce);
|
||||
|
||||
inline bool isNicehash() const { return m_nicehash; }
|
||||
inline bool isValid() const { return m_size > 0 && m_diff > 0; }
|
||||
|
@ -72,6 +74,8 @@ public:
|
|||
inline uint64_t target() const { return m_target; }
|
||||
inline uint8_t fixedByte() const { return *(m_blob + 42); }
|
||||
inline uint8_t index() const { return m_index; }
|
||||
inline const String &poolWallet() { return m_poolWallet; }
|
||||
inline const String &extraNonce() { return m_extraNonce; }
|
||||
inline void reset() { m_size = 0; m_diff = 0; }
|
||||
inline void setAlgorithm(const char *algo) { m_algorithm = algo; }
|
||||
inline void setClientId(const String &id) { m_clientId = id; }
|
||||
|
@ -106,6 +110,8 @@ private:
|
|||
uint8_t m_blob[kMaxBlobSize];
|
||||
uint8_t m_index = 0;
|
||||
uint8_t m_seedHash[32];
|
||||
String m_poolWallet;
|
||||
String m_extraNonce;
|
||||
|
||||
# ifdef XMRIG_PROXY_PROJECT
|
||||
char m_rawBlob[kMaxBlobSize * 2 + 8];
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
namespace xmrig {
|
||||
|
||||
static const char *kAlgo = "algo";
|
||||
static const char *kSelfSelect = "self-select";
|
||||
static const char *kDaemon = "daemon";
|
||||
static const char *kDaemonPollInterval = "daemon-poll-interval";
|
||||
static const char *kEnabled = "enabled";
|
||||
|
@ -121,6 +122,8 @@ xmrig::Pool::Pool(const rapidjson::Value &object) :
|
|||
m_pollInterval = Json::getUint64(object, kDaemonPollInterval, kDefaultPollInterval);
|
||||
m_algorithm = Json::getString(object, kAlgo);
|
||||
|
||||
m_selfSelect = Json::getString(object, kSelfSelect);
|
||||
|
||||
m_flags.set(FLAG_ENABLED, Json::getBool(object, kEnabled, true));
|
||||
m_flags.set(FLAG_NICEHASH, Json::getBool(object, kNicehash));
|
||||
m_flags.set(FLAG_TLS, Json::getBool(object, kTls, m_flags.test(FLAG_TLS)));
|
||||
|
@ -296,6 +299,8 @@ rapidjson::Value xmrig::Pool::toJSON(rapidjson::Document &doc) const
|
|||
obj.AddMember(StringRef(kDaemonPollInterval), m_pollInterval, allocator);
|
||||
}
|
||||
|
||||
obj.AddMember(StringRef(kSelfSelect), m_selfSelect.toJSON(), allocator);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -312,6 +317,7 @@ void xmrig::Pool::print() const
|
|||
LOG_DEBUG ("algo: %s", m_algorithm.name());
|
||||
LOG_DEBUG ("nicehash: %d", static_cast<int>(m_flags.test(FLAG_NICEHASH)));
|
||||
LOG_DEBUG ("keepAlive: %d", m_keepAlive);
|
||||
LOG_DEBUG ("selfSelect %s", m_selfSelect.data());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ public:
|
|||
inline const String &rigId() const { return m_rigId; }
|
||||
inline const String &url() const { return m_url; }
|
||||
inline const String &user() const { return !m_user.isNull() ? m_user : kDefaultUser; }
|
||||
inline const String &selfSelect() const { return m_selfSelect; }
|
||||
inline int keepAlive() const { return m_keepAlive; }
|
||||
inline uint16_t port() const { return m_port; }
|
||||
inline uint64_t pollInterval() const { return m_pollInterval; }
|
||||
|
@ -87,6 +88,7 @@ public:
|
|||
inline void setPassword(const String &password) { m_password = password; }
|
||||
inline void setRigId(const String &rigId) { m_rigId = rigId; }
|
||||
inline void setUser(const String &user) { m_user = user; }
|
||||
inline void setSelfSelect(const String &selfSelect) { m_selfSelect = selfSelect; }
|
||||
|
||||
inline bool operator!=(const Pool &other) const { return !isEqual(other); }
|
||||
inline bool operator==(const Pool &other) const { return isEqual(other); }
|
||||
|
@ -115,6 +117,7 @@ private:
|
|||
String m_rigId;
|
||||
String m_url;
|
||||
String m_user;
|
||||
String m_selfSelect;
|
||||
uint16_t m_port;
|
||||
uint64_t m_pollInterval;
|
||||
};
|
||||
|
|
|
@ -43,7 +43,8 @@
|
|||
"enabled": true,
|
||||
"tls": false,
|
||||
"tls-fingerprint": null,
|
||||
"daemon": false
|
||||
"daemon": false,
|
||||
"self-select": null
|
||||
}
|
||||
],
|
||||
"print-time": 60,
|
||||
|
@ -52,4 +53,4 @@
|
|||
"syslog": false,
|
||||
"user-agent": null,
|
||||
"watch": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,8 @@ R"===(
|
|||
"enabled": true,
|
||||
"tls": false,
|
||||
"tls-fingerprint": null,
|
||||
"daemon": false
|
||||
"daemon": false,
|
||||
"self-select": null
|
||||
}
|
||||
],
|
||||
"print-time": 60,
|
||||
|
|
|
@ -63,6 +63,7 @@ static const option options[] = {
|
|||
{ "keepalive", 0, nullptr, IConfig::KeepAliveKey },
|
||||
{ "log-file", 1, nullptr, IConfig::LogFileKey },
|
||||
{ "nicehash", 0, nullptr, IConfig::NicehashKey },
|
||||
{ "self-select", 1, nullptr, IConfig::SelfSelectKey },
|
||||
{ "no-color", 0, nullptr, IConfig::ColorKey },
|
||||
{ "no-huge-pages", 0, nullptr, IConfig::HugePagesKey },
|
||||
{ "pass", 1, nullptr, IConfig::PasswordKey },
|
||||
|
|
|
@ -68,7 +68,8 @@ Options:\n\
|
|||
-t, --threads=N number of miner threads\n\
|
||||
-v, --av=N algorithm variation, 0 auto select\n\
|
||||
-k, --keepalive send keepalived packet for prevent timeout (needs pool support)\n\
|
||||
--nicehash enable nicehash.com support\n"
|
||||
--nicehash enable nicehash.com support\n\
|
||||
--self-select=URL self-select block templates from URL\n"
|
||||
#ifdef XMRIG_FEATURE_TLS
|
||||
"\
|
||||
--tls enable SSL/TLS support (needs pool support)\n\
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue