StratumServer: support custom fixed difficulty

Example (set fixed difficulty 10000)

`"user":"x+10000"` in config.json or
`-u x+10000` in command line
This commit is contained in:
SChernykh 2021-08-28 17:18:41 +02:00
parent 46ce4ebee7
commit 9e438210d1
3 changed files with 167 additions and 48 deletions

View file

@ -195,6 +195,11 @@ struct difficulty_type
return 1;
}
// Safeguard against division by zero (CPU will trigger it even if lo = 1 because result doesn't fit in 64 bits)
if (lo <= 1) {
return std::numeric_limits<uint64_t>::max();
}
uint64_t rem;
uint64_t result = udiv128(1, 0, lo, &rem);
return rem ? (result + 1) : result;

View file

@ -127,7 +127,35 @@ void StratumServer::on_block(const BlockTemplate& block)
}
}
bool StratumServer::on_login(StratumClient* client, uint32_t id)
bool StratumServer::get_custom_diff(const char* s, difficulty_type& diff)
{
const char* diff_str = nullptr;
// Find last of '+' or '.'
while (s) {
const char c = *s;
if (!c) {
break;
}
if ((c == '+') || (c == '.')) {
diff_str = s;
}
++s;
}
if (diff_str) {
const uint64_t t = strtoull(diff_str + 1, nullptr, 10);
if (t) {
// Don't let clients set difficulty less than 1000
diff = { std::max<uint64_t>(t + 1, 1000), 0 };
return true;
}
}
return false;
}
bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* login)
{
const uint32_t extra_nonce = m_extraNonce.fetch_add(1);
@ -140,7 +168,13 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id)
uint32_t template_id;
const size_t blob_size = m_pool->block_template().get_hashing_blob(extra_nonce, hashing_blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset, template_id);
const uint64_t target = std::max(difficulty.target(), sidechain_difficulty.target());
uint64_t target = std::max(difficulty.target(), sidechain_difficulty.target());
if (get_custom_diff(login, client->m_customDiff)) {
LOGINFO(5, "client " << log::Gray() << static_cast<char*>(client->m_addrString) << " set custom difficulty " << client->m_customDiff);
target = std::max(target, client->m_customDiff.target());
}
uint32_t job_id;
{
@ -176,7 +210,7 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id)
return result;
}
bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* job_id_str, const char* nonce_str)
bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* job_id_str, const char* nonce_str, const char* result_str)
{
uint32_t job_id = 0;
@ -200,6 +234,17 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
nonce = (nonce << 8) | (d[0] << 4) | d[1];
}
hash resultHash;
for (size_t i = 0; i < HASH_SIZE; ++i) {
uint32_t d[2];
if (!from_hex(result_str[i * 2], d[0]) || !from_hex(result_str[i * 2 + 1], d[1])) {
LOGWARN(4, "Client: invalid params ('result' is not a hex value)");
return false;
}
resultHash.h[i] = static_cast<uint8_t>((d[0] << 4) | d[1]);
}
uint32_t template_id = 0;
uint32_t extra_nonce = 0;
@ -239,6 +284,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
share->m_templateId = template_id;
share->m_nonce = nonce;
share->m_extraNonce = extra_nonce;
share->m_resultHash = resultHash;
const int err = uv_queue_work(&m_loop, &share->m_req, on_share_found, on_after_share_found);
if (err) {
@ -328,14 +374,19 @@ void StratumServer::on_blobs_ready()
saved_job.target = data->m_target;
}
uint64_t target = data->m_target;
if (client->m_customDiff.lo) {
target = std::max(target, client->m_customDiff.target());
}
const bool result = send(client,
[data, client, hashing_blob, &job_id](void* buf)
[data, target, client, hashing_blob, &job_id](void* buf)
{
log::Stream s(reinterpret_cast<char*>(buf));
s << "{\"jsonrpc\":\"2.0\",\"method\":\"job\",\"params\":{\"blob\":\"";
s << log::hex_buf(hashing_blob, data->m_blobSize) << "\",\"job_id\":\"";
s << log::Hex(job_id) << "\",\"target\":\"";
s << log::hex_buf(reinterpret_cast<const uint8_t*>(&data->m_target), sizeof(data->m_target)) << "\",\"algo\":\"rx/0\",\"height\":";
s << log::hex_buf(reinterpret_cast<const uint8_t*>(&target), sizeof(target)) << "\",\"algo\":\"rx/0\",\"height\":";
s << data->m_height << ",\"seed_hash\":\"";
s << data->m_seedHash << "\"}}\n";
return s.m_pos;
@ -385,41 +436,56 @@ void StratumServer::on_share_found(uv_work_t* req)
return;
}
for (uint32_t i = 0, nonce = share->m_nonce; i < sizeof(share->m_nonce); ++i) {
blob[nonce_offset + i] = nonce & 255;
nonce >>= 8;
}
const bool mainchain_solution = difficulty.check_pow(share->m_resultHash);
const bool sidechain_solution = sidechain_difficulty.check_pow(share->m_resultHash);
hash pow_hash;
if (!pool->calculate_hash(blob, blob_size, seed_hash, pow_hash)) {
LOGWARN(4, "client: couldn't check share PoW");
share->m_result = SubmittedShare::Result::COULDNT_CHECK_POW;
return;
if (mainchain_solution || sidechain_solution) {
for (uint32_t i = 0, nonce = share->m_nonce; i < sizeof(share->m_nonce); ++i) {
blob[nonce_offset + i] = nonce & 255;
nonce >>= 8;
}
hash pow_hash;
if (!pool->calculate_hash(blob, blob_size, seed_hash, pow_hash)) {
LOGWARN(4, "client: couldn't check share PoW");
share->m_result = SubmittedShare::Result::COULDNT_CHECK_POW;
return;
}
if (pow_hash != share->m_resultHash) {
LOGWARN(4, "client: submitted a share with invalid PoW");
share->m_result = SubmittedShare::Result::INVALID_POW;
return;
}
LOGINFO(0, log::Green() << "SHARE FOUND at mainchain height " << height);
if (mainchain_solution) {
pool->submit_block_async(share->m_templateId, share->m_nonce, share->m_extraNonce);
block.update_tx_keys();
}
if (sidechain_solution) {
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
}
}
// Send the response to miner
const uint64_t value = *reinterpret_cast<uint64_t*>(pow_hash.h + HASH_SIZE - sizeof(uint64_t));
const uint64_t target = std::max(difficulty.target(), sidechain_difficulty.target());
const uint64_t value = *reinterpret_cast<uint64_t*>(share->m_resultHash.h + HASH_SIZE - sizeof(uint64_t));
uint64_t target = std::max(difficulty.target(), sidechain_difficulty.target());
if (client->m_customDiff.lo) {
target = std::max(target, client->m_customDiff.target());
}
if (LIKELY(value < target)) {
LOGINFO(0, log::Green() << "SHARE FOUND at mainchain height " << height);
share->m_result = SubmittedShare::Result::OK;
}
else {
LOGWARN(4, "got a low diff share from " << static_cast<char*>(client->m_addrString));
share->m_result = SubmittedShare::Result::LOW_DIFF;
}
// First check if we found a mainnet block
if (difficulty.check_pow(pow_hash)) {
pool->submit_block_async(share->m_templateId, share->m_nonce, share->m_extraNonce);
block.update_tx_keys();
}
// Then check if we found a sidechain block
if (sidechain_difficulty.check_pow(pow_hash)) {
pool->submit_sidechain_block(share->m_templateId, share->m_nonce, share->m_extraNonce);
}
}
void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
@ -454,6 +520,9 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
case SubmittedShare::Result::LOW_DIFF:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Low diff share\"}}\n";
break;
case SubmittedShare::Result::INVALID_POW:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Invalid PoW\"}}\n";
break;
case SubmittedShare::Result::OK:
s << "{\"id\":" << share->m_id << ",\"jsonrpc\":\"2.0\",\"error\":null,\"result\":{\"status\":\"OK\"}}\n";
break;
@ -475,6 +544,7 @@ StratumServer::StratumClient::StratumClient()
: m_rpcId(0)
, m_jobs{}
, m_perConnectionJobId(0)
, m_customDiff{}
{
uv_mutex_init_checked(&m_jobsLock);
}
@ -490,6 +560,7 @@ void StratumServer::StratumClient::reset()
m_rpcId = 0;
memset(m_jobs, 0, sizeof(m_jobs));
m_perConnectionJobId = 0;
m_customDiff = {};
}
bool StratumServer::StratumClient::on_read(char* data, uint32_t size)
@ -562,11 +633,11 @@ bool StratumServer::StratumClient::process_request(char* data, uint32_t /*size*/
const char* s = method.GetString();
if (strcmp(s, "login") == 0) {
LOGINFO(5, "incoming login from " << log::Gray() << static_cast<char*>(m_addrString));
LOGINFO(6, "incoming login from " << log::Gray() << static_cast<char*>(m_addrString));
return process_login(doc, id.GetUint());
}
else if (strcmp(s, "submit") == 0) {
LOGINFO(3, "incoming share from " << log::Gray() << static_cast<char*>(m_addrString));
LOGINFO(6, "incoming share from " << log::Gray() << static_cast<char*>(m_addrString));
return process_submit(doc, id.GetUint());
}
else {
@ -577,63 +648,101 @@ bool StratumServer::StratumClient::process_request(char* data, uint32_t /*size*/
return true;
}
bool StratumServer::StratumClient::process_login(rapidjson::Document& /*doc*/, uint32_t id)
{
return static_cast<StratumServer*>(m_owner)->on_login(this, id);
}
bool StratumServer::StratumClient::process_submit(rapidjson::Document& doc, uint32_t id)
bool StratumServer::StratumClient::process_login(rapidjson::Document& doc, uint32_t id)
{
if (!doc.HasMember("params")) {
LOGWARN(4, "client: invalid JSON request ('params' field not found)");
LOGWARN(4, "client: invalid JSON login request ('params' field not found)");
return false;
}
auto& params = doc["params"];
if (!params.IsObject()) {
LOGWARN(4, "client: invalid JSON request ('params' field is not an object)");
LOGWARN(4, "client: invalid JSON login request ('params' field is not an object)");
return false;
}
if (!params.HasMember("login")) {
LOGWARN(4, "client: invalid login params ('login' field not found)");
return false;
}
auto& login = params["login"];
if (!login.IsString()) {
LOGWARN(4, "client: invalid login params ('login' field is not a string)");
return false;
}
return static_cast<StratumServer*>(m_owner)->on_login(this, id, login.GetString());
}
bool StratumServer::StratumClient::process_submit(rapidjson::Document& doc, uint32_t id)
{
if (!doc.HasMember("params")) {
LOGWARN(4, "client: invalid JSON submit request ('params' field not found)");
return false;
}
auto& params = doc["params"];
if (!params.IsObject()) {
LOGWARN(4, "client: invalid JSON submit request ('params' field is not an object)");
return false;
}
if (!params.HasMember("id")) {
LOGWARN(4, "client: invalid params ('id' field not found)");
LOGWARN(4, "client: invalid submit params ('id' field not found)");
return false;
}
auto& rpcId = params["id"];
if (!rpcId.IsString()) {
LOGWARN(4, "client: invalid params ('id' field is not a string)");
LOGWARN(4, "client: invalid submit params ('id' field is not a string)");
return false;
}
if (!params.HasMember("job_id")) {
LOGWARN(4, "client: invalid params ('job_id' field not found)");
LOGWARN(4, "client: invalid submit params ('job_id' field not found)");
return false;
}
auto& job_id = params["job_id"];
if (!job_id.IsString()) {
LOGWARN(4, "client: invalid params ('job_id' field is not a string)");
LOGWARN(4, "client: invalid submit params ('job_id' field is not a string)");
return false;
}
if (!params.HasMember("nonce")) {
LOGWARN(4, "client: invalid params ('nonce' field not found)");
LOGWARN(4, "client: invalid submit params ('nonce' field not found)");
return false;
}
auto& nonce = params["nonce"];
if (!nonce.IsString()) {
LOGWARN(4, "client: invalid params ('nonce' field is not a string)");
LOGWARN(4, "client: invalid submit params ('nonce' field is not a string)");
return false;
}
if (nonce.GetStringLength() != sizeof(uint32_t) * 2) {
LOGWARN(4, "client: invalid params ('nonce' field has invalid length)");
LOGWARN(4, "client: invalid submit params ('nonce' field has invalid length)");
return false;
}
return static_cast<StratumServer*>(m_owner)->on_submit(this, id, job_id.GetString(), nonce.GetString());
if (!params.HasMember("result")) {
LOGWARN(4, "client: invalid submit params ('result' field not found)");
return false;
}
auto& result = params["result"];
if (!result.IsString()) {
LOGWARN(4, "client: invalid submit params ('result' field is not a string)");
return false;
}
if (result.GetStringLength() != HASH_SIZE * 2) {
LOGWARN(4, "client: invalid submit params ('result' field has invalid length)");
return false;
}
return static_cast<StratumServer*>(m_owner)->on_submit(this, id, job_id.GetString(), nonce.GetString(), result.GetString());
}
} // namespace p2pool

View file

@ -63,13 +63,16 @@ public:
} m_jobs[4];
uint32_t m_perConnectionJobId;
difficulty_type m_customDiff;
};
bool on_login(StratumClient* client, uint32_t id);
bool on_submit(StratumClient* client, uint32_t id, const char* job_id_str, const char* nonce_str);
bool on_login(StratumClient* client, uint32_t id, const char* login);
bool on_submit(StratumClient* client, uint32_t id, const char* job_id_str, const char* nonce_str, const char* result_str);
uint64_t get_random64();
private:
static bool get_custom_diff(const char* s, difficulty_type& diff);
static void on_share_found(uv_work_t* req);
static void on_after_share_found(uv_work_t* req, int status);
@ -110,11 +113,13 @@ private:
uint32_t m_templateId;
uint32_t m_nonce;
uint32_t m_extraNonce;
hash m_resultHash;
enum class Result {
STALE,
COULDNT_CHECK_POW,
LOW_DIFF,
INVALID_POW,
OK
} m_result;
};