From 054b4c7f412013e33c80efa8e0f0f33944572a11 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 27 Nov 2019 00:09:25 +0000 Subject: [PATCH] protocol: request txpool contents when synced A newly synced Alice sends a (typically quite small) list of txids in the local tpxool to a random peer Bob, who then uses the existing tx relay system to send Alice any tx in his txpool which is not in the list Alice sent --- src/cryptonote_core/cryptonote_core.cpp | 6 ++ src/cryptonote_core/cryptonote_core.h | 9 +++ src/cryptonote_core/tx_pool.cpp | 33 +++++++++ src/cryptonote_core/tx_pool.h | 5 ++ .../cryptonote_protocol_defs.h | 18 +++++ .../cryptonote_protocol_handler.h | 4 ++ .../cryptonote_protocol_handler.inl | 68 +++++++++++++++++++ tests/core_proxy/core_proxy.h | 2 + tests/unit_tests/node_server.cpp | 2 + 9 files changed, 147 insertions(+) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 8b1613b4b..de60367e6 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1946,6 +1946,12 @@ namespace cryptonote { m_blockchain_storage.flush_invalid_blocks(); } + //----------------------------------------------------------------------------------------------- + bool core::get_txpool_complement(const std::vector &hashes, std::vector &txes) + { + return m_mempool.get_complement(hashes, txes); + } + //----------------------------------------------------------------------------------------------- bool core::update_blockchain_pruning() { return m_blockchain_storage.update_blockchain_pruning(); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 5dec890e8..6fb2f68ab 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -864,6 +864,15 @@ namespace cryptonote */ void flush_invalid_blocks(); + /** + * @brief returns the set of transactions in the txpool which are not in the argument + * + * @param hashes hashes of transactions to exclude from the result + * + * @return true iff success, false otherwise + */ + bool get_txpool_complement(const std::vector &hashes, std::vector &txes); + private: /** diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 1bc475879..8c8e43c90 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -594,6 +594,39 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- + bool tx_memory_pool::get_complement(const std::vector &hashes, std::vector &txes) const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + CRITICAL_REGION_LOCAL1(m_blockchain); + + m_blockchain.for_all_txpool_txes([this, &hashes, &txes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) { + const auto relay_method = meta.get_relay_method(); + if (relay_method != relay_method::block && relay_method != relay_method::fluff) + return true; + const auto i = std::find(hashes.begin(), hashes.end(), txid); + if (i == hashes.end()) + { + cryptonote::blobdata bd; + try + { + if (!m_blockchain.get_txpool_tx_blob(txid, bd, cryptonote::relay_category::broadcasted)) + { + MERROR("Failed to get blob for txpool transaction " << txid); + return true; + } + txes.emplace_back(std::move(bd)); + } + catch (const std::exception &e) + { + MERROR("Failed to get blob for txpool transaction " << txid << ": " << e.what()); + return true; + } + } + return true; + }, false); + return true; + } + //--------------------------------------------------------------------------------- void tx_memory_pool::on_idle() { m_remove_stuck_tx_interval.do_call([this](){return remove_stuck_transactions();}); diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index f716440ad..ca0e50415 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -441,6 +441,11 @@ namespace cryptonote */ bool get_transaction_info(const crypto::hash &txid, tx_details &td) const; + /** + * @brief get transactions not in the passed set + */ + bool get_complement(const std::vector &hashes, std::vector &txes) const; + private: /** diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index ee7e69eb7..f809bff74 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -353,5 +353,23 @@ namespace cryptonote }; typedef epee::misc_utils::struct_init request; }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_GET_TXPOOL_COMPLEMENT + { + const static int ID = BC_COMMANDS_POOL_BASE + 10; + + struct request_t + { + std::vector hashes; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(hashes) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init request; + }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index ddbd45a61..c6aa2b71b 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -92,6 +92,7 @@ namespace cryptonote HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry) HANDLE_NOTIFY_T2(NOTIFY_NEW_FLUFFY_BLOCK, &cryptonote_protocol_handler::handle_notify_new_fluffy_block) HANDLE_NOTIFY_T2(NOTIFY_REQUEST_FLUFFY_MISSING_TX, &cryptonote_protocol_handler::handle_request_fluffy_missing_tx) + HANDLE_NOTIFY_T2(NOTIFY_GET_TXPOOL_COMPLEMENT, &cryptonote_protocol_handler::handle_notify_get_txpool_complement) END_INVOKE_MAP2() bool on_idle(); @@ -127,6 +128,7 @@ namespace cryptonote int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); int handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context); int handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context); + int handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context); //----------------- i_bc_protocol_layout --------------------------------------- virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context); @@ -147,6 +149,7 @@ namespace cryptonote int try_add_next_blocks(cryptonote_connection_context &context); void notify_new_stripe(cryptonote_connection_context &context, uint32_t stripe); void skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const; + bool request_txpool_complement(cryptonote_connection_context &context); t_core& m_core; @@ -156,6 +159,7 @@ namespace cryptonote std::atomic m_synchronized; std::atomic m_stopping; std::atomic m_no_sync; + std::atomic m_ask_for_txpool_complement; boost::mutex m_sync_lock; block_queue m_block_queue; epee::math_helper::once_a_time_seconds<30> m_idle_peer_kicker; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index d11f198aa..aad43c586 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -83,6 +83,7 @@ namespace cryptonote m_p2p(p_net_layout), m_syncronized_connections_count(0), m_synchronized(offline), + m_ask_for_txpool_complement(true), m_stopping(false), m_no_sync(false) @@ -885,6 +886,34 @@ namespace cryptonote } //------------------------------------------------------------------------------------------------------------------------ template + int t_cryptonote_protocol_handler::handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context) + { + MLOG_P2P_MESSAGE("Received NOTIFY_GET_TXPOOL_COMPLEMENT (" << arg.hashes.size() << " txes)"); + + std::vector> local_blocks; + std::vector local_txs; + + std::vector txes; + if (!m_core.get_txpool_complement(arg.hashes, txes)) + { + LOG_ERROR_CCONTEXT("failed to get txpool complement"); + return 1; + } + + NOTIFY_NEW_TRANSACTIONS::request new_txes; + new_txes.txs = std::move(txes); + + MLOG_P2P_MESSAGE + ( + "-->>NOTIFY_NEW_TRANSACTIONS: " + << ", txs.size()=" << new_txes.txs.size() + ); + + post_notify(new_txes, context); + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template int t_cryptonote_protocol_handler::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_NEW_TRANSACTIONS (" << arg.txs.size() << " txes)"); @@ -2201,6 +2230,27 @@ skip: } m_core.safesyncmode(true); m_p2p->clear_used_stripe_peers(); + + // ask for txpool complement from any suitable node if we did not yet + val_expected = true; + if (m_ask_for_txpool_complement.compare_exchange_strong(val_expected, false)) + { + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool + { + if(context.m_state < cryptonote_connection_context::state_synchronizing) + { + MDEBUG(context << "not ready, ignoring"); + return true; + } + if (!request_txpool_complement(context)) + { + MERROR(context << "Failed to request txpool complement"); + return true; + } + return false; + }); + } + return true; } //------------------------------------------------------------------------------------------------------------------------ @@ -2354,6 +2404,21 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template + bool t_cryptonote_protocol_handler::request_txpool_complement(cryptonote_connection_context &context) + { + NOTIFY_GET_TXPOOL_COMPLEMENT::request r = {}; + if (!m_core.get_pool_transaction_hashes(r.hashes, false)) + { + MERROR("Failed to get txpool hashes"); + return false; + } + MLOG_P2P_MESSAGE("-->>NOTIFY_GET_TXPOOL_COMPLEMENT: hashes.size()=" << r.hashes.size() ); + post_notify(r, context); + MLOG_PEER_STATE("requesting txpool complement"); + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template std::string t_cryptonote_protocol_handler::get_peers_overview() const { std::stringstream ss; @@ -2463,7 +2528,10 @@ skip: MINFO("Target height decreasing from " << previous_target << " to " << target); m_core.set_target_blockchain_height(target); if (target == 0 && context.m_state > cryptonote_connection_context::state_before_handshake && !m_stopping) + { MCWARNING("global", "monerod is now disconnected from the network"); + m_ask_for_txpool_complement = true; + } } m_block_queue.flush_spans(context.m_connection_id, false); diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 8732c85cc..d09eb31c9 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -110,5 +110,7 @@ namespace tests bool pad_transactions() const { return false; } uint32_t get_blockchain_pruning_seed() const { return 0; } bool prune_blockchain(uint32_t pruning_seed) const { return true; } + bool get_txpool_complement(const std::vector &hashes, std::vector &txes) { return false; } + bool get_pool_transaction_hashes(std::vector& txs, bool include_unrelayed_txes = true) const { return false; } }; } diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index c92f70b97..dff223ed3 100644 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -91,6 +91,8 @@ public: bool prune_blockchain(uint32_t pruning_seed = 0) { return true; } bool is_within_compiled_block_hash_area(uint64_t height) const { return false; } bool has_block_weights(uint64_t height, uint64_t nblocks) const { return false; } + bool get_txpool_complement(const std::vector &hashes, std::vector &txes) { return false; } + bool get_pool_transaction_hashes(std::vector& txs, bool include_unrelayed_txes = true) const { return false; } void stop() {} };