|
|
@ -24,6 +24,9 @@ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
#include <uv.h> |
|
|
|
|
|
|
|
|
|
|
|
#include "base/net/stratum/DaemonClient.h" |
|
|
|
#include "3rdparty/rapidjson/document.h" |
|
|
|
#include "3rdparty/rapidjson/error/en.h" |
|
|
@ -42,7 +45,6 @@ |
|
|
|
#include "base/tools/Cvt.h" |
|
|
|
#include "base/tools/Timer.h" |
|
|
|
#include "base/tools/cryptonote/Signatures.h" |
|
|
|
#include "base/tools/cryptonote/WalletAddress.h" |
|
|
|
#include "net/JobResult.h" |
|
|
|
|
|
|
|
|
|
|
@ -181,12 +183,28 @@ int64_t xmrig::DaemonClient::submit(const JobResult &result) |
|
|
|
|
|
|
|
void xmrig::DaemonClient::connect() |
|
|
|
{ |
|
|
|
if ((m_pool.algorithm() == Algorithm::ASTROBWT_DERO) || (m_pool.coin() == Coin::DERO)) { |
|
|
|
m_apiVersion = API_DERO; |
|
|
|
} |
|
|
|
auto connectError = [this](const char *message) { |
|
|
|
if (!isQuiet()) { |
|
|
|
LOG_ERR("%s " RED("connect error: ") RED_BOLD("\"%s\""), tag(), message); |
|
|
|
} |
|
|
|
|
|
|
|
retry(); |
|
|
|
}; |
|
|
|
|
|
|
|
setState(ConnectingState); |
|
|
|
|
|
|
|
if (!m_walletAddress.isValid()) { |
|
|
|
return connectError("Invalid wallet address."); |
|
|
|
} |
|
|
|
|
|
|
|
if (!m_coin.isValid() && !m_pool.algorithm().isValid()) { |
|
|
|
return connectError("Invalid algorithm."); |
|
|
|
} |
|
|
|
|
|
|
|
if ((m_pool.algorithm() == Algorithm::ASTROBWT_DERO) || (m_coin == Coin::DERO)) { |
|
|
|
m_apiVersion = API_DERO; |
|
|
|
} |
|
|
|
|
|
|
|
if (m_pool.zmq_port() >= 0) { |
|
|
|
m_dns = Dns::resolve(m_pool.host(), this); |
|
|
|
} |
|
|
@ -203,6 +221,20 @@ void xmrig::DaemonClient::connect(const Pool &pool) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void xmrig::DaemonClient::setPool(const Pool &pool) |
|
|
|
{ |
|
|
|
BaseClient::setPool(pool); |
|
|
|
|
|
|
|
m_walletAddress.decode(m_user); |
|
|
|
|
|
|
|
m_coin = pool.coin().isValid() ? pool.coin() : m_walletAddress.coin(); |
|
|
|
|
|
|
|
if (!m_coin.isValid() && pool.algorithm() == Algorithm::RX_WOW) { |
|
|
|
m_coin = Coin::WOWNERO; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void xmrig::DaemonClient::onHttpData(const HttpData &data) |
|
|
|
{ |
|
|
|
if (data.status != 200) { |
|
|
@ -219,7 +251,7 @@ void xmrig::DaemonClient::onHttpData(const HttpData &data) |
|
|
|
rapidjson::Document doc; |
|
|
|
if (doc.Parse(data.body.c_str()).HasParseError()) { |
|
|
|
if (!isQuiet()) { |
|
|
|
LOG_ERR("[%s:%d] JSON decode failed: \"%s\"", m_pool.host().data(), m_pool.port(), rapidjson::GetParseError_En(doc.GetParseError())); |
|
|
|
LOG_ERR("%s " RED("JSON decode failed: ") RED_BOLD("\"%s\""), tag(), rapidjson::GetParseError_En(doc.GetParseError())); |
|
|
|
} |
|
|
|
|
|
|
|
return retry(); |
|
|
@ -284,7 +316,7 @@ void xmrig::DaemonClient::onTimer(const Timer *) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void xmrig::DaemonClient::onResolved(const DnsRecords& records, int status, const char* error) |
|
|
|
void xmrig::DaemonClient::onResolved(const DnsRecords &records, int status, const char* error) |
|
|
|
{ |
|
|
|
m_dns.reset(); |
|
|
|
|
|
|
@ -297,14 +329,14 @@ void xmrig::DaemonClient::onResolved(const DnsRecords& records, int status, cons |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (m_ZMQSocket) { |
|
|
|
delete m_ZMQSocket; |
|
|
|
} |
|
|
|
|
|
|
|
const auto& record = records.get(); |
|
|
|
delete m_ZMQSocket; |
|
|
|
|
|
|
|
|
|
|
|
const auto &record = records.get(); |
|
|
|
m_ip = record.ip(); |
|
|
|
|
|
|
|
uv_connect_t* req = new uv_connect_t; |
|
|
|
auto req = new uv_connect_t; |
|
|
|
req->data = m_storage.ptr(m_key); |
|
|
|
|
|
|
|
m_ZMQSocket = new uv_tcp_t; |
|
|
@ -329,26 +361,26 @@ bool xmrig::DaemonClient::isOutdated(uint64_t height, const char *hash) const |
|
|
|
|
|
|
|
bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code) |
|
|
|
{ |
|
|
|
Job job(false, m_pool.algorithm(), String()); |
|
|
|
|
|
|
|
String blocktemplate = Json::getString(params, kBlocktemplateBlob); |
|
|
|
auto jobError = [this, code](const char *message) { |
|
|
|
if (!isQuiet()) { |
|
|
|
LOG_ERR("%s " RED("job error: ") RED_BOLD("\"%s\""), tag(), message); |
|
|
|
} |
|
|
|
|
|
|
|
if (blocktemplate.isNull()) { |
|
|
|
LOG_ERR("Empty block template received from daemon"); |
|
|
|
*code = 1; |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Coin pool_coin = m_pool.coin(); |
|
|
|
Job job(false, m_pool.algorithm(), String()); |
|
|
|
|
|
|
|
if (!pool_coin.isValid() && (m_pool.algorithm() == Algorithm::RX_WOW)) { |
|
|
|
pool_coin = Coin::WOWNERO; |
|
|
|
String blocktemplate = Json::getString(params, kBlocktemplateBlob); |
|
|
|
|
|
|
|
if (blocktemplate.isNull()) { |
|
|
|
return jobError("Empty block template received from daemon."); |
|
|
|
} |
|
|
|
|
|
|
|
if (!m_blocktemplate.Init(blocktemplate, pool_coin)) { |
|
|
|
LOG_ERR("Invalid block template received from daemon"); |
|
|
|
*code = 2; |
|
|
|
return false; |
|
|
|
if (!m_blocktemplate.Init(blocktemplate, m_coin)) { |
|
|
|
return jobError("Invalid block template received from daemon."); |
|
|
|
} |
|
|
|
|
|
|
|
# ifdef XMRIG_PROXY_PROJECT |
|
|
@ -368,29 +400,21 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code) |
|
|
|
|
|
|
|
if (m_blocktemplate.has_miner_signature) { |
|
|
|
if (m_pool.spendSecretKey().isEmpty()) { |
|
|
|
LOG_ERR("Secret spend key is not set"); |
|
|
|
*code = 4; |
|
|
|
return false; |
|
|
|
return jobError("Secret spend key is not set."); |
|
|
|
} |
|
|
|
|
|
|
|
if (m_pool.spendSecretKey().size() != 64) { |
|
|
|
LOG_ERR("Secret spend key has invalid length. It must be 64 hex characters."); |
|
|
|
*code = 5; |
|
|
|
return false; |
|
|
|
return jobError("Secret spend key has invalid length. It must be 64 hex characters."); |
|
|
|
} |
|
|
|
|
|
|
|
uint8_t secret_spendkey[32]; |
|
|
|
if (!Cvt::fromHex(secret_spendkey, 32, m_pool.spendSecretKey(), 64)) { |
|
|
|
LOG_ERR("Secret spend key is not a valid hex data."); |
|
|
|
*code = 6; |
|
|
|
return false; |
|
|
|
return jobError("Secret spend key is not a valid hex data."); |
|
|
|
} |
|
|
|
|
|
|
|
uint8_t public_spendkey[32]; |
|
|
|
if (!secret_key_to_public_key(secret_spendkey, public_spendkey)) { |
|
|
|
LOG_ERR("Secret spend key is invalid."); |
|
|
|
*code = 7; |
|
|
|
return false; |
|
|
|
return jobError("Secret spend key is invalid."); |
|
|
|
} |
|
|
|
|
|
|
|
# ifdef XMRIG_PROXY_PROJECT |
|
|
@ -401,35 +425,24 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code) |
|
|
|
|
|
|
|
uint8_t public_viewkey[32]; |
|
|
|
if (!secret_key_to_public_key(secret_viewkey, public_viewkey)) { |
|
|
|
LOG_ERR("Secret view key is invalid."); |
|
|
|
*code = 8; |
|
|
|
return false; |
|
|
|
return jobError("Secret view key is invalid."); |
|
|
|
} |
|
|
|
|
|
|
|
uint8_t derivation[32]; |
|
|
|
if (!generate_key_derivation(m_blocktemplate.raw_blob.data() + m_blocktemplate.tx_pubkey_index, secret_viewkey, derivation)) { |
|
|
|
LOG_ERR("Failed to generate key derivation for miner signature."); |
|
|
|
*code = 9; |
|
|
|
return false; |
|
|
|
return jobError("Failed to generate key derivation for miner signature."); |
|
|
|
} |
|
|
|
|
|
|
|
WalletAddress user_address; |
|
|
|
if (!user_address.decode(m_pool.user())) { |
|
|
|
LOG_ERR("Invalid wallet address."); |
|
|
|
*code = 10; |
|
|
|
return false; |
|
|
|
if (!m_walletAddress.decode(m_pool.user())) { |
|
|
|
return jobError("Invalid wallet address."); |
|
|
|
} |
|
|
|
|
|
|
|
if (memcmp(user_address.spendKey(), public_spendkey, sizeof(public_spendkey)) != 0) { |
|
|
|
LOG_ERR("Wallet address and spend key don't match."); |
|
|
|
*code = 11; |
|
|
|
return false; |
|
|
|
if (memcmp(m_walletAddress.spendKey(), public_spendkey, sizeof(public_spendkey)) != 0) { |
|
|
|
return jobError("Wallet address and spend key don't match."); |
|
|
|
} |
|
|
|
|
|
|
|
if (memcmp(user_address.viewKey(), public_viewkey, sizeof(public_viewkey)) != 0) { |
|
|
|
LOG_ERR("Wallet address and view key don't match."); |
|
|
|
*code = 12; |
|
|
|
return false; |
|
|
|
if (memcmp(m_walletAddress.viewKey(), public_viewkey, sizeof(public_viewkey)) != 0) { |
|
|
|
return jobError("Wallet address and view key don't match."); |
|
|
|
} |
|
|
|
|
|
|
|
uint8_t eph_secret_key[32]; |
|
|
@ -444,8 +457,8 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code) |
|
|
|
Cvt::toHex(m_blockhashingblob.data() + offset * 2, kBlobReserveSize * 2, Cvt::randomBytes(kBlobReserveSize).data(), kBlobReserveSize); |
|
|
|
} |
|
|
|
|
|
|
|
if (pool_coin.isValid()) { |
|
|
|
job.setAlgorithm(pool_coin.algorithm(m_blocktemplate.major_version)); |
|
|
|
if (m_coin.isValid()) { |
|
|
|
job.setAlgorithm(m_coin.algorithm(m_blocktemplate.major_version)); |
|
|
|
} |
|
|
|
|
|
|
|
if (!job.setBlob(m_blockhashingblob)) { |
|
|
@ -594,7 +607,6 @@ void xmrig::DaemonClient::send(const char *path) |
|
|
|
|
|
|
|
void xmrig::DaemonClient::setState(SocketState state) |
|
|
|
{ |
|
|
|
assert(m_state != state); |
|
|
|
if (m_state == state) { |
|
|
|
return; |
|
|
|
} |
|
|
@ -735,10 +747,9 @@ void xmrig::DaemonClient::ZMQRead(ssize_t nread, const uv_buf_t* buf) |
|
|
|
m_ZMQConnectionState = ZMQ_GREETING_2; |
|
|
|
break; |
|
|
|
} |
|
|
|
else { |
|
|
|
LOG_ERR("%s " RED("ZMQ handshake failed: invalid greeting format"), tag()); |
|
|
|
ZMQClose(); |
|
|
|
} |
|
|
|
|
|
|
|
LOG_ERR("%s " RED("ZMQ handshake failed: invalid greeting format"), tag()); |
|
|
|
ZMQClose(); |
|
|
|
} |
|
|
|
return; |
|
|
|
|
|
|
@ -751,10 +762,10 @@ void xmrig::DaemonClient::ZMQRead(ssize_t nread, const uv_buf_t* buf) |
|
|
|
ZMQWrite(kZMQHandshake, sizeof(kZMQHandshake) - 1); |
|
|
|
break; |
|
|
|
} |
|
|
|
else { |
|
|
|
LOG_ERR("%s " RED("ZMQ handshake failed: invalid greeting format 2"), tag()); |
|
|
|
ZMQClose(); |
|
|
|
} |
|
|
|
|
|
|
|
LOG_ERR("%s " RED("ZMQ handshake failed: invalid greeting format 2"), tag()); |
|
|
|
ZMQClose(); |
|
|
|
|
|
|
|
} |
|
|
|
return; |
|
|
|
|
|
|
|