From 743f7d3d036cc88de5afc5fa00dbbe732d55753e Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Mon, 11 Dec 2023 21:03:48 +0100 Subject: [PATCH] Refactor --- src/node/sv2_template_provider.cpp | 146 ++++++----------------- src/node/sv2_template_provider.h | 17 +-- src/test/fuzz/sv2_template_provider.cpp | 18 +-- src/test/sv2_template_provider_tests.cpp | 35 +----- 4 files changed, 50 insertions(+), 166 deletions(-) diff --git a/src/node/sv2_template_provider.cpp b/src/node/sv2_template_provider.cpp index be49744263f5d3..49b3e1e1013d65 100644 --- a/src/node/sv2_template_provider.cpp +++ b/src/node/sv2_template_provider.cpp @@ -131,7 +131,11 @@ void Sv2TemplateProvider::ThreadSv2Handler() WAIT_LOCK(g_best_block_mutex, lock); auto checktime = std::chrono::steady_clock::now() + std::chrono::milliseconds(50); g_best_block_cv.wait_until(lock, checktime); - return m_best_prev_hash.m_prev_hash != g_best_block; + if (m_best_prev_hash.m_prev_hash != g_best_block) { + m_best_prev_hash.m_prev_hash = g_best_block; + return true; + } + return false; }(); // TODO: this is triggered far more often than necessary, and should @@ -148,31 +152,14 @@ void Sv2TemplateProvider::ThreadSv2Handler() // Build a new best template, best prev hash and update the block cache. should_make_template = true; template_last_update = mempool_last_update; - } else if (timer.trigger() && mempool_last_update > template_last_update && m_sv2_clients.size() > 0) { + } else if (timer.trigger() && mempool_last_update > template_last_update && !m_sv2_clients.empty()) { should_make_template = true; - // for (const auto& client : m_sv2_clients) { - // ++m_template_id; - // Sv2Client& c = *client.get(); - // should_make_template = true; - // auto new_template = BuildNewTemplate(false, c.m_coinbase_tx_outputs_size, m_template_id); - // m_block_cache.insert({m_template_id, std::move(new_template.block_template)}); - // if (!SendTemplate(*client.get(), new_template.new_template)) { - // client->m_disconnect_flag = true; - // continue; - // } - // } } if (should_make_template) { - ++m_template_id; - auto new_work_set = BuildNewWorkSet(m_default_future_templates, m_default_coinbase_tx_additional_output_size, m_template_id); - m_best_new_template = std::move(new_work_set.new_template); - m_best_prev_hash = std::move(new_work_set.prev_hash); - m_block_cache.insert({m_template_id, std::move(new_work_set.block_template)}); - // Update all clients with the new template and prev hash. for (const auto& client : m_sv2_clients) { - if (!SendWork(*client.get(), m_best_new_template, m_best_prev_hash, m_block_cache, m_template_id)) { + if (!SendWork(*client.get(), /*send_new_prevhash=*/best_block_changed)) { client->m_disconnect_flag = true; continue; } @@ -239,7 +226,7 @@ void Sv2TemplateProvider::ThreadSv2Handler() for (auto& m : sv2_msgs) { - ProcessSv2Message(m, *client.get(), m_best_new_template, m_best_prev_hash, m_block_cache, m_template_id); + ProcessSv2Message(m, *client.get()); } } } catch (const std::exception& e) { @@ -295,7 +282,7 @@ void Sv2TemplateProvider::ProcessSv2Noise(Sv2Client& client, Span buf } } -Sv2TemplateProvider::NewWorkSet Sv2TemplateProvider::BuildNewWorkSet(bool future_template, unsigned int coinbase_output_max_additional_size, uint64_t template_id) +Sv2TemplateProvider::NewWorkSet Sv2TemplateProvider::BuildNewWorkSet(bool future_template, unsigned int coinbase_output_max_additional_size) { node::BlockAssembler::Options options; @@ -307,71 +294,41 @@ Sv2TemplateProvider::NewWorkSet Sv2TemplateProvider::BuildNewWorkSet(bool future auto blocktemplate = node::BlockAssembler(m_chainman.ActiveChainstate(), &m_mempool, options).CreateNewBlock(CScript()); LogPrintLevel(BCLog::SV2, BCLog::Level::Trace, "Assemble template: %.2fms\n", Ticks(SteadyClock::now() - time_start)); - node::Sv2NewTemplateMsg new_template{blocktemplate->block, template_id, future_template}; - node::Sv2SetNewPrevHashMsg set_new_prev_hash{blocktemplate->block, template_id}; + node::Sv2NewTemplateMsg new_template{blocktemplate->block, m_template_id, future_template}; + node::Sv2SetNewPrevHashMsg set_new_prev_hash{blocktemplate->block, m_template_id}; return NewWorkSet { new_template, std::move(blocktemplate), set_new_prev_hash}; } -// Sv2TemplateProvider::ReducedWorkSet Sv2TemplateProvider::BuildNewTemplate(bool future_template, unsigned int coinbase_output_max_additional_size, uint64_t template_id) -// { -// node::BlockAssembler::Options options; - -// // Reducing the size of nBlockMaxWeight by the coinbase output additional size allows the miner extra weighted bytes in their coinbase space. -// options.nBlockMaxWeight = MAX_BLOCK_WEIGHT - coinbase_output_max_additional_size; -// options.blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); - -// const auto time_start{SteadyClock::now()}; -// auto blocktemplate = node::BlockAssembler(m_chainman.ActiveChainstate(), &m_mempool, options).CreateNewBlock(CScript()); -// LogPrintLevel(BCLog::SV2, BCLog::Level::Trace, "Assemble template: %.2fms\n", -// Ticks(SteadyClock::now() - time_start)); - -// node::Sv2NewTemplateMsg new_template{blocktemplate->block, template_id, future_template}; -// return ReducedWorkSet { new_template, std::move(blocktemplate)}; -// } - -bool Sv2TemplateProvider::SendTemplate(const Sv2Client& client, const node::Sv2NewTemplateMsg& best_template) +bool Sv2TemplateProvider::SendWork(const Sv2Client& client, bool send_new_prevhash) { - LogPrintLevel(BCLog::SV2, BCLog::Level::Debug, "Send 0x71 NewTemplate (id=%d)\n", best_template.m_template_id); + // The current implementation doesn't create templates for future empty + // or speculative blocks. Despite that, we first send NewTemplate with + // future_template set to true, followed by SetNewPrevHash. We do this + // both when first connecting and when a new block is found. + // + // When the template is update to take newer mempool transactions into + // account, we set future_template to false and don't send SetNewPrevHash. + + // TODO: reuse template_id for clients with the same m_default_coinbase_tx_additional_output_size + ++m_template_id; + auto new_work_set = BuildNewWorkSet(/*future_template=*/send_new_prevhash, client.m_coinbase_tx_outputs_size); + m_block_cache.insert({m_template_id, std::move(new_work_set.block_template)}); try { - auto msg = node::Sv2NetMsg{best_template}; + auto msg = node::Sv2NetMsg{new_work_set.new_template}; auto msg_buf = BuildEncryptedHeader(msg, *client.m_noise.get()); if (!SendBuf(client, msg_buf)) { - LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Error sending best NewTemplate message\n"); + LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Error sending NewTemplate message\n"); return false; } } catch (const std::exception& e) { - LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Failed to serialize best new template: %s\n", e.what()); + LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Failed to serialize new template: %s\n", e.what()); return false; } - return true; -} - -bool Sv2TemplateProvider::SendWork(const Sv2Client& client, const node::Sv2NewTemplateMsg& best_template, const node::Sv2SetNewPrevHashMsg& best_prev_hash, BlockCache& block_cache, uint64_t& template_id) -{ - if (client.m_coinbase_tx_outputs_size > 0) { - ++template_id; - auto new_work_set = BuildNewWorkSet(m_default_future_templates, client.m_coinbase_tx_outputs_size, template_id); - block_cache.insert({template_id, std::move(new_work_set.block_template)}); - - LogPrintLevel(BCLog::SV2, BCLog::Level::Debug, "Send 0x71 NewTemplate (id=%d)\n", template_id); - - try { - auto msg = node::Sv2NetMsg{new_work_set.new_template}; - auto msg_buf = BuildEncryptedHeader(msg, *client.m_noise.get()); - - if (!SendBuf(client, msg_buf)) { - LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Error sending Sv2NewTemplate message\n"); - return false; - } - } catch (const std::exception& e) { - LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Failed to serialize new template: %s\n", e.what()); - return false; - } - + if (send_new_prevhash) { LogPrintLevel(BCLog::SV2, BCLog::Level::Debug, "Send 0x20 SetNewPrevHash\n"); try { @@ -379,43 +336,13 @@ bool Sv2TemplateProvider::SendWork(const Sv2Client& client, const node::Sv2NewTe auto msg_buf = BuildEncryptedHeader(msg, *client.m_noise.get()); if (!SendBuf(client, msg_buf)) { - LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Error sending Sv2SetNewPrevHash message\n"); + LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Error sending SetNewPrevHash message\n"); return false; } } catch (const std::exception& e) { LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Failed to serialize new prev hash: %s\n", e.what()); return false; } - } else { - LogPrintLevel(BCLog::SV2, BCLog::Level::Debug, "Send 0x71 NewTemplate (id=%d)\n", template_id); - - try { - auto msg = node::Sv2NetMsg{best_template}; - auto msg_buf = BuildEncryptedHeader(msg, *client.m_noise.get()); - - if (!SendBuf(client, msg_buf)) { - LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Error sending best Sv2NewTemplate message\n"); - return false; - } - } catch (const std::exception& e) { - LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Failed to serialize best new template: %s\n", e.what()); - return false; - } - - try { - auto msg = node::Sv2NetMsg{best_prev_hash}; - auto msg_buf = BuildEncryptedHeader(msg, *client.m_noise.get()); - - LogPrintLevel(BCLog::SV2, BCLog::Level::Debug, "Send 0x20 SetNewPrevHash\n"); - - if (!SendBuf(client, msg_buf)) { - LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Error sending best Sv2SetNewPrevHash message\n"); - return false; - } - } catch (const std::exception& e) { - LogPrintLevel(BCLog::SV2, BCLog::Level::Error, "Failed to serialize best new prev hash: %s\n", e.what()); - return false; - } } return true; @@ -435,7 +362,7 @@ Sock::EventsPerSock Sv2TemplateProvider::GenerateWaitSockets(const std::shared_p return events_per_sock; } -void Sv2TemplateProvider::ProcessSv2Message(const node::Sv2NetMsg& sv2_net_msg, Sv2Client& client, const node::Sv2NewTemplateMsg& best_new_template, const node::Sv2SetNewPrevHashMsg& best_prev_hash, BlockCache& block_cache, uint64_t& template_id) +void Sv2TemplateProvider::ProcessSv2Message(const node::Sv2NetMsg& sv2_net_msg, Sv2Client& client) { DataStream ss (sv2_net_msg.m_msg); @@ -544,7 +471,8 @@ void Sv2TemplateProvider::ProcessSv2Message(const node::Sv2NetMsg& sv2_net_msg, client.m_coinbase_tx_outputs_size = coinbase_output_data_size.m_coinbase_output_max_additional_size; - if (!SendWork(client, best_new_template, best_prev_hash, block_cache, template_id)) { + // Send new template and prevout + if (!SendWork(client, /*send_new_prevhash=*/true)) { return; } @@ -566,8 +494,8 @@ void Sv2TemplateProvider::ProcessSv2Message(const node::Sv2NetMsg& sv2_net_msg, return; } - auto cached_block = block_cache.find(submit_solution.m_template_id); - if (cached_block != block_cache.end()) { + auto cached_block = m_block_cache.find(submit_solution.m_template_id); + if (cached_block != m_block_cache.end()) { CBlock& block = (*cached_block->second).block; auto coinbase_tx = CTransaction(std::move(submit_solution.m_coinbase_tx)); @@ -606,8 +534,8 @@ void Sv2TemplateProvider::ProcessSv2Message(const node::Sv2NetMsg& sv2_net_msg, return; } - auto cached_block = block_cache.find(request_tx_data.m_template_id); - if (cached_block != block_cache.end()) { + auto cached_block = m_block_cache.find(request_tx_data.m_template_id); + if (cached_block != m_block_cache.end()) { CBlock& block = (*cached_block->second).block; std::vector witness_reserve_value; @@ -661,6 +589,8 @@ std::vector txs; std::vector Sv2TemplateProvider::BuildEncryptedHeader(const node::Sv2NetMsg& net_msg, Sv2NoiseSession& noise) { + LogPrintLevel(BCLog::SV2, BCLog::Level::Trace, "To encrypt: %s", HexStr(net_msg.m_msg)); + DataStream ss_header{}; ss_header << net_msg.m_sv2_header; diff --git a/src/node/sv2_template_provider.h b/src/node/sv2_template_provider.h index 107a6dc7b2fcb6..a0b418f7e3ab56 100644 --- a/src/node/sv2_template_provider.h +++ b/src/node/sv2_template_provider.h @@ -130,15 +130,11 @@ class Sv2TemplateProvider Clients m_sv2_clients; /** - * The current best known new template id. This is incremented on creating new template. + * The most recent template id. This is incremented on creating new template, + * which happens for each connected client. */ uint64_t m_template_id; - /** - * The best known template to send to all sv2 clients. - */ - node::Sv2NewTemplateMsg m_best_new_template; - /** * The current best known SetNewPrevHash that references the current best known * block hash in the network. @@ -201,7 +197,7 @@ class Sv2TemplateProvider /** * Main handler for all received stratum v2 messages. */ - void ProcessSv2Message(const node::Sv2NetMsg& sv2_header, Sv2Client& client, const node::Sv2NewTemplateMsg& best_new_template, const node::Sv2SetNewPrevHashMsg& best_prev_hash, BlockCache& block_cache, uint64_t& template_id); + void ProcessSv2Message(const node::Sv2NetMsg& sv2_header, Sv2Client& client); /** * A helper function to process incoming noise messages to either progress a handshake or encrypt/decrypt in secure communication. @@ -242,15 +238,12 @@ class Sv2TemplateProvider /** * Builds a NewWorkSet that contains the Sv2NewTemplateMsg, a new full block and a Sv2SetNewPrevHashMsg that are all linked to the same work. */ - [[nodiscard]] NewWorkSet BuildNewWorkSet(bool future_template, unsigned int coinbase_output_max_additional_size, uint64_t template_id); + [[nodiscard]] NewWorkSet BuildNewWorkSet(bool future_template, unsigned int coinbase_output_max_additional_size); - // [[nodiscard]] ReducedWorkSet BuildNewTemplate(bool future_template, unsigned int coinbase_output_max_additional_size, uint64_t template_id); /** * Sends the best NewTemplate and SetNewPrevHash to a client. */ - [[nodiscard]] bool SendWork(const Sv2Client& client, const node::Sv2NewTemplateMsg& new_template, const node::Sv2SetNewPrevHashMsg& prev_hash, BlockCache& block_cache, uint64_t& template_id); - - [[nodiscard]] bool SendTemplate(const Sv2Client& client, const node::Sv2NewTemplateMsg& new_template); + [[nodiscard]] bool SendWork(const Sv2Client& client, bool send_new_prevhash); /** * Generates the socket events for each Sv2Client socket and the main listening socket. diff --git a/src/test/fuzz/sv2_template_provider.cpp b/src/test/fuzz/sv2_template_provider.cpp index 227375344534ff..3fadfb92726808 100644 --- a/src/test/fuzz/sv2_template_provider.cpp +++ b/src/test/fuzz/sv2_template_provider.cpp @@ -31,29 +31,17 @@ FUZZ_TARGET(sv2_template_provider) auto setup_conn = node::Sv2NetMsg(std::move(header), std::move(random_bytes)); template_provider.ProcessSv2Message(setup_conn, - client, - best_new_template, - best_prev_hash, - block_cache, - template_id); + client); client.m_setup_connection_confirmed = true; header = node::Sv2NetHeader{node::Sv2MsgType::COINBASE_OUTPUT_DATA_SIZE, static_cast(random_bytes.size())}; auto coinbase_output_size = node::Sv2NetMsg(std::move(header), std::move(random_bytes)); template_provider.ProcessSv2Message(coinbase_output_size, - client, - best_new_template, - best_prev_hash, - block_cache, - template_id); + client); client.m_coinbase_output_data_size_recv = true; header = node::Sv2NetHeader{node::Sv2MsgType::SUBMIT_SOLUTION, static_cast(random_bytes.size())}; auto submit_solution = node::Sv2NetMsg(std::move(header), std::move(random_bytes)); template_provider.ProcessSv2Message(submit_solution, - client, - best_new_template, - best_prev_hash, - block_cache, - template_id); + client); }; diff --git a/src/test/sv2_template_provider_tests.cpp b/src/test/sv2_template_provider_tests.cpp index 3e8c916b63005f..74c28816b2c7ca 100644 --- a/src/test/sv2_template_provider_tests.cpp +++ b/src/test/sv2_template_provider_tests.cpp @@ -34,17 +34,8 @@ static void connect(Sv2TemplateProvider& template_provider, Sv2Client& client, s node::Sv2NetHeader setup_conn_header = node::Sv2NetHeader{node::Sv2MsgType::SETUP_CONNECTION, static_cast(bytes_copy.size())}; node::Sv2NetMsg sv2_msg = node::Sv2NetMsg{std::move(setup_conn_header), std::move(bytes_copy)}; - node::Sv2NewTemplateMsg best_new_template; - node::Sv2SetNewPrevHashMsg best_prev_hash; - std::map> block_cache; - uint64_t template_id{0}; - template_provider.ProcessSv2Message(sv2_msg, - client, - best_new_template, - best_prev_hash, - block_cache, - template_id); + client); BOOST_CHECK(client.m_setup_connection_confirmed); BOOST_CHECK(!client.m_disconnect_flag); @@ -61,12 +52,6 @@ BOOST_AUTO_TEST_CASE(Sv2TemplateProvider_Connection_test) BOOST_CHECK(!client.m_disconnect_flag); BOOST_CHECK(!client.m_setup_connection_confirmed); - std::map> block_cache; - uint64_t template_id{0}; - - node::Sv2NewTemplateMsg best_new_template; - node::Sv2SetNewPrevHashMsg best_prev_hash; - // Check that failure to deserialize the body of the message into a SetupConnection // results in a disconnection for the client. node::Sv2NetHeader setup_conn_header{node::Sv2MsgType::SETUP_CONNECTION, 0}; @@ -74,11 +59,7 @@ BOOST_AUTO_TEST_CASE(Sv2TemplateProvider_Connection_test) node::Sv2NetMsg sv2_msg{std::move(setup_conn_header), std::move(empty_body)}; template_provider.ProcessSv2Message(sv2_msg, - client, - best_new_template, - best_prev_hash, - block_cache, - template_id); + client); BOOST_CHECK(client.m_disconnect_flag); // Check that if a client is already connected and sends a SetupConnection message twice, @@ -87,11 +68,7 @@ BOOST_AUTO_TEST_CASE(Sv2TemplateProvider_Connection_test) client.m_disconnect_flag = false; template_provider.ProcessSv2Message(sv2_msg, - client, - best_new_template, - best_prev_hash, - block_cache, - template_id); + client); BOOST_CHECK(!client.m_disconnect_flag); // Make a succesful connection @@ -106,11 +83,7 @@ BOOST_AUTO_TEST_CASE(Sv2TemplateProvider_Connection_test) sv2_msg = node::Sv2NetMsg{std::move(setup_conn_header), std::move(setup_conn_bytes)}; BOOST_CHECK_THROW(template_provider.ProcessSv2Message(sv2_msg, - client, - best_new_template, - best_prev_hash, - block_cache, - template_id), std::runtime_error); + client), std::runtime_error); BOOST_CHECK(!client.m_setup_connection_confirmed);