Add daemon support

For solo mining directly against daemon.
Use "daemon+tcp://" URL format instead of "stratum+tcp://"
This commit is contained in:
Howard Chu 2019-04-05 05:15:05 +01:00
parent 356fd04b0f
commit 837c1d7e0d
7 changed files with 256 additions and 68 deletions

View file

@ -6,6 +6,7 @@
* 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
@ -65,6 +66,7 @@ xmrig::Pool::Pool() :
m_enabled(true),
m_nicehash(false),
m_tls(false),
m_daemon(false),
m_keepAlive(0),
m_port(kDefaultPort)
{
@ -86,6 +88,7 @@ xmrig::Pool::Pool(const char *url) :
m_enabled(true),
m_nicehash(false),
m_tls(false),
m_daemon(false),
m_keepAlive(0),
m_port(kDefaultPort)
{
@ -135,6 +138,7 @@ xmrig::Pool::Pool(const char *host, uint16_t port, const char *user, const char
m_enabled(true),
m_nicehash(nicehash),
m_tls(tls),
m_daemon(false),
m_keepAlive(keepAlive),
m_host(host),
m_password(password),
@ -210,17 +214,24 @@ bool xmrig::Pool::parse(const char *url)
const char *base = url;
if (p) {
if (strncasecmp(url, "stratum+tcp://", 14) == 0) {
m_tls = false;
if (strncasecmp(url, "stratum+", 8) == 0) {
if (p-url != 11)
return false;
} else if (strncasecmp(url, "daemon+",7) == 0) {
if (p-url != 10)
return false;
m_daemon = true;
}
else if (strncasecmp(url, "stratum+ssl://", 14) == 0) {
if (strncasecmp(p-3, "tcp:", 4) == 0) {
m_tls = false;
} else if (strncasecmp(p-3, "ssl:", 4) == 0) {
m_tls = true;
}
else {
return false;
}
base = url + 14;
base = p+3;
}
if (!strlen(base) || *base == '/') {

View file

@ -6,6 +6,7 @@
* 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
@ -60,6 +61,7 @@ public:
inline bool isNicehash() const { return m_nicehash; }
inline bool isTLS() const { return m_tls; }
inline bool isValid() const { return !m_host.isNull() && m_port > 0; }
inline bool isDaemon() const { return m_daemon; }
inline const char *fingerprint() const { return m_fingerprint.data(); }
inline const char *host() const { return m_host.data(); }
inline const char *password() const { return !m_password.isNull() ? m_password.data() : kDefaultPassword; }
@ -107,6 +109,7 @@ private:
bool m_enabled;
bool m_nicehash;
bool m_tls;
bool m_daemon;
int m_keepAlive;
String m_fingerprint;
String m_host;

View file

@ -6,6 +6,7 @@
* 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
@ -88,6 +89,7 @@ xmrig::Client::Client(int id, const char *agent, IClientListener *listener) :
m_expire(0),
m_jobs(0),
m_keepAlive(0),
m_daemonPoll(0),
m_key(0),
m_stream(nullptr),
m_socket(nullptr)
@ -160,6 +162,7 @@ void xmrig::Client::setPool(const Pool &pool)
}
m_pool = pool;
m_daemon = pool.isDaemon();
}
@ -173,6 +176,12 @@ void xmrig::Client::tick(uint64_t now)
else if (m_keepAlive && now > m_keepAlive) {
ping();
}
if (m_daemonPoll && now > m_daemonPoll) {
send((char *)"GET /getheight HTTP/1.0\r\n\r\n", sizeof("GET /getheight HTTP/1.0\r\n\r\n")-1);
if (m_daemonInterval > 1)
m_daemonInterval >>= 1;
m_daemonPoll = uv_now(uv_default_loop()) + (m_daemonInterval * 1000);
}
}
if (m_expire && now > m_expire && m_state == ConnectingState) {
@ -223,6 +232,39 @@ int64_t xmrig::Client::submit(const JobResult &result)
}
# endif
if (m_daemon) {
size_t seqlen, datalen, datalenlen, bufsiz;
char cseq[33], cdata[33], *buf, *nonce;
int64_t rc, sequence;
if (!m_job.isValid())
return -1;
std::string blob = m_job.daemonBlob();
sequence = m_sequence;
m_job.reset();
seqlen = snprintf(cseq, sizeof(cseq), "%" PRId64, sequence);
datalen = blob.size() + sizeof("{\"id\": , \"method\": \"submitblock\", \"params\": [\"\"]}") + seqlen;
datalenlen = snprintf(cdata, sizeof(cdata), "%zd", datalen);
bufsiz = datalen+datalenlen+sizeof("POST /json_rpc HTTP/1.0\r\nContent-Length:\r\n\r\n");
buf = (char *)malloc(bufsiz);
if (buf == NULL)
return -1;
seqlen = sprintf(buf, "POST /json_rpc HTTP/1.0\r\nContent-Length: %zd\r\n\r\n"
"{\"id\": %zd, \"method\": \"submitblock\", \"params\": [\"%s\"]}", datalen, sequence, blob.c_str());
nonce = buf + seqlen - blob.size() - 3 + 78;
Job::toHex(reinterpret_cast<const unsigned char*>(&result.nonce), 4, nonce);
# ifdef XMRIG_PROXY_PROJECT
m_results[sequence] = SubmitResult(sequence, result.diff, result.actualDiff(), result.id);
# else
m_results[sequence] = SubmitResult(sequence, result.diff, result.actualDiff());
# endif
rc = send(buf, bufsiz);
free(buf);
return rc;
}
using namespace rapidjson;
# ifdef XMRIG_PROXY_PROJECT
@ -329,6 +371,32 @@ bool xmrig::Client::parseJob(const rapidjson::Value &params, int *code)
Job job(m_id, m_nicehash, m_pool.algorithm(), m_rpcId);
if (m_daemon) {
if (!job.setBlob(params["blockhashing_blob"].GetString())) {
*code = 4;
return false;
}
if (!job.setDiff(params["difficulty"].GetUint64())) {
*code = 5;
return false;
}
const char *bstr = params["blocktemplate_blob"].GetString();
size_t blen = strlen(bstr);
if (!job.setDaemonBlob(bstr)) {
*code = 4;
return false;
}
/* faking a unique job ID, using last 32 chars of block template blob */
if (!job.setId(bstr+blen-32)) {
*code = 3;
return false;
}
if (params.HasMember("prev_hash")) {
strncpy(m_prevHash, params["prev_hash"].GetString(), 64);
m_prevHash[64] = '\0';
}
} else {
if (!job.setId(params["job_id"].GetString())) {
*code = 3;
return false;
@ -358,6 +426,7 @@ bool xmrig::Client::parseJob(const rapidjson::Value &params, int *code)
job.setVariant(variant.GetString());
}
}
}
if (params.HasMember("height")) {
const rapidjson::Value &variant = params["height"];
@ -523,11 +592,15 @@ int64_t xmrig::Client::send(const rapidjson::Document &doc)
int64_t xmrig::Client::send(size_t size)
{
LOG_DEBUG("[%s] send (%d bytes): \"%s\"", m_pool.url(), size, m_sendBuf);
return send(m_sendBuf, size);
}
int64_t xmrig::Client::send(char *cbuf, size_t size)
{
LOG_DEBUG("[%s] send (%d bytes): \"%s\"", m_pool.url(), size, cbuf);
# ifndef XMRIG_NO_TLS
if (isTLS()) {
if (!m_tls->send(m_sendBuf, size)) {
if (!m_tls->send(cbuf, size)) {
return -1;
}
}
@ -539,7 +612,7 @@ int64_t xmrig::Client::send(size_t size)
return -1;
}
uv_buf_t buf = uv_buf_init(m_sendBuf, (unsigned int) size);
uv_buf_t buf = uv_buf_init(cbuf, (unsigned int) size);
if (uv_try_write(m_stream, &buf, 1) < 0) {
close();
@ -614,14 +687,23 @@ void xmrig::Client::login()
using namespace rapidjson;
m_results.clear();
if (m_daemon) {
char cseq[33];
int clen = snprintf(cseq, sizeof(cseq), "%" PRId64, m_sequence);
send(snprintf(m_sendBuf, sizeof(m_sendBuf), "POST /json_rpc HTTP/1.0\r\nContent-Length: %zd\r\n\r\n"
"{\"id\": %s, \"method\": \"getblocktemplate\", \"params\": {\"reserve_size\": 8, \"wallet_address\": "
"\"%s\"}}", 91+clen+strlen(m_pool.user()), cseq, m_pool.user()));
m_daemonInterval = 32;
} else {
Document doc(kObjectType);
auto &allocator = doc.GetAllocator();
Value params(kObjectType);
doc.AddMember("id", 1, allocator);
doc.AddMember("jsonrpc", "2.0", allocator);
doc.AddMember("method", "login", allocator);
Value params(kObjectType);
params.AddMember("login", StringRef(m_pool.user()), allocator);
params.AddMember("pass", StringRef(m_pool.password()), allocator);
params.AddMember("agent", StringRef(m_agent), allocator);
@ -647,6 +729,7 @@ void xmrig::Client::login()
send(doc);
}
}
void xmrig::Client::onClose()
@ -697,6 +780,22 @@ void xmrig::Client::parse(char *line, size_t len)
return;
}
if (m_daemon) {
/* height check */
if (doc.HasMember("height")) {
const char *hash = NULL;
uint64_t height = doc["height"].GetUint64();
if (doc.HasMember("hash"))
hash = doc["hash"].GetString();
if (height != m_job.height() ||
(hash != NULL && strcmp(hash, m_prevHash))) {
/* chain has changed, get a new job */
login();
}
return;
}
}
const rapidjson::Value &id = doc["id"];
if (id.IsInt64()) {
parseResponse(id.GetInt64(), doc["result"], doc["error"]);
@ -786,9 +885,25 @@ void xmrig::Client::parseResponse(int64_t id, const rapidjson::Value &result, co
return;
}
if (id == 1) {
int code = -1;
if (m_daemon) {
if (result.HasMember("difficulty")) {
/* new job */
if (!parseJob(result, &code))
goto loginFail;
if (id == 1) {
m_failures = 0;
m_listener->onLoginSuccess(this);
}
m_listener->onJobReceived(this, m_job);
m_daemonPoll = uv_now(uv_default_loop()) + (m_daemonInterval * 1000);
return;
}
}
if (id == 1) {
if (!parseLogin(result, &code)) {
loginFail:
if (!isQuiet()) {
LOG_ERR("[%s] login error code: %d", m_pool.url(), code);
}
@ -808,6 +923,8 @@ void xmrig::Client::parseResponse(int64_t id, const rapidjson::Value &result, co
it->second.done();
m_listener->onResultAccepted(this, it->second, nullptr);
m_results.erase(it);
if (m_daemon)
login();
}
}
@ -824,6 +941,30 @@ void xmrig::Client::read()
char* start = m_recvBuf.base;
size_t remaining = m_recvBufPos;
if (m_daemon) {
char *l, *crlf;
int hlen, rlen;
start[remaining] = '\0';
l = strstr(start, "Content-Length: ");
if (!l)
return;
crlf = strstr(l, "\r\n\r\n");
if (!crlf)
return;
hlen = crlf - start + 4;
if (sscanf(l + sizeof("Content-Length:"), "%d", &rlen) != 1) {
/* fail */
m_recvBufPos = 0;
return;
}
/* message not yet complete */
if (remaining-hlen < (unsigned)rlen)
return;
parse(crlf+4, rlen+1);
start = crlf+4 + rlen;
remaining -= hlen + rlen;
} else {
while ((end = static_cast<char*>(memchr(start, '\n', remaining))) != nullptr) {
end++;
size_t len = end - start;
@ -832,6 +973,7 @@ void xmrig::Client::read()
remaining -= len;
start = end;
}
}
if (remaining == 0) {
m_recvBufPos = 0;

View file

@ -6,6 +6,7 @@
* 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
@ -83,6 +84,7 @@ public:
void tick(uint64_t now);
inline bool isReady() const { return m_state == ConnectedState && m_failures == 0; }
inline const char *mode() const { return m_daemon ? "daemon" : "pool"; }
inline const char *host() const { return m_pool.host(); }
inline const char *ip() const { return m_ip; }
inline const Job &job() const { return m_job; }
@ -113,6 +115,7 @@ private:
int resolve(const char *host);
int64_t send(const rapidjson::Document &doc);
int64_t send(size_t size);
int64_t send(char *buf, size_t size);
void connect(const std::vector<addrinfo*> &ipv4, const std::vector<addrinfo*> &ipv6);
void connect(sockaddr *addr);
void handshake();
@ -142,15 +145,18 @@ private:
bool m_ipv6;
bool m_nicehash;
bool m_quiet;
bool m_daemon;
char m_buf[kInputBufferSize];
char m_ip[46];
char m_sendBuf[2048];
char m_prevHash[65];
const char *m_agent;
IClientListener *m_listener;
int m_extensions;
int m_id;
int m_retries;
int m_retryPause;
int m_daemonInterval;
int64_t m_failures;
Job m_job;
Pool m_pool;
@ -161,6 +167,7 @@ private:
uint64_t m_expire;
uint64_t m_jobs;
uint64_t m_keepAlive;
uint64_t m_daemonPoll;
uintptr_t m_key;
uv_buf_t m_recvBuf;
uv_getaddrinfo_t m_resolver;

View file

@ -7,6 +7,7 @@
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018 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
@ -99,6 +100,15 @@ bool xmrig::Job::isEqual(const Job &other) const
}
bool xmrig::Job::setDaemonBlob(const char *blob)
{
if (!blob) {
return false;
}
m_daemonBlob = blob;
return true;
}
bool xmrig::Job::setBlob(const char *blob)
{
if (!blob) {
@ -154,6 +164,14 @@ bool xmrig::Job::setBlob(const char *blob)
}
bool xmrig::Job::setDiff(uint64_t diff)
{
m_diff = diff;
m_target = toDiff(diff);
return true;
}
bool xmrig::Job::setTarget(const char *target)
{
if (!target) {

View file

@ -7,6 +7,7 @@
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* 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
@ -30,6 +31,7 @@
#include <stddef.h>
#include <stdint.h>
#include <string>
#include "common/crypto/Algorithm.h"
#include "common/net/Id.h"
@ -52,6 +54,8 @@ public:
bool isEqual(const Job &other) const;
bool setBlob(const char *blob);
bool setTarget(const char *target);
bool setDiff(uint64_t diff);
bool setDaemonBlob(const char *blob);
void setAlgorithm(const char *algo);
void setHeight(uint64_t height);
@ -60,6 +64,7 @@ public:
inline bool setId(const char *id) { return m_id.setId(id); }
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 std::string& daemonBlob() { return m_daemonBlob; }
inline const Algorithm &algorithm() const { return m_algorithm; }
inline const Id &clientId() const { return m_clientId; }
inline const Id &id() const { return m_id; }
@ -114,6 +119,7 @@ private:
char m_rawBlob[kMaxBlobSize * 2 + 8];
char m_rawTarget[24];
# endif
std::string m_daemonBlob;
};

View file

@ -6,6 +6,7 @@
* 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
@ -94,9 +95,9 @@ void xmrig::Network::onActive(IStrategy *strategy, Client *client)
m_state.setPool(client->host(), client->port(), client->ip());
const char *tlsVersion = client->tlsVersion();
LOG_INFO(isColors() ? WHITE_BOLD("use pool ") CYAN_BOLD("%s:%d ") GREEN_BOLD("%s") " \x1B[1;30m%s "
: "use pool %s:%d %s %s",
client->host(), client->port(), tlsVersion ? tlsVersion : "", client->ip());
LOG_INFO(isColors() ? WHITE_BOLD("use %s ") CYAN_BOLD("%s:%d ") GREEN_BOLD("%s") " \x1B[1;30m%s "
: "use %s %s:%d %s %s",
client->mode(), client->host(), client->port(), tlsVersion ? tlsVersion : "", client->ip());
const char *fingerprint = client->tlsFingerprint();
if (fingerprint != nullptr) {