implement stratum mode self-select

This commit is contained in:
Jethro Grassie 2019-07-06 14:49:44 -04:00
parent 5439f2d7b8
commit 40c69d2019
No known key found for this signature in database
GPG key ID: DE8ED755616565BB
12 changed files with 234 additions and 18 deletions

View file

@ -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);

View file

@ -71,6 +71,7 @@ public:
ProxyDonateKey = 1017,
DaemonKey = 1018,
DaemonPollKey = 1019,
SelfSelectKey = 1024,
// xmrig common
CPUPriorityKey = 1021,

View file

@ -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 &params, 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 &params, 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);

View file

@ -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;
};

View file

@ -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));

View file

@ -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];

View file

@ -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

View file

@ -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;
};

View file

@ -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
}
}

View file

@ -77,7 +77,8 @@ R"===(
"enabled": true,
"tls": false,
"tls-fingerprint": null,
"daemon": false
"daemon": false,
"self-select": null
}
],
"print-time": 60,

View file

@ -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 },

View file

@ -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\