mirror of
https://github.com/SChernykh/p2pool.git
synced 2024-12-23 03:49:23 +00:00
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:
parent
46ce4ebee7
commit
9e438210d1
3 changed files with 167 additions and 48 deletions
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue