From f9def6cb734e05e20a97747a635a58877d26effe Mon Sep 17 00:00:00 2001 From: SChernykh Date: Sat, 20 Nov 2021 11:51:22 +0100 Subject: [PATCH] Added "calc_pow" RPC support --- src/block_template.cpp | 2 +- src/json_rpc_request.cpp | 4 +- src/json_rpc_request.h | 8 +-- src/main.cpp | 1 + src/p2pool.cpp | 21 ++++-- src/p2pool.h | 8 +-- src/params.cpp | 4 ++ src/params.h | 1 + src/pool_block.cpp | 4 +- src/pool_block.h | 4 +- src/pow_hash.cpp | 114 ++++++++++++++++++++++++++++++++- src/pow_hash.h | 51 +++++++++++++-- src/side_chain.cpp | 2 +- src/stratum_server.cpp | 2 +- src/tcp_server.inl | 1 + tests/src/pool_block_tests.cpp | 2 +- 16 files changed, 201 insertions(+), 28 deletions(-) diff --git a/src/block_template.cpp b/src/block_template.cpp index 1f80e2e..d1171fa 100644 --- a/src/block_template.cpp +++ b/src/block_template.cpp @@ -1018,7 +1018,7 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, } hash pow_hash; - if (!check.get_pow_hash(m_pool->hasher(), m_seedHash, pow_hash)) { + if (!check.get_pow_hash(m_pool->hasher(), check.m_txinGenHeight, m_seedHash, pow_hash)) { LOGERR(1, "PoW check failed for the sidechain block. Fix it! "); } else if (!check.m_difficulty.check_pow(pow_hash)) { diff --git a/src/json_rpc_request.cpp b/src/json_rpc_request.cpp index 7ed54d2..dafb65d 100644 --- a/src/json_rpc_request.cpp +++ b/src/json_rpc_request.cpp @@ -25,7 +25,7 @@ static constexpr char log_category_prefix[] = "JSONRPCRequest "; namespace p2pool { -JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb) +JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop) : m_socket{} , m_connect{} , m_write{} @@ -38,7 +38,7 @@ JSONRPCRequest::JSONRPCRequest(const char* address, int port, const char* req, C { m_readBuf[0] = '\0'; - uv_tcp_init(uv_default_loop_checked(), &m_socket); + uv_tcp_init(loop ? loop : uv_default_loop_checked(), &m_socket); uv_tcp_nodelay(&m_socket, 1); sockaddr_storage addr; diff --git a/src/json_rpc_request.h b/src/json_rpc_request.h index e3be1b9..1d0e229 100644 --- a/src/json_rpc_request.h +++ b/src/json_rpc_request.h @@ -26,18 +26,18 @@ public: static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb) { // It will be deleted in one of the tcp callbacks eventually - JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback(std::move(cb)), nullptr); + JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback(std::move(cb)), nullptr, nullptr); if (!r->m_valid) { delete r; } } template - static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb, U&& close_cb) + static FORCEINLINE void call(const char* address, int port, const char* req, T&& cb, U&& close_cb, uv_loop_t* loop = nullptr) { // It will be deleted in one of the tcp callbacks eventually CallbackBase* close_callback = new Callback(std::move(close_cb)); - JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback(std::move(cb)), close_callback); + JSONRPCRequest* r = new JSONRPCRequest(address, port, req, new Callback(std::move(cb)), close_callback, loop); if (!r->m_valid) { constexpr char err[] = "internal error"; (*close_callback)(err, sizeof(err) - 1); @@ -63,7 +63,7 @@ private: T m_cb; }; - JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb); + JSONRPCRequest(const char* address, int port, const char* req, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop); ~JSONRPCRequest(); static void on_connect(uv_connect_t* req, int status); diff --git a/src/main.cpp b/src/main.cpp index 52a855c..7511966 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,6 +39,7 @@ static void usage() "--stratum-api Enable /local/ path in api path for Stratum Server statistics\n" "--no-cache Disable p2pool.cache\n" "--no-color Disable colors in console output\n" + "--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes\n" "--help Show this help message\n\n" "Example command line:\n\n" "%s --host 127.0.0.1 --rpc-port 18081 --zmq-port 18083 --wallet YOUR_WALLET_ADDRESS --stratum 0.0.0.0:%d --p2p 0.0.0.0:%d\n\n", diff --git a/src/p2pool.cpp b/src/p2pool.cpp index 870c62e..61e9ab7 100644 --- a/src/p2pool.cpp +++ b/src/p2pool.cpp @@ -110,7 +110,14 @@ p2pool::p2pool(int argc, char* argv[]) m_api = m_params->m_apiPath.empty() ? nullptr : new p2pool_api(m_params->m_apiPath, m_params->m_localStats); m_sideChain = new SideChain(this, type); - m_hasher = new RandomX_Hasher(this); + + if (m_params->m_disableRandomX) { + m_hasher = new RandomX_Hasher_RPC(this); + } + else { + m_hasher = new RandomX_Hasher(this); + } + m_blockTemplate = new BlockTemplate(this); m_mempool = new Mempool(); m_consoleCommands = new ConsoleCommands(this); @@ -131,9 +138,9 @@ p2pool::~p2pool() delete m_consoleCommands; } -bool p2pool::calculate_hash(const void* data, size_t size, const hash& seed, hash& result) +bool p2pool::calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) { - return m_hasher->calculate(data, size, seed, result); + return m_hasher->calculate(data, size, height, seed, result); } uint64_t p2pool::get_seed_height(uint64_t height) @@ -811,10 +818,14 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size) return; } - if (version < 0x30008) { + const uint64_t required = m_params->m_disableRandomX ? 0x30009 : 0x30008; + + if (version < required) { const uint64_t version_hi = version >> 16; const uint64_t version_lo = version & 65535; - LOGERR(1, "monerod RPC v" << version_hi << '.' << version_lo << " is incompatible, update to RPC >= v3.8"); + const uint64_t required_version_hi = required >> 16; + const uint64_t required_version_lo = required & 65535; + LOGERR(1, "monerod RPC v" << version_hi << '.' << version_lo << " is incompatible, update to RPC >= v" << required_version_hi << '.' << required_version_lo); panic(); } diff --git a/src/p2pool.h b/src/p2pool.h index 270ec25..d877fb7 100644 --- a/src/p2pool.h +++ b/src/p2pool.h @@ -23,7 +23,7 @@ namespace p2pool { struct Params; -class RandomX_Hasher; +class RandomX_Hasher_Base; class BlockTemplate; class Mempool; class SideChain; @@ -51,8 +51,8 @@ public: p2pool_api* api() const { return m_api; } - RandomX_Hasher* hasher() const { return m_hasher; } - bool calculate_hash(const void* data, size_t size, const hash& seed, hash& result); + RandomX_Hasher_Base* hasher() const { return m_hasher; } + bool calculate_hash(const void* data, size_t size, uint64_t height, const hash& seed, hash& result); static uint64_t get_seed_height(uint64_t height); bool get_seed(uint64_t height, hash& seed) const; @@ -97,7 +97,7 @@ private: p2pool_api* m_api; SideChain* m_sideChain; - RandomX_Hasher* m_hasher; + RandomX_Hasher_Base* m_hasher; BlockTemplate* m_blockTemplate; MinerData m_minerData; bool m_updateSeed; diff --git a/src/params.cpp b/src/params.cpp index 6457391..e848f5f 100644 --- a/src/params.cpp +++ b/src/params.cpp @@ -81,6 +81,10 @@ Params::Params(int argc, char* argv[]) if (strcmp(argv[i], "--no-color") == 0) { log::CONSOLE_COLORS = false; } + + if (strcmp(argv[i], "--no-randomx") == 0) { + m_disableRandomX = true; + } } if (m_stratumAddresses.empty()) { diff --git a/src/params.h b/src/params.h index ef60deb..d3645b0 100644 --- a/src/params.h +++ b/src/params.h @@ -39,6 +39,7 @@ struct Params std::string m_apiPath; bool m_localStats = false; bool m_blockCache = true; + bool m_disableRandomX = false; }; } // namespace p2pool diff --git a/src/pool_block.cpp b/src/pool_block.cpp index 5003559..7ed0172 100644 --- a/src/pool_block.cpp +++ b/src/pool_block.cpp @@ -225,7 +225,7 @@ void PoolBlock::serialize_sidechain_data() writeVarint(m_cumulativeDifficulty.hi, m_sideChainData); } -bool PoolBlock::get_pow_hash(RandomX_Hasher* hasher, const hash& seed_hash, hash& pow_hash) +bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash) { alignas(8) uint8_t hashes[HASH_SIZE * 3]; @@ -295,7 +295,7 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher* hasher, const hash& seed_hash, hash writeVarint(count, [&blob, &blob_size](uint8_t b) { blob[blob_size++] = b; }); - return hasher->calculate(blob, blob_size, seed_hash, pow_hash); + return hasher->calculate(blob, blob_size, height, seed_hash, pow_hash); } } // namespace p2pool diff --git a/src/pool_block.h b/src/pool_block.h index 1430be8..91b6efe 100644 --- a/src/pool_block.h +++ b/src/pool_block.h @@ -28,7 +28,7 @@ namespace p2pool { -class RandomX_Hasher; +class RandomX_Hasher_Base; class SideChain; /* @@ -136,7 +136,7 @@ struct PoolBlock void serialize_sidechain_data(); int deserialize(const uint8_t* data, size_t size, SideChain& sidechain); - bool get_pow_hash(RandomX_Hasher* hasher, const hash& seed_hash, hash& pow_hash); + bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash); }; } // namespace p2pool diff --git a/src/pow_hash.cpp b/src/pow_hash.cpp index d54a183..86753bc 100644 --- a/src/pow_hash.cpp +++ b/src/pow_hash.cpp @@ -22,6 +22,9 @@ #include "randomx.h" #include "configuration.h" #include "virtual_machine.hpp" +#include "json_rpc_request.h" +#include "json_parsers.h" +#include #include static constexpr char log_category_prefix[] = "RandomX_Hasher "; @@ -302,7 +305,7 @@ void RandomX_Hasher::set_old_seed(const hash& seed) LOGINFO(1, log::LightCyan() << "old cache updated"); } -bool RandomX_Hasher::calculate(const void* data, size_t size, const hash& seed, hash& result) +bool RandomX_Hasher::calculate(const void* data, size_t size, uint64_t /*height*/, const hash& seed, hash& result) { // First try to use the dataset if it's ready if (uv_rwlock_tryrdlock(&m_datasetLock) == 0) { @@ -348,4 +351,113 @@ bool RandomX_Hasher::calculate(const void* data, size_t size, const hash& seed, return false; } +RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool) + : m_pool(pool) + , m_loopStopped(false) + , m_loopThread{} +{ + int err = uv_loop_init(&m_loop); + if (err) { + LOGERR(1, "failed to create event loop, error " << uv_err_name(err)); + panic(); + } + + uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown); + uv_async_init(&m_loop, &m_kickTheLoopAsync, nullptr); + m_shutdownAsync.data = this; + + uv_mutex_init_checked(&m_requestMutex); + uv_mutex_init_checked(&m_condMutex); + + err = uv_cond_init(&m_cond); + if (err) { + LOGERR(1, "failed to create cond, error " << uv_err_name(err)); + panic(); + } + + err = uv_thread_create(&m_loopThread, loop, this); + if (err) { + LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err)); + panic(); + } +} + +RandomX_Hasher_RPC::~RandomX_Hasher_RPC() +{ + uv_async_send(&m_shutdownAsync); + + using namespace std::chrono; + while (!m_loopStopped) { + std::this_thread::sleep_for(milliseconds(1)); + } + + uv_mutex_destroy(&m_requestMutex); + uv_mutex_destroy(&m_condMutex); + uv_cond_destroy(&m_cond); + + LOGINFO(1, "stopped"); +} + +void RandomX_Hasher_RPC::loop(void* data) +{ + LOGINFO(1, "event loop started"); + RandomX_Hasher_RPC* hasher = static_cast(data); + uv_run(&hasher->m_loop, UV_RUN_DEFAULT); + uv_loop_close(&hasher->m_loop); + LOGINFO(1, "event loop stopped"); + hasher->m_loopStopped = true; +} + +bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t height, const hash& /*seed*/, hash& h) +{ + MutexLock lock(m_requestMutex); + + const uint8_t* data = reinterpret_cast(data_ptr); + const uint8_t major_version = data[0]; + + char buf[log::Stream::BUF_SIZE + 1]; + log::Stream s(buf); + s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"calc_pow\",\"params\":{\"major_version\":" << major_version << + ",\"height\":" << height << + ",\"block_blob\":\"" << log::hex_buf(data, size) << '"' << + ",\"seed_hash\":\"\"}}"; + + volatile int result = 0; + volatile bool done = false; + + JSONRPCRequest::call(m_pool->params().m_host.c_str(), m_pool->params().m_rpcPort, buf, + [&result, &h](const char* data, size_t size) + { + rapidjson::Document doc; + if (doc.Parse(data, size).HasParseError() || !parseValue(doc, "result", h)) { + LOGWARN(3, "RPC calc_pow: invalid JSON response (parse error)"); + result = -1; + return; + } + result = 1; + }, + [this, &result, &done](const char* data, size_t size) + { + if (size > 0) { + LOGWARN(3, "RPC calc_pow: server returned error " << log::const_buf(data, size)); + result = -1; + } + + MutexLock lock2(m_condMutex); + done = true; + uv_cond_signal(&m_cond); + }, &m_loop); + + uv_async_send(&m_kickTheLoopAsync); + + { + MutexLock lock2(m_condMutex); + while (!done) { + uv_cond_wait(&m_cond, &m_condMutex); + } + } + + return result > 0; +} + } // namespace p2pool diff --git a/src/pow_hash.h b/src/pow_hash.h index ee2e0d8..b12a674 100644 --- a/src/pow_hash.h +++ b/src/pow_hash.h @@ -27,18 +27,29 @@ namespace p2pool { class p2pool; -class RandomX_Hasher +class RandomX_Hasher_Base +{ +public: + virtual ~RandomX_Hasher_Base() {} + + virtual void set_seed_async(const hash&) {} + virtual void set_old_seed(const hash&) {} + + virtual bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) = 0; +}; + +class RandomX_Hasher : public RandomX_Hasher_Base { public: explicit RandomX_Hasher(p2pool* pool); ~RandomX_Hasher(); - void set_seed_async(const hash& seed); + void set_seed_async(const hash& seed) override; void set_seed(const hash& seed); - void set_old_seed(const hash& seed); + void set_old_seed(const hash& seed) override; - bool calculate(const void* data, size_t size, const hash& seed, hash& result); + bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) override; private: @@ -70,4 +81,36 @@ private: std::atomic m_setSeedCounter; }; +class RandomX_Hasher_RPC : public RandomX_Hasher_Base +{ +public: + explicit RandomX_Hasher_RPC(p2pool* pool); + ~RandomX_Hasher_RPC(); + + bool calculate(const void* data, size_t size, uint64_t height, const hash& seed, hash& result) override; + +private: + static void loop(void* data); + + p2pool* m_pool; + + uv_mutex_t m_requestMutex; + uv_loop_t m_loop; + volatile bool m_loopStopped; + + uv_thread_t m_loopThread; + uv_mutex_t m_condMutex; + uv_cond_t m_cond; + + uv_async_t m_shutdownAsync; + uv_async_t m_kickTheLoopAsync; + + static void on_shutdown(uv_async_t* async) + { + RandomX_Hasher_RPC* server = reinterpret_cast(async->data); + uv_close(reinterpret_cast(&server->m_shutdownAsync), nullptr); + uv_close(reinterpret_cast(&server->m_kickTheLoopAsync), nullptr); + } +}; + } // namespace p2pool diff --git a/src/side_chain.cpp b/src/side_chain.cpp index d0bba61..4b67a96 100644 --- a/src/side_chain.cpp +++ b/src/side_chain.cpp @@ -429,7 +429,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector& missing_ } hash pow_hash; - if (!block.get_pow_hash(m_pool->hasher(), seed, pow_hash)) { + if (!block.get_pow_hash(m_pool->hasher(), block.m_txinGenHeight, seed, pow_hash)) { LOGWARN(3, "add_external_block: couldn't get PoW hash for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight << ". Ignoring it."); unsee_block(block); return true; diff --git a/src/stratum_server.cpp b/src/stratum_server.cpp index c350362..13c9169 100644 --- a/src/stratum_server.cpp +++ b/src/stratum_server.cpp @@ -661,7 +661,7 @@ void StratumServer::on_share_found(uv_work_t* req) } hash pow_hash; - if (!pool->calculate_hash(blob, blob_size, seed_hash, pow_hash)) { + if (!pool->calculate_hash(blob, blob_size, height, seed_hash, pow_hash)) { LOGWARN(3, "client " << static_cast(client->m_addrString) << " couldn't check share PoW"); share->m_result = SubmittedShare::Result::COULDNT_CHECK_POW; return; diff --git a/src/tcp_server.inl b/src/tcp_server.inl index 3a74083..87aa850 100644 --- a/src/tcp_server.inl +++ b/src/tcp_server.inl @@ -564,6 +564,7 @@ void TCPServer::loop(void* data) TCPServer* server = static_cast(data); uv_run(&server->m_loop, UV_RUN_DEFAULT); uv_loop_close(&server->m_loop); + LOGINFO(1, "event loop stopped"); server->m_loopStopped = true; } diff --git a/tests/src/pool_block_tests.cpp b/tests/src/pool_block_tests.cpp index 55dde77..72e73d6 100644 --- a/tests/src/pool_block_tests.cpp +++ b/tests/src/pool_block_tests.cpp @@ -91,7 +91,7 @@ TEST(pool_block, deserialize) hasher.set_seed(seed); hash pow_hash; - ASSERT_EQ(b.get_pow_hash(&hasher, seed, pow_hash), true); + ASSERT_EQ(b.get_pow_hash(&hasher, 0, seed, pow_hash), true); std::stringstream s; s << pow_hash;