Add nodejs pool extra nonce support
This commit is contained in:
@@ -27,7 +27,7 @@ namespace JSONRPCRequest {
|
||||
|
||||
struct CurlContext : public nocopy_nomove
|
||||
{
|
||||
CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, bool ssl, const std::string& ssl_fingerprint, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
|
||||
CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, bool ssl, const std::string& ssl_fingerprint, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop, int timeout_seconds);
|
||||
~CurlContext();
|
||||
|
||||
static int socket_func(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp)
|
||||
@@ -81,9 +81,10 @@ struct CurlContext : public nocopy_nomove
|
||||
uint64_t m_connectedTime;
|
||||
|
||||
std::string m_proxy;
|
||||
int m_timeoutSeconds;
|
||||
};
|
||||
|
||||
CurlContext::CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, bool ssl, const std::string& ssl_fingerprint, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
|
||||
CurlContext::CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, bool ssl, const std::string& ssl_fingerprint, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop, int timeout_seconds)
|
||||
: m_callback(cb)
|
||||
, m_closeCallback(close_cb)
|
||||
, m_loop(loop)
|
||||
@@ -95,6 +96,7 @@ CurlContext::CurlContext(const std::string& address, int port, const std::string
|
||||
, m_startTime(0)
|
||||
, m_connectedTime(0)
|
||||
, m_proxy(proxy)
|
||||
, m_timeoutSeconds(timeout_seconds)
|
||||
{
|
||||
m_pollHandles.reserve(2);
|
||||
|
||||
@@ -179,11 +181,14 @@ CurlContext::CurlContext(const std::string& address, int port, const std::string
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_WRITEFUNCTION, write_func);
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_WRITEDATA, this);
|
||||
|
||||
int timeout = m_timeoutSeconds;
|
||||
if (timeout <= 0) {
|
||||
#ifdef DEV_TEST_SYNC
|
||||
const int timeout = 10;
|
||||
timeout = 10;
|
||||
#else
|
||||
const int timeout = proxy.empty() ? 1 : 5;
|
||||
timeout = proxy.empty() ? 1 : 5;
|
||||
#endif
|
||||
}
|
||||
|
||||
curl_easy_setopt_checked(m_handle, CURLOPT_URL, m_url.c_str());
|
||||
|
||||
@@ -518,7 +523,7 @@ void CurlContext::shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, bool ssl, const std::string& ssl_fingerprint, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
|
||||
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, bool ssl, const std::string& ssl_fingerprint, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop, int timeout_seconds)
|
||||
{
|
||||
if (!loop) {
|
||||
loop = uv_default_loop_checked();
|
||||
@@ -528,7 +533,7 @@ void Call(const std::string& address, int port, const std::string& req, const st
|
||||
[=]()
|
||||
{
|
||||
try {
|
||||
new CurlContext(address, port, req, auth, proxy, ssl, ssl_fingerprint, cb, close_cb, loop);
|
||||
new CurlContext(address, port, req, auth, proxy, ssl, ssl_fingerprint, cb, close_cb, loop, timeout_seconds);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
const char* msg = e.what();
|
||||
|
||||
@@ -24,15 +24,15 @@ typedef Callback<void, const char* /*msg*/, size_t /*msg_size*/, double /*tcp_pi
|
||||
|
||||
FORCEINLINE static constexpr void dummy_callback(const char* /*msg*/, size_t /*msg_size*/, double /*tcp_ping*/) {}
|
||||
|
||||
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, bool ssl, const std::string& ssl_fingerprint, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
|
||||
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, bool ssl, const std::string& ssl_fingerprint, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop, int timeout_seconds = 0);
|
||||
|
||||
template<typename T, typename U>
|
||||
FORCEINLINE void call(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, bool ssl, const std::string& ssl_fingerprint, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr)
|
||||
FORCEINLINE void call(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, bool ssl, const std::string& ssl_fingerprint, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr, int timeout_seconds = 0)
|
||||
{
|
||||
typedef Callback<void, const char*, size_t, double>::Derived<T> CallbackT;
|
||||
typedef Callback<void, const char*, size_t, double>::Derived<U> CallbackU;
|
||||
|
||||
Call(address, port, req, auth, proxy, ssl, ssl_fingerprint, new CallbackT(std::forward<T>(cb)), new CallbackU(std::forward<U>(close_cb)), loop);
|
||||
Call(address, port, req, auth, proxy, ssl, ssl_fingerprint, new CallbackT(std::forward<T>(cb)), new CallbackU(std::forward<U>(close_cb)), loop, timeout_seconds);
|
||||
}
|
||||
|
||||
} // namespace JSONRPCRequest
|
||||
|
||||
@@ -28,6 +28,10 @@ LOG_CATEGORY(MergeMiningClientJSON_RPC)
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
namespace {
|
||||
static constexpr int MERGE_MINING_RPC_TIMEOUT_SECONDS = 30;
|
||||
}
|
||||
|
||||
MergeMiningClientJSON_RPC::MergeMiningClientJSON_RPC(p2pool* pool, const std::string& host, const std::string& wallet)
|
||||
: MergeMiningClientShared(pool, wallet)
|
||||
, m_host(host)
|
||||
@@ -135,7 +139,7 @@ void MergeMiningClientJSON_RPC::merge_mining_get_chain_id()
|
||||
if (size > 0) {
|
||||
LOGERR(1, "couldn't get merge mining id from " << m_host << ':' << m_port << ", error " << log::const_buf(data, size));
|
||||
}
|
||||
}, &m_loop);
|
||||
}, &m_loop, MERGE_MINING_RPC_TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
bool MergeMiningClientJSON_RPC::parse_merge_mining_get_chain_id(const char* data, size_t size)
|
||||
@@ -237,7 +241,7 @@ void MergeMiningClientJSON_RPC::merge_mining_get_aux_block(uint64_t height, cons
|
||||
LOGERR(3, "couldn't get merge mining job from " << m_host << ':' << m_port << ", error " << log::const_buf(data, size));
|
||||
}
|
||||
m_getJobRunning = false;
|
||||
}, &m_loop);
|
||||
}, &m_loop, MERGE_MINING_RPC_TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
bool MergeMiningClientJSON_RPC::parse_merge_mining_get_aux_block(const char* data, size_t size, bool& changed)
|
||||
@@ -343,7 +347,7 @@ void MergeMiningClientJSON_RPC::submit_solution(const std::vector<uint8_t>& /*co
|
||||
}
|
||||
// Get new mining job
|
||||
on_timer();
|
||||
}, &m_loop);
|
||||
}, &m_loop, MERGE_MINING_RPC_TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
void MergeMiningClientJSON_RPC::print_status() const
|
||||
|
||||
+111
-18
@@ -27,9 +27,10 @@ LOG_CATEGORY(NodejsPoolRpc)
|
||||
namespace p2pool {
|
||||
|
||||
static constexpr int DEFAULT_BACKLOG = 64;
|
||||
static constexpr uint32_t NODEJS_POOL_NONCE_SIZE = 17;
|
||||
static constexpr size_t MAX_CACHED_WORK_ITEMS = 8;
|
||||
static constexpr size_t NODEJS_POOL_RPC_CALLBACK_BUF_SIZE = 65536;
|
||||
static constexpr size_t EXTERNAL_EXTRA_NONCE_SIZE = 4;
|
||||
static constexpr uint32_t HASHING_BLOB_BATCH_COUNT = 128;
|
||||
static thread_local const char* log_category_prefix = "NodejsPoolRpc ";
|
||||
|
||||
namespace {
|
||||
@@ -56,6 +57,28 @@ FORCEINLINE uint32_t read_u32_le(const uint8_t* p)
|
||||
| (static_cast<uint32_t>(p[3]) << 24);
|
||||
}
|
||||
|
||||
bool compare_ignoring_ranges(const uint8_t* a, const uint8_t* b, size_t size, const std::initializer_list<std::pair<size_t, size_t>>& ignored_ranges)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
bool ignored = false;
|
||||
|
||||
for (const auto& range : ignored_ranges) {
|
||||
const size_t offset = range.first;
|
||||
const size_t range_size = range.second;
|
||||
if ((range_size > 0) && (offset <= i) && (i < offset + range_size)) {
|
||||
ignored = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignored && (a[i] != b[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string http_response(const std::string& body, const char* status = "200 OK", const char* content_type = "application/json")
|
||||
{
|
||||
std::string s;
|
||||
@@ -200,19 +223,19 @@ bool NodejsPoolRpc::find_cached_work_by_block_blob(const std::vector<uint8_t>& b
|
||||
continue;
|
||||
}
|
||||
|
||||
if (work.nonce_offset + NONCE_SIZE > blob.size()) {
|
||||
if ((work.nonce_offset + NONCE_SIZE > blob.size()) || (work.extra_nonce_offset + work.extra_nonce_size > blob.size())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t prefix_size = work.nonce_offset;
|
||||
const size_t suffix_offset = work.nonce_offset + NONCE_SIZE;
|
||||
const size_t suffix_size = blob.size() - suffix_offset;
|
||||
|
||||
if (prefix_size && (memcmp(blob.data(), work.block_template_blob.data(), prefix_size) != 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (suffix_size && (memcmp(blob.data() + suffix_offset, work.block_template_blob.data() + suffix_offset, suffix_size) != 0)) {
|
||||
if (!compare_ignoring_ranges(
|
||||
blob.data(),
|
||||
work.block_template_blob.data(),
|
||||
blob.size(),
|
||||
{
|
||||
{ work.nonce_offset, NONCE_SIZE },
|
||||
{ work.extra_nonce_offset, work.extra_nonce_size }
|
||||
}))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -269,6 +292,8 @@ bool NodejsPoolRpc::make_work_snapshot(WorkSnapshot& out) const
|
||||
out.nonce_offset = full_nonce_offset;
|
||||
out.hashing_nonce_offset = nonce_offset;
|
||||
out.extra_nonce_offset = extra_nonce_offset;
|
||||
out.extra_nonce_size = EXTERNAL_EXTRA_NONCE_SIZE;
|
||||
out.hashing_blob_extra_nonce_start = 1;
|
||||
out.mainchain_difficulty = mainchain_diff;
|
||||
out.sidechain_difficulty = sidechain_diff;
|
||||
out.aux_difficulty = aux_diff;
|
||||
@@ -276,6 +301,51 @@ bool NodejsPoolRpc::make_work_snapshot(WorkSnapshot& out) const
|
||||
out.block_template_blob = std::move(block_template_blob);
|
||||
out.hashing_blob.assign(hashing_blob, hashing_blob + hashing_blob_size);
|
||||
|
||||
uint64_t batch_height = 0;
|
||||
difficulty_type batch_mainchain_diff, batch_aux_diff, batch_sidechain_diff;
|
||||
hash batch_seed_hash;
|
||||
size_t batch_nonce_offset = 0;
|
||||
uint8_t batch_blob[128] = {};
|
||||
out.hashing_blob_size = 0;
|
||||
out.hashing_blobs.clear();
|
||||
out.hashing_blobs.reserve(static_cast<size_t>(HASHING_BLOB_BATCH_COUNT) * sizeof(batch_blob));
|
||||
|
||||
for (uint32_t i = 0; i < HASHING_BLOB_BATCH_COUNT; ++i) {
|
||||
const uint32_t current_extra_nonce = out.hashing_blob_extra_nonce_start + i;
|
||||
const uint32_t current_blob_size = m_pool->block_template().get_hashing_blob(
|
||||
template_id,
|
||||
current_extra_nonce,
|
||||
batch_blob,
|
||||
batch_height,
|
||||
batch_mainchain_diff,
|
||||
batch_aux_diff,
|
||||
batch_sidechain_diff,
|
||||
batch_seed_hash,
|
||||
batch_nonce_offset);
|
||||
|
||||
if (!current_blob_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
out.hashing_blob_size = current_blob_size;
|
||||
}
|
||||
else if (current_blob_size != out.hashing_blob_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((batch_nonce_offset != nonce_offset) || (batch_seed_hash != seed_hash)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out.hashing_blobs.insert(out.hashing_blobs.end(), batch_blob, batch_blob + current_blob_size);
|
||||
++out.hashing_blob_count;
|
||||
}
|
||||
|
||||
if (!out.hashing_blob_size || !out.hashing_blob_count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> work_key;
|
||||
work_key.reserve(HASH_SIZE * 2 + sizeof(uint64_t) * 4 + sizeof(uint32_t) + out.hashing_blob.size());
|
||||
work_key.insert(work_key.end(), miner_data.prev_id.h, miner_data.prev_id.h + HASH_SIZE);
|
||||
@@ -444,6 +514,7 @@ std::string NodejsPoolRpc::build_json_rpc_response(const std::string& body) cons
|
||||
2048 +
|
||||
(work.block_template_blob.size() * 2) +
|
||||
(work.hashing_blob.size() * 2) +
|
||||
(work.hashing_blobs.size() * 2) +
|
||||
256;
|
||||
|
||||
std::vector<char> response(response_capacity, '\0');
|
||||
@@ -451,6 +522,10 @@ std::string NodejsPoolRpc::build_json_rpc_response(const std::string& body) cons
|
||||
s << "{\"id\":\"" << log::EscapedString(id) << "\",\"jsonrpc\":\"2.0\",\"result\":{"
|
||||
<< "\"blocktemplate_blob\":\"" << log::hex_buf(work.block_template_blob.data(), work.block_template_blob.size()) << '"'
|
||||
<< ",\"blockhashing_blob\":\"" << log::hex_buf(work.hashing_blob.data(), work.hashing_blob.size()) << '"'
|
||||
<< ",\"p2pool_hashing_blob_batch_start\":" << work.hashing_blob_extra_nonce_start
|
||||
<< ",\"p2pool_hashing_blob_batch_size\":" << work.hashing_blob_size
|
||||
<< ",\"p2pool_hashing_blob_batch_count\":" << work.hashing_blob_count
|
||||
<< ",\"p2pool_hashing_blob_batch\":\"" << log::hex_buf(work.hashing_blobs.data(), work.hashing_blobs.size()) << '"'
|
||||
<< ",\"difficulty\":" << work.submit_difficulty.lo
|
||||
<< ",\"height\":" << work.mining_height
|
||||
<< ",\"nonce_offset\":" << work.nonce_offset
|
||||
@@ -465,9 +540,9 @@ std::string NodejsPoolRpc::build_json_rpc_response(const std::string& body) cons
|
||||
<< ",\"mainchain_difficulty\":\"" << work.mainchain_difficulty << '"'
|
||||
<< ",\"backend_submit_difficulty\":\"" << work.submit_difficulty << '"'
|
||||
<< ",\"backend_mode\":\"aux_only\""
|
||||
<< ",\"p2pool_fixed_extra_nonce\":true"
|
||||
<< ",\"p2pool_fixed_extra_nonce_value\":0"
|
||||
<< ",\"p2pool_nodejs_pool_nonce_size\":" << NODEJS_POOL_NONCE_SIZE
|
||||
<< ",\"p2pool_external_extra_nonce\":true"
|
||||
<< ",\"p2pool_external_extra_nonce_size\":" << work.extra_nonce_size
|
||||
<< ",\"p2pool_external_extra_nonce_endianness\":\"LE\""
|
||||
<< "}}";
|
||||
return std::string(response.data(), s.m_pos);
|
||||
}
|
||||
@@ -493,7 +568,10 @@ std::string NodejsPoolRpc::build_json_rpc_response(const std::string& body) cons
|
||||
}
|
||||
|
||||
const uint32_t nonce = read_u32_le(blob.data() + work.nonce_offset);
|
||||
const uint32_t extra_nonce = 0;
|
||||
if (work.extra_nonce_offset + work.extra_nonce_size > blob.size()) {
|
||||
return json_error_response(id, 0, "invalid_extra_nonce_offset");
|
||||
}
|
||||
const uint32_t extra_nonce = read_u32_le(blob.data() + work.extra_nonce_offset);
|
||||
|
||||
uint8_t hashing_blob[128] = {};
|
||||
uint64_t height = 0;
|
||||
@@ -517,16 +595,31 @@ std::string NodejsPoolRpc::build_json_rpc_response(const std::string& body) cons
|
||||
return json_error_response(id, 0, "low_diff");
|
||||
}
|
||||
|
||||
const bool meets_mainchain = mainchain_diff.check_pow(result_hash);
|
||||
const bool meets_aux = aux_diff.check_pow(result_hash);
|
||||
const bool meets_sidechain = sidechain_diff.check_pow(result_hash);
|
||||
|
||||
LOGINFO(4, "NodejsPoolRpc submitblock: template id = " << work.template_id
|
||||
<< ", nonce = " << nonce
|
||||
<< ", extra_nonce = " << extra_nonce
|
||||
<< ", backend_submit_diff = " << work.submit_difficulty
|
||||
<< ", aux_diff = " << aux_diff
|
||||
<< ", sidechain_diff = " << sidechain_diff
|
||||
<< ", mainchain_diff = " << mainchain_diff
|
||||
<< ", meets_aux = " << (meets_aux ? "true" : "false")
|
||||
<< ", meets_sidechain = " << (meets_sidechain ? "true" : "false")
|
||||
<< ", meets_mainchain = " << (meets_mainchain ? "true" : "false"));
|
||||
|
||||
bool parent_submitted = false;
|
||||
bool sidechain_submitted = false;
|
||||
size_t aux_submitted = 0;
|
||||
|
||||
if (mainchain_diff.check_pow(result_hash)) {
|
||||
if (meets_mainchain) {
|
||||
m_pool->submit_block_async(std::move(blob));
|
||||
parent_submitted = true;
|
||||
}
|
||||
|
||||
if (aux_diff.check_pow(result_hash)) {
|
||||
if (meets_aux) {
|
||||
const std::vector<AuxChainData> aux_chains = m_pool->block_template().get_aux_chains(work.template_id);
|
||||
|
||||
std::vector<p2pool::SubmitAuxBlockData> aux_blocks;
|
||||
@@ -544,7 +637,7 @@ std::string NodejsPoolRpc::build_json_rpc_response(const std::string& body) cons
|
||||
}
|
||||
}
|
||||
|
||||
if (sidechain_diff.check_pow(result_hash)) {
|
||||
if (meets_sidechain) {
|
||||
sidechain_submitted = m_pool->submit_sidechain_block(work.template_id, nonce, extra_nonce);
|
||||
}
|
||||
|
||||
|
||||
+20
-15
@@ -32,25 +32,30 @@ public:
|
||||
void on_shutdown() override;
|
||||
|
||||
private:
|
||||
struct WorkSnapshot
|
||||
{
|
||||
bool ready = false;
|
||||
uint64_t mainchain_height = 0;
|
||||
uint64_t mining_height = 0;
|
||||
uint64_t sidechain_height = 0;
|
||||
struct WorkSnapshot
|
||||
{
|
||||
bool ready = false;
|
||||
uint64_t mainchain_height = 0;
|
||||
uint64_t mining_height = 0;
|
||||
uint64_t sidechain_height = 0;
|
||||
hash parent_prev_id;
|
||||
hash seed_hash;
|
||||
hash work_hash;
|
||||
uint32_t template_id = 0;
|
||||
size_t nonce_offset = 0;
|
||||
size_t hashing_nonce_offset = 0;
|
||||
size_t extra_nonce_offset = 0;
|
||||
difficulty_type mainchain_difficulty;
|
||||
difficulty_type sidechain_difficulty;
|
||||
difficulty_type aux_difficulty;
|
||||
difficulty_type submit_difficulty;
|
||||
std::vector<uint8_t> block_template_blob;
|
||||
uint32_t template_id = 0;
|
||||
size_t nonce_offset = 0;
|
||||
size_t hashing_nonce_offset = 0;
|
||||
size_t extra_nonce_offset = 0;
|
||||
size_t extra_nonce_size = 4;
|
||||
uint32_t hashing_blob_extra_nonce_start = 0;
|
||||
uint32_t hashing_blob_count = 0;
|
||||
uint32_t hashing_blob_size = 0;
|
||||
difficulty_type mainchain_difficulty;
|
||||
difficulty_type sidechain_difficulty;
|
||||
difficulty_type aux_difficulty;
|
||||
difficulty_type submit_difficulty;
|
||||
std::vector<uint8_t> block_template_blob;
|
||||
std::vector<uint8_t> hashing_blob;
|
||||
std::vector<uint8_t> hashing_blobs;
|
||||
};
|
||||
|
||||
struct RpcClient : public Client
|
||||
|
||||
+51
-1
@@ -172,6 +172,7 @@ p2pool::p2pool(const Params& params)
|
||||
|
||||
uv_mutex_init_checked(&m_submitBlockDataLock);
|
||||
uv_mutex_init_checked(&m_submitAuxBlockDataLock);
|
||||
uv_mutex_init_checked(&m_recentAuxSubmitsLock);
|
||||
|
||||
uv_mutex_init_checked(&m_missingHeightsLock);
|
||||
|
||||
@@ -256,6 +257,7 @@ p2pool::~p2pool()
|
||||
|
||||
uv_mutex_destroy(&m_submitBlockDataLock);
|
||||
uv_mutex_destroy(&m_submitAuxBlockDataLock);
|
||||
uv_mutex_destroy(&m_recentAuxSubmitsLock);
|
||||
|
||||
uv_mutex_destroy(&m_missingHeightsLock);
|
||||
|
||||
@@ -906,9 +908,28 @@ void p2pool::submit_block_async(std::vector<uint8_t>&& blob)
|
||||
|
||||
void p2pool::submit_aux_block_async(const std::vector<SubmitAuxBlockData>& aux_blocks)
|
||||
{
|
||||
std::vector<SubmitAuxBlockData> filtered_aux_blocks;
|
||||
filtered_aux_blocks.reserve(aux_blocks.size());
|
||||
|
||||
for (const SubmitAuxBlockData& aux_block : aux_blocks) {
|
||||
if (remember_recent_aux_submit(aux_block)) {
|
||||
filtered_aux_blocks.push_back(aux_block);
|
||||
}
|
||||
else {
|
||||
LOGINFO(4, "submit_aux_block_async: skipping duplicate aux submit for template id = " << aux_block.template_id
|
||||
<< ", chain_id = " << aux_block.chain_id
|
||||
<< ", nonce = " << aux_block.nonce
|
||||
<< ", extra_nonce = " << aux_block.extra_nonce);
|
||||
}
|
||||
}
|
||||
|
||||
if (filtered_aux_blocks.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
MutexLock lock(m_submitAuxBlockDataLock);
|
||||
m_submitAuxBlockData.insert(m_submitAuxBlockData.end(), aux_blocks.begin(), aux_blocks.end());
|
||||
m_submitAuxBlockData.insert(m_submitAuxBlockData.end(), filtered_aux_blocks.begin(), filtered_aux_blocks.end());
|
||||
}
|
||||
|
||||
// If p2pool is stopped, m_submitAuxBlockAsync is most likely already closed
|
||||
@@ -924,6 +945,35 @@ void p2pool::submit_aux_block_async(const std::vector<SubmitAuxBlockData>& aux_b
|
||||
}
|
||||
}
|
||||
|
||||
bool p2pool::remember_recent_aux_submit(const SubmitAuxBlockData& data) const
|
||||
{
|
||||
// Collapse burst duplicates from overlapping workers, but keep the window short
|
||||
// so a later retry after a genuine transport failure can still go through.
|
||||
constexpr uint64_t DUPLICATE_WINDOW = 5;
|
||||
const uint64_t now = seconds_since_epoch();
|
||||
|
||||
MutexLock lock(m_recentAuxSubmitsLock);
|
||||
|
||||
m_recentAuxSubmits.erase(
|
||||
std::remove_if(m_recentAuxSubmits.begin(), m_recentAuxSubmits.end(),
|
||||
[now](const RecentAuxSubmit& item) {
|
||||
return (now > item.timestamp) && (now - item.timestamp > DUPLICATE_WINDOW);
|
||||
}),
|
||||
m_recentAuxSubmits.end());
|
||||
|
||||
for (const RecentAuxSubmit& item : m_recentAuxSubmits) {
|
||||
if ((item.chain_id == data.chain_id)
|
||||
&& (item.template_id == data.template_id)
|
||||
&& (item.nonce == data.nonce)
|
||||
&& (item.extra_nonce == data.extra_nonce)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_recentAuxSubmits.push_back(RecentAuxSubmit{ data.chain_id, data.template_id, data.nonce, data.extra_nonce, now });
|
||||
return true;
|
||||
}
|
||||
|
||||
void p2pool::submit_aux_block() const
|
||||
{
|
||||
std::vector<SubmitAuxBlockData> submit_data;
|
||||
|
||||
@@ -157,6 +157,7 @@ private:
|
||||
|
||||
void submit_block() const;
|
||||
void submit_aux_block() const;
|
||||
bool remember_recent_aux_submit(const SubmitAuxBlockData& data) const;
|
||||
|
||||
std::atomic<bool> m_stopped;
|
||||
|
||||
@@ -254,6 +255,18 @@ private:
|
||||
mutable uv_mutex_t m_submitAuxBlockDataLock;
|
||||
mutable std::vector<SubmitAuxBlockData> m_submitAuxBlockData;
|
||||
|
||||
struct RecentAuxSubmit
|
||||
{
|
||||
hash chain_id;
|
||||
uint32_t template_id = 0;
|
||||
uint32_t nonce = 0;
|
||||
uint32_t extra_nonce = 0;
|
||||
uint64_t timestamp = 0;
|
||||
};
|
||||
|
||||
mutable uv_mutex_t m_recentAuxSubmitsLock;
|
||||
mutable std::vector<RecentAuxSubmit> m_recentAuxSubmits;
|
||||
|
||||
uv_async_t m_submitBlockAsync;
|
||||
uv_async_t m_submitAuxBlockAsync;
|
||||
uv_async_t m_blockTemplateAsync;
|
||||
|
||||
Reference in New Issue
Block a user