Skip to content

Commit

Permalink
Fixed the duplicate shares problem, added a time out for detecting di…
Browse files Browse the repository at this point in the history
…sconnects on Linux

Signed-off-by: Pttn <28868425+Pttn@users.noreply.github.com>
  • Loading branch information
Pttn committed Sep 10, 2018
1 parent d5b4f8c commit 7641c62
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 87 deletions.
28 changes: 28 additions & 0 deletions client.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <jansson.h>
#include <curl/curl.h>
#include "global.h"
#include "tools.h"

struct BlockHeader { // Total 1024 bits/128 bytes (256 hex chars)
uint32_t version;
Expand Down Expand Up @@ -42,6 +43,9 @@ struct WorkData {
uint16_t txCount;

// For Stratum
std::vector<std::array<uint32_t, 8>> txHashes;
std::vector<uint8_t> coinbase1, coinbase2;
uint16_t extraNonce2Len;
std::vector<uint8_t> extraNonce1, extraNonce2, jobId;

WorkData() {
Expand All @@ -53,10 +57,34 @@ struct WorkData {
transactions = std::string();
txCount = 0;

txHashes = std::vector<std::array<uint32_t, 8>>();
coinbase1 = std::vector<uint8_t>();
coinbase2 = std::vector<uint8_t>();
extraNonce2Len = 0;
extraNonce1 = std::vector<uint8_t>();
extraNonce2 = std::vector<uint8_t>();
jobId = std::vector<uint8_t>();
}

void merkleRootGenStratum() {
std::vector<uint8_t> coinbase;
extraNonce2 = std::vector<uint8_t>();
for (uint32_t i(0) ; i < coinbase1.size() ; i++) coinbase.push_back(coinbase1[i]);
for (uint32_t i(0) ; i < extraNonce1.size() ; i++) coinbase.push_back(extraNonce1[i]);
for (uint32_t i(0) ; i < extraNonce2Len ; i++) {
extraNonce2.push_back(rand(0x00, 0xFF));
coinbase.push_back(extraNonce2[i]);
}
for (uint32_t i(0) ; i < coinbase2.size() ; i++) coinbase.push_back(coinbase2[i]);

uint8_t cbHashTmp[32];
sha256(coinbase.data(), cbHashTmp, coinbase.size());
sha256(cbHashTmp, cbHashTmp, 32);
std::array<uint32_t, 8> cbHash;
for (uint32_t i(0) ; i < 8 ; i++) cbHash[i] = ((uint32_t*) cbHashTmp)[i];
txHashes.insert(txHashes.begin(), cbHash);
memcpy(bh.merkleRoot, calculateMerkleRootStratum(txHashes).data(), 32);
}
};

// Communicates with the server to get, parse, and submit mining work
Expand Down
10 changes: 8 additions & 2 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <iomanip>
#include <fstream>

std::string minerVersionString("rieMiner 0.9-alpha3");
std::string minerVersionString("rieMiner 0.9-alpha3.1");

Client *client;
std::mutex clientMutex;
Expand Down Expand Up @@ -46,7 +46,13 @@ void minerThread() {
}
else if (arguments.protocol() == "Stratum") {
wd.extraNonce2 = client->workData().extraNonce2;
wd.txHashes = client->workData().txHashes;
wd.coinbase1 = client->workData().coinbase1;
wd.coinbase2 = client->workData().coinbase2;
wd.extraNonce1 = client->workData().extraNonce1;
wd.extraNonce2Len = client->workData().extraNonce2Len;
wd.jobId = client->workData().jobId;
wd.merkleRootGenStratum();
}
hasValidWork = true;
}
Expand Down Expand Up @@ -104,7 +110,7 @@ void workManagement() {
// Update work
getWorkFromClient(client);
clientMutex.unlock();
usleep(100000);
usleep(200000);
}
}
else {
Expand Down
69 changes: 32 additions & 37 deletions stratumclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ bool StratumClient::connect(const Arguments& arguments) {
_port = arguments.port();
_sd = StratumData();
_wd = WorkData();
_buffer = std::array<char, RBUFSIZE>();
_noDataCount = 0;
_state = INIT;
_result = std::string();

Expand Down Expand Up @@ -58,18 +60,6 @@ bool StratumClient::connect(const Arguments& arguments) {
return true;
}

void StratumData::coinBaseGen() {
coinbase = std::vector<uint8_t>();
extraNonce2 = std::vector<uint8_t>();
for (uint32_t i(0) ; i < coinbase1.size() ; i++) coinbase.push_back(coinbase1[i]);
for (uint32_t i(0) ; i < extraNonce1.size() ; i++) coinbase.push_back(extraNonce1[i]);
for (uint32_t i(0) ; i < extraNonce2Len ; i++) {
extraNonce2.push_back(rand(0x00, 0xFF));
coinbase.push_back(extraNonce2[i]);
}
for (uint32_t i(0) ; i < coinbase2.size() ; i++) coinbase.push_back(coinbase2[i]);
}

void StratumClient::getSubscribeInfo() {
std::string r;
json_t *jsonObj(NULL), *jsonRes(NULL), *jsonErr(NULL);
Expand Down Expand Up @@ -135,9 +125,9 @@ void StratumClient::getSubscribeInfo() {
else {
std::cout << std::endl;
for (uint16_t i(0) ; i < _sd.sids.size() ; i++)
std::cout << " " << i << " - " << _sd.sids[i].first << ": " << binToHexStr(_sd.sids[i].second.data(), _sd.sids[i].second.size()) << std::endl;
std::cout << " " << i << " - " << _sd.sids[i].first << ": " << v8ToHexStr(_sd.sids[i].second) << std::endl;
}
std::cout << "ExtraNonce1 = " << binToHexStr(_sd.extraNonce1.data(), _sd.extraNonce1.size()) << std::endl;
std::cout << "ExtraNonce1 = " << v8ToHexStr(_sd.extraNonce1) << std::endl;
std::cout << "extraNonce1Len = " << _sd.extraNonce1Len << std::endl;
std::cout << "extraNonce2Len = " << _sd.extraNonce2Len << std::endl;

Expand All @@ -146,6 +136,7 @@ void StratumClient::getSubscribeInfo() {
send(_socket, oss.str().c_str(), oss.str().size(), 0);

_state = AUTHORIZE_SENT;
std::cout << "Waiting for server data..." << std::endl;
}
}

Expand All @@ -160,7 +151,7 @@ void StratumClient::handleOther() {

jsonObj = json_loads(_result.c_str(), 0, &err);
if (jsonObj == NULL)
std::cerr << "handleOther: JSON decode failed :| - " << err.text << std::endl;
/*std::cerr << "handleOther: JSON decode failed :| - " << err.text << std::endl*/; // This happens sometimes but without harm
else {
const char *method(NULL);
method = json_string_value(json_object_get(jsonObj, "method"));
Expand Down Expand Up @@ -237,43 +228,43 @@ bool StratumClient::getWork() {
_sd.coinbase1 = hexStrToV8(coinb1);
_sd.coinbase2 = hexStrToV8(coinb2);
_sd.jobId = hexStrToV8(job_id);

_sd.coinBaseGen();
_sd.txHashes.push_back(_sd.coinBaseHash());
memcpy(&_wd.bh, &_sd.bh, 128);
// Get Transactions Hashes
for (uint32_t i(0) ; i < json_array_size(jsonTxs) ; i++) {
std::array<uint32_t, 8> txHash;
uint8_t txHashTmp[32];
hexStrToBin(json_string_value(json_array_get(jsonTxs, i)), txHashTmp);
for (uint32_t j(0) ; j < 8 ; j++) txHash[j] = ((uint32_t*) txHashTmp)[j];
_sd.txHashes.push_back(txHash);
}
_sd.merkleRootGen();
memcpy(&_wd.bh, &_sd.bh, 128);

// Extract BlockHeight from Coinbase
uint32_t oldHeight(_wd.height), height(_sd.coinbase[43] + 256*_sd.coinbase[44] + 65536*_sd.coinbase[45]);
uint32_t oldHeight(_wd.height), height(_sd.coinbase1[43] + 256*_sd.coinbase1[44] + 65536*_sd.coinbase1[45] - 1);
// Notify when the network found a block
if (oldHeight != height) {
if (oldHeight != 0) {
stats.printTime();
if (_wd.height - stats.blockHeightAtDifficultyChange != 0) {
std::cout << " Blockheight = " << height - 1 << ", average "
std::cout << " Blockheight = " << height << ", average "
<< FIXED(1) << timeSince(stats.lastDifficultyChange)/(_wd.height - stats.blockHeightAtDifficultyChange)
<< " s, difficulty = " << stats.difficulty << std::endl;
}
else
std::cout << " Blockheight = " << height - 1 << ", new difficulty = " << stats.difficulty << std::endl;
std::cout << " Blockheight = " << height << ", new difficulty = " << stats.difficulty << std::endl;
}
else stats.blockHeightAtDifficultyChange = height;
}

_wd.height = height - 1;
_wd.bh.bits = swab32(_wd.bh.bits);
_wd.targetCompact = getCompact(_wd.bh.bits);
_wd.txCount = _sd.txHashes.size();
_wd.extraNonce1 = _sd.extraNonce1;
_wd.extraNonce2 = _sd.extraNonce2;
_wd.jobId = _sd.jobId;
// Fill the work data for the workers
_wd.height = height;
_wd.bh.bits = swab32(_wd.bh.bits);
_wd.targetCompact = getCompact(_wd.bh.bits);
_wd.txHashes = _sd.txHashes;
_wd.txCount = _sd.txHashes.size();
_wd.coinbase1 = _sd.coinbase1;
_wd.coinbase2 = _sd.coinbase2;
_wd.extraNonce1 = _sd.extraNonce1;
_wd.extraNonce2Len = _sd.extraNonce2Len;
_wd.jobId = _sd.jobId;
// Change endianness for mining (will revert back when submit share)
for (uint8_t i(0) ; i < 8; i++)
_wd.bh.previousblockhash[i] = be32dec(&_wd.bh.previousblockhash[i]);
Expand Down Expand Up @@ -320,13 +311,12 @@ void StratumClient::sendWork(const std::pair<WorkData, uint8_t>& share) const {

oss << "{\"method\": \"mining.submit\", \"params\": [\""
<< _user << "\", \""
<< binToHexStr(wdToSend.jobId.data(), wdToSend.jobId.size()) << "\", \""
<< binToHexStr(wdToSend.extraNonce2.data(), wdToSend.extraNonce2.size()) << "\", \""
<< v8ToHexStr(wdToSend.jobId) << "\", \""
<< v8ToHexStr(wdToSend.extraNonce2) << "\", \""
<< binToHexStr((const uint8_t*) &wdToSend.bh.curtime, 8) << "\", \""
<< binToHexStr(nonce, 32) << "\"], \"id\":0}\n";

send(_socket, oss.str().c_str(), oss.str().size(), 0);
std::cout << "Sent: " << oss.str();
}

bool StratumClient::process() {
Expand All @@ -346,19 +336,24 @@ bool StratumClient::process() {
_result = std::string();

if (n <= 0) { // No data received. Usually, this is normal, because of the non-blocking socket...
_noDataCount++;
#ifdef _WIN32
if (WSAGetLastError() != WSAEWOULDBLOCK || n == 0) { // ...but else, this is an error!
if (WSAGetLastError() != WSAEWOULDBLOCK || n == 0 || _noDataCount > MAXNDC) { // ...but else, this is an error!
#else
if (errno != EWOULDBLOCK || n == 0) { // ...but else, this is an error!
if (errno != EWOULDBLOCK || n == 0 || _noDataCount > MAXNDC) { // ...but else, this is an error!
#endif
std::cerr << "process: error receiving work data :| - " << std::strerror(errno) << std::endl;
if (_noDataCount > MAXNDC)
std::cerr << "process: no server response since a very long time, disconnection assumed." << std::endl;
else
std::cerr << "process: error receiving work data :| - " << std::strerror(errno) << std::endl;
_socket = -1;
_connected = false;
return false;
}
return true;
}

_noDataCount = 0;
_result.append(_buffer.cbegin(), _buffer.cbegin() + n);
// std::cout << "Result = " << _result << std::endl; // Main line for Stratum debugging

Expand Down
43 changes: 6 additions & 37 deletions stratumclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#define HEADER_STRATUMCLIENT_H

#include "client.h"
#include "tools.h"

#ifdef _WIN32
#include <winsock2.h>
Expand All @@ -21,11 +20,10 @@ struct StratumData {
BlockHeader bh;
std::vector<std::array<uint32_t, 8>> txHashes;
uint32_t height;
std::vector<uint8_t> coinbase1, coinbase2, coinbase;
uint8_t scriptPubKey[20]; // Calculated from custom payout address for Coinbase Transaction
std::vector<uint8_t> coinbase1, coinbase2;

std::vector<std::pair<std::string, std::vector<uint8_t>>> sids; // Subscription Ids
std::vector<uint8_t> extraNonce1, extraNonce2, jobId;
std::vector<uint8_t> extraNonce1, jobId;
uint16_t extraNonce1Len, extraNonce2Len;

StratumData() {
Expand All @@ -34,52 +32,23 @@ struct StratumData {
height = 0;
coinbase1 = std::vector<uint8_t>();
coinbase2 = std::vector<uint8_t>();
coinbase = std::vector<uint8_t>();
for (uint16_t i(0) ; i < 20 ; i++) scriptPubKey[i] = 0;
sids = std::vector<std::pair<std::string, std::vector<uint8_t>>>();
extraNonce1 = std::vector<uint8_t>();
extraNonce2 = std::vector<uint8_t>();
jobId = std::vector<uint8_t>();
extraNonce1Len = 0;
extraNonce2Len = 0;
}

void coinBaseGen();
std::array<uint32_t, 8> coinBaseHash() {
uint8_t cbHashTmp[32];
sha256(coinbase.data(), cbHashTmp, coinbase.size());
sha256(cbHashTmp, cbHashTmp, 32);
std::array<uint32_t, 8> cbHash;
for (uint32_t j(0) ; j < 8 ; j++) cbHash[j] = ((uint32_t*) cbHashTmp)[j];
return cbHash;
}
void merkleRootGen() {
if (txHashes.size() == 0) {
std::cerr << "MerkleRootGen: no transaction to hash!";
memset(bh.merkleRoot, 0, 32);
}
else if (txHashes.size() == 1)
memcpy(bh.merkleRoot, &txHashes[0], 32);
else {
uint8_t hashData[64], hashOut[32];
memcpy(hashData, txHashes[0].data(), 32);
for (uint32_t i(1) ; i < txHashes.size() ; i++) {
memcpy(&hashData[32], txHashes[i].data(), 32);
sha256(hashData, hashOut, 64);
sha256(hashOut, hashData, 32);
}
memcpy(bh.merkleRoot, hashData, 32);
}
}
};

#define RBUFSIZE 2048
#define RECVSIZE (RBUFSIZE - 4)
#define RBUFSIZE 2048
#define RECVSIZE (RBUFSIZE - 4)
#define MAXNDC 1024 // A sort of Timeout

class StratumClient : public Client {
StratumData _sd;
int _socket;
std::array<char, RBUFSIZE> _buffer;
uint16_t _noDataCount; // Used to disconnect if the server sent nothing since a long time

enum State {INIT, SUBSCRIBE_SENT, SUBSCRIBE_RCVD, AUTHORIZE_SENT, AUTHORIZE_RCVD, READY, SHARE_SENT};
State _state;
Expand Down
29 changes: 21 additions & 8 deletions tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,15 @@

#include "tools.h"

std::default_random_engine eng((std::random_device())());

uint8_t rand(uint8_t min, uint8_t max) {
if (min > max) std::swap(min, max);
std::default_random_engine eng((std::random_device())());
std::uniform_int_distribution<uint8_t> urd(min, max);
uint8_t n(urd(eng));
return n;
}

std::string binToHexStr(const void* p, uint32_t len) {
std::ostringstream oss;
for (uint32_t i(0) ; i < len ; i++)
oss << std::setfill('0') << std::setw(2) << std::hex << (uint32_t) ((uint8_t*) p)[i];
return oss.str();
}

std::vector<uint8_t> hexStrToV8(std::string str) {
if (str.size() % 2 != 0) str = "0" + str;
std::vector<uint8_t> v;
Expand Down Expand Up @@ -218,3 +212,22 @@ std::array<uint32_t, 8> calculateMerkleRoot(std::vector<std::array<uint32_t, 8>>
}
return merkleRoot;
}

std::array<uint32_t, 8> calculateMerkleRootStratum(std::vector<std::array<uint32_t, 8>> txHashes) {
std::array<uint32_t, 8> merkleRoot = {0, 0, 0, 0, 0, 0, 0, 0};
if (txHashes.size() == 0)
std::cerr << "calculateMerkleRootStratum: no transaction to hash!";
else if (txHashes.size() == 1)
return txHashes[0];
else {
uint8_t hashData[64], hashOut[32];
memcpy(hashData, txHashes[0].data(), 32);
for (uint32_t i(1) ; i < txHashes.size() ; i++) {
memcpy(&hashData[32], txHashes[i].data(), 32);
sha256(hashData, hashOut, 64);
sha256(hashOut, hashData, 32);
}
for (uint32_t i(0) ; i < 8 ; i++) merkleRoot[i] = ((uint32_t*) hashData)[i];
}
return merkleRoot;
}
Loading

0 comments on commit 7641c62

Please sign in to comment.