Cryptocurrency mining pool written in C++ for speed. Supports Stratum.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

315 lines
10 KiB

#include "Server.h"
#include "Client.h"
#include "BigNum.h"
#include "DataMgr.h"
#include "ShareLimiter.h"
#include "Exception.h"
#include <iostream>
namespace Stratum
{
void Client::SendJob(bool clean)
{
if (clean)
_jobs.clear();
Job job = GetJob();
uint32 jobid = _jobid++;
_jobs[jobid] = job;
// Build merkle branch array
JSON merkle_branch(JSON_ARRAY);
uint64 branches = job.block->merkleBranches;
uint64 index = 0;
while (branches > 1) {
merkle_branch.Add(Util::BinToASCII(job.block->merkleTree[index+1]));
index += branches;
branches /= 2;
}
// Reverse prev block hash every 4 bytes... Makes a lot of sense...
ByteBuffer prevhashbuf(Util::Reverse(job.block->prevBlockHash));
std::vector<uint32> prevhash(8);
prevhashbuf >> prevhash[7] >> prevhash[6] >> prevhash[5] >> prevhash[4] >> prevhash[3] >> prevhash[2] >> prevhash[1] >> prevhash[0];
ByteBuffer prevhashfixed;
prevhashfixed << prevhash[0] << prevhash[1] << prevhash[2] << prevhash[3] << prevhash[4] << prevhash[5] << prevhash[6] << prevhash[7];
JSON params;
params.Add(Util::BinToASCII(ByteBuffer(jobid).Binary()));
params.Add(Util::BinToASCII(prevhashfixed.Binary()));
params.Add(Util::BinToASCII(job.coinbase1));
params.Add(Util::BinToASCII(job.coinbase2));
params.Add(merkle_branch);
params.Add(Util::BinToASCII(Util::Reverse(ByteBuffer(job.block->version).Binary())));
params.Add(Util::BinToASCII(Util::Reverse(ByteBuffer(job.block->bits).Binary())));
params.Add(Util::BinToASCII(Util::Reverse(ByteBuffer(job.block->time).Binary())));
params.Add(clean);
JSON msg;
msg["params"] = params;
msg["id"];
msg["method"] = "mining.notify";
SendMessage(msg);
}
void Client::OnMiningSubmit(JSON msg)
{
JSON params = msg["params"];
// Share limiter
if (!_shareLimiter.Submit()) {
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(20));
response["error"].Add("Blocked by share limiter");
response["error"].Add(JSON());
SendMessage(response);
return;
}
uint32 jobid;
ByteBuffer jobbuf(Util::ASCIIToBin(params[1].GetString()));
jobbuf >> jobid;
// Check if such job exists
if (!_jobs.count(jobid)) {
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(21));
response["error"].Add("Job not found");
response["error"].Add(JSON());
SendMessage(response);
return;
}
// check username
std::string username = params[0].GetString();
BinaryData extranonce2 = Util::ASCIIToBin(params[2].GetString());
if (extranonce2.size() != 4) {
sLog.Error(LOG_SERVER, "Wrong extranonce size");
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(20));
response["error"].Add("Wrong extranonce size");
response["error"].Add(JSON());
SendMessage(response);
return;
}
ByteBuffer timebuf(Util::Reverse(Util::ASCIIToBin(params[3].GetString())));
if (timebuf.Size() != 4) {
sLog.Error(LOG_SERVER, "Wrong ntime size");
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(20));
response["error"].Add("Wrong ntime size");
response["error"].Add(JSON());
SendMessage(response);
return;
}
ByteBuffer noncebuf(Util::Reverse(Util::ASCIIToBin(params[4].GetString())));
if (noncebuf.Size() != 4) {
sLog.Error(LOG_SERVER, "Wrong nonce size");
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(20));
response["error"].Add("Wrong nonce size");
response["error"].Add(JSON());
SendMessage(response);
return;
}
// Get job
Job& job = _jobs[jobid];
ByteBuffer share;
share << extranonce2 << timebuf << noncebuf;
if (!job.SubmitShare(share.Binary())) {
sLog.Error(LOG_SERVER, "Duplicate share");
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(22));
response["error"].Add("Duplicate share");
response["error"].Add(JSON());
SendMessage(response);
return;
}
// Copy block we are working on
Bitcoin::Block block = *job.block;
// Start assembling the block
timebuf >> block.time;
noncebuf >> block.nonce;
// Assemble coinbase
Bitcoin::Transaction coinbasetx;
ByteBuffer coinbasebuf;
coinbasebuf << job.coinbase1 << _extranonce << extranonce2 << job.coinbase2;
coinbasebuf >> coinbasetx;
// Set coinbase tx
block.tx[0] = coinbasetx;
sLog.Info(LOG_SERVER, "Coinbase hash1: %s", Util::BinToASCII(Crypto::SHA256D(coinbasebuf.Binary())).c_str());
sLog.Info(LOG_SERVER, "Coinbase hash2: %s", Util::BinToASCII(coinbasetx.GetHash()).c_str());
// Rebuilds only left side of merkle tree
block.RebuildMerkleTree();
sLog.Info(LOG_SERVER, "Merklehash: %s", Util::BinToASCII(block.merkleRootHash).c_str());
// Get block hash
BinaryData hash = block.GetHash();
sLog.Info(LOG_SERVER, "Block hash: %s", Util::BinToASCII(hash).c_str());
// Get block target
BigInt target(Util::BinToASCII(Util::Reverse(hash)), 16);
// Network target criteria
BigInt criteria(Bitcoin::TargetFromBits(block.bits));
BigInt diff = Bitcoin::TargetToDiff(criteria);
std::cout << "Target: " << target << std::endl;
std::cout << "Criteria: " << criteria << std::endl;
std::cout << "Diff: " << diff << std::endl;
// Check if difficulty meets job diff
if (target > Bitcoin::DiffToTarget(job.diff)) {
sLog.Error(LOG_SERVER, "Share above target");
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(23));
response["error"].Add("Share above target");
response["error"].Add(JSON());
SendMessage(response);
return;
}
// Check if block meets criteria
if (target <= criteria) {
sLog.Info(LOG_SERVER, "We have found a block candidate!");
_server->SubmitBlock(block);
} else {
sDataMgr.Push(Share(_ip, username, true, "", Util::Date(), job.diff));
JSON response;
response["id"] = msg["id"];
response["result"] = true;
response["error"];
SendMessage(response);
return;
}
}
void Client::OnMiningSubscribe(JSON msg)
{
_subscribed = true;
// Get extranonce from server
_extranonce = _server->GetExtranonce();
ByteBuffer noncebuf;
noncebuf << _extranonce;
JSON notify;
notify.Add("mining.notify");
notify.Add("ae6812eb4cd7735a302a8a9dd95cf71f");
JSON result;
result.Add(notify);
result.Add(Util::BinToASCII(noncebuf.Binary()));
result.Add(int64(4));
JSON response;
response["id"] = msg["id"].GetInt();
response["result"] = result;
response["error"];
SendMessage(response);
SendJob(false);
}
void Client::OnMiningAuthorize(JSON msg)
{
std::string username = msg["params"][0].GetString();
std::string password = msg["params"][1].GetString();
JSON response;
response["id"] = msg["id"].GetInt();
response["error"];
response["result"] = true;
SendMessage(response);
}
Job Client::GetJob()
{
Job job;
job.block = _server->GetWork();
job.diff = 1;
// Serialize transaction
ByteBuffer coinbasebuf;
coinbasebuf << job.block->tx[0];
BinaryData coinbase = coinbasebuf.Binary();
// Split coinbase
// tx.version - 4 bytes
// tx.in[0].outpoint.hash - 32 bytes
// tx.in[0].outpoint.n - 4 bytes
// tx.script (varint) - 2 bytes
// tx.script (block height) - 4 bytes
uint32 cbsplit = 4 + 32 + 4 + 2 + 4;
job.coinbase1 = BinaryData(coinbase.begin(), coinbase.begin() + cbsplit);
job.coinbase2 = BinaryData(coinbase.begin() + cbsplit + 8, coinbase.end()); // plus extranonce size
return job;
}
void Client::_OnReceive(const boost::system::error_code& error, size_t bytes_transferred)
{
if (!error) {
std::istream is(&_recvBuffer);
std::stringstream iss;
iss << is.rdbuf();
sLog.Debug(LOG_SERVER, "Received: %s", iss.str().c_str());
OnMessage(JSON::FromString(iss.str()));
/*try {
OnMessage(JSON::FromString(iss.str()));
} catch (uint64 e) {
sLog.Error(LOG_SERVER, "Exception caught while parsing json: %s", e.what());
}*/
StartRead();
} else {
// Client disconnected
if ((error == asio::error::eof) || (error == asio::error::connection_reset)) {
_server->Disconnect(shared_from_this());
}
}
}
void Client::_OnSend(const boost::system::error_code& error)
{
if (!error) {
// Party
} else {
// Client disconnected
if ((error == asio::error::eof) || (error == asio::error::connection_reset)) {
_server->Disconnect(shared_from_this());
}
}
}
}