rpc: further restrict and clean up get_transaction_pool

This commit is contained in:
selsta
2026-08-25 19:29:30 +02:00
parent bc933f920f
commit a496b4981c
10 changed files with 28 additions and 73 deletions
+4 -4
View File
@@ -1528,14 +1528,14 @@ namespace cryptonote
return m_mempool.have_tx(id, relay_category::legacy); return m_mempool.have_tx(id, relay_category::legacy);
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
bool core::get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data) const bool core::get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const
{ {
return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos, include_sensitive_data); return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos);
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
bool core::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos, bool include_sensitive) const bool core::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const
{ {
return m_mempool.get_pool_for_rpc(tx_infos, key_image_infos, include_sensitive); return m_mempool.get_pool_for_rpc(tx_infos, key_image_infos);
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
bool core::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t& current_height) const bool core::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t& current_height) const
+2 -6
View File
@@ -505,19 +505,15 @@ namespace cryptonote
/** /**
* @copydoc tx_memory_pool::get_pool_transactions_and_spent_keys_info * @copydoc tx_memory_pool::get_pool_transactions_and_spent_keys_info
* @param include_sensitive_txes include private transactions
*
* @note see tx_memory_pool::get_pool_transactions_and_spent_keys_info * @note see tx_memory_pool::get_pool_transactions_and_spent_keys_info
*/ */
bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_txes = false) const; bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const;
/** /**
* @copydoc tx_memory_pool::get_pool_for_rpc * @copydoc tx_memory_pool::get_pool_for_rpc
* @param include_sensitive include node-private fields (timing)
*
* @note see tx_memory_pool::get_pool_for_rpc * @note see tx_memory_pool::get_pool_for_rpc
*/ */
bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos, bool include_sensitive) const; bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const;
/** /**
* @copydoc tx_memory_pool::get_transactions_count * @copydoc tx_memory_pool::get_transactions_count
+11 -16
View File
@@ -1228,15 +1228,14 @@ namespace cryptonote
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
//TODO: investigate whether boolean return is appropriate //TODO: investigate whether boolean return is appropriate
bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data) const bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
const relay_category category = include_sensitive_data ? relay_category::all : relay_category::broadcasted; const size_t count = m_blockchain.get_txpool_tx_count(true);
const size_t count = m_blockchain.get_txpool_tx_count(include_sensitive_data);
tx_infos.reserve(count); tx_infos.reserve(count);
key_image_infos.reserve(count); key_image_infos.reserve(count);
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref *bd){ m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref *bd){
tx_info txi; tx_info txi;
txi.id_hash = epee::string_tools::pod_to_hex(txid); txi.id_hash = epee::string_tools::pod_to_hex(txid);
txi.tx_blob = blobdata(bd->data(), bd->size()); txi.tx_blob = blobdata(bd->data(), bd->size());
@@ -1257,16 +1256,14 @@ namespace cryptonote
txi.max_used_block_id_hash = epee::string_tools::pod_to_hex(meta.max_used_block_id); txi.max_used_block_id_hash = epee::string_tools::pod_to_hex(meta.max_used_block_id);
txi.last_failed_height = meta.last_failed_height; txi.last_failed_height = meta.last_failed_height;
txi.last_failed_id_hash = epee::string_tools::pod_to_hex(meta.last_failed_id); txi.last_failed_id_hash = epee::string_tools::pod_to_hex(meta.last_failed_id);
// In restricted mode we do not include this data: txi.receive_time = meta.receive_time;
txi.receive_time = include_sensitive_data ? meta.receive_time : 0;
txi.relayed = meta.relayed; txi.relayed = meta.relayed;
// In restricted mode we do not include this data: txi.last_relayed_time = meta.dandelionpp_stem ? 0 : meta.last_relayed_time;
txi.last_relayed_time = (include_sensitive_data && !meta.dandelionpp_stem) ? meta.last_relayed_time : 0;
txi.do_not_relay = meta.do_not_relay; txi.do_not_relay = meta.do_not_relay;
txi.double_spend_seen = meta.double_spend_seen; txi.double_spend_seen = meta.double_spend_seen;
tx_infos.push_back(std::move(txi)); tx_infos.push_back(std::move(txi));
return true; return true;
}, true, category); }, true, relay_category::all);
for (const key_images_container::value_type& kee : m_spent_key_images) { for (const key_images_container::value_type& kee : m_spent_key_images) {
const crypto::key_image& k_image = kee.first; const crypto::key_image& k_image = kee.first;
@@ -1275,7 +1272,7 @@ namespace cryptonote
ki.id_hash = epee::string_tools::pod_to_hex(k_image); ki.id_hash = epee::string_tools::pod_to_hex(k_image);
for (const crypto::hash& tx_id_hash : kei_image_set) for (const crypto::hash& tx_id_hash : kei_image_set)
{ {
if (m_blockchain.txpool_tx_matches_category(tx_id_hash, category)) if (m_blockchain.txpool_tx_matches_category(tx_id_hash, relay_category::all))
ki.txs_hashes.push_back(epee::string_tools::pod_to_hex(tx_id_hash)); ki.txs_hashes.push_back(epee::string_tools::pod_to_hex(tx_id_hash));
} }
@@ -1286,13 +1283,13 @@ namespace cryptonote
return true; return true;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
bool tx_memory_pool::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos, bool include_sensitive) const bool tx_memory_pool::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
tx_infos.reserve(m_blockchain.get_txpool_tx_count()); tx_infos.reserve(m_blockchain.get_txpool_tx_count());
key_image_infos.reserve(m_blockchain.get_txpool_tx_count()); key_image_infos.reserve(m_blockchain.get_txpool_tx_count());
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref *bd){ m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref *bd){
cryptonote::rpc::tx_in_pool txi; cryptonote::rpc::tx_in_pool txi;
txi.tx_hash = txid; txi.tx_hash = txid;
if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, txi.tx) : parse_and_validate_tx_from_blob(*bd, txi.tx))) if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, txi.tx) : parse_and_validate_tx_from_blob(*bd, txi.tx)))
@@ -1310,11 +1307,9 @@ namespace cryptonote
txi.max_used_block_hash = meta.max_used_block_id; txi.max_used_block_hash = meta.max_used_block_id;
txi.last_failed_block_height = meta.last_failed_height; txi.last_failed_block_height = meta.last_failed_height;
txi.last_failed_block_hash = meta.last_failed_id; txi.last_failed_block_hash = meta.last_failed_id;
// In restricted mode we do not include this data: txi.receive_time = meta.receive_time;
txi.receive_time = include_sensitive ? meta.receive_time : 0;
txi.relayed = meta.relayed; txi.relayed = meta.relayed;
// In restricted mode we do not include this data: txi.last_relayed_time = meta.dandelionpp_stem ? 0 : meta.last_relayed_time;
txi.last_relayed_time = (include_sensitive && !meta.dandelionpp_stem) ? meta.last_relayed_time : 0;
txi.do_not_relay = meta.do_not_relay; txi.do_not_relay = meta.do_not_relay;
txi.double_spend_seen = meta.double_spend_seen; txi.double_spend_seen = meta.double_spend_seen;
tx_infos.push_back(txi); tx_infos.push_back(txi);
+2 -7
View File
@@ -315,12 +315,9 @@ namespace cryptonote
* *
* @param tx_infos return-by-reference the transactions' information * @param tx_infos return-by-reference the transactions' information
* @param key_image_infos return-by-reference the spent key images' information * @param key_image_infos return-by-reference the spent key images' information
* @param include_sensitive_data return stempool, anonymity-pool, and unrelayed
* txes and fields that are sensitive to the node privacy
*
* @return true * @return true
*/ */
bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data = false) const; bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const;
/** /**
* @brief get information about all transactions and key images in the pool * @brief get information about all transactions and key images in the pool
@@ -329,11 +326,9 @@ namespace cryptonote
* *
* @param tx_infos [out] the transactions' information * @param tx_infos [out] the transactions' information
* @param key_image_infos [out] the spent key images' information * @param key_image_infos [out] the spent key images' information
* @param include_sensitive include fields that are sensitive to node privacy
*
* @return true * @return true
*/ */
bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos, bool include_sensitive) const; bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const;
/** /**
* @brief check for presence of key images in the pool * @brief check for presence of key images in the pool
+2 -6
View File
@@ -1358,14 +1358,10 @@ namespace cryptonote
{ {
RPC_TRACKER(get_transaction_pool); RPC_TRACKER(get_transaction_pool);
const bool restricted = m_restricted && ctx; size_t n_txes = m_core.get_pool_transactions_count(true);
const bool request_has_rpc_origin = ctx != NULL;
const bool allow_sensitive = !request_has_rpc_origin || !restricted;
size_t n_txes = m_core.get_pool_transactions_count(allow_sensitive);
if (n_txes > 0) if (n_txes > 0)
{ {
m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, allow_sensitive); m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images);
for (tx_info& txi : res.transactions) for (tx_info& txi : res.transactions)
txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(txi.tx_blob); txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(txi.tx_blob);
} }
+1 -1
View File
@@ -756,7 +756,7 @@ namespace rpc
void DaemonHandler::handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res) void DaemonHandler::handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res)
{ {
bool r = m_core.get_pool_for_rpc(res.transactions, res.key_images, !m_restricted); bool r = m_core.get_pool_for_rpc(res.transactions, res.key_images);
if (!r) res.status = Message::STATUS_FAILED; if (!r) res.status = Message::STATUS_FAILED;
else res.status = Message::STATUS_OK; else res.status = Message::STATUS_OK;
+2 -1
View File
@@ -39,9 +39,10 @@ namespace rpc
{ {
namespace namespace
{ {
constexpr std::array<std::string_view, 9> blocked_in_restricted_mode{{ constexpr std::array<std::string_view, 10> blocked_in_restricted_mode{{
"flush_txpool", "flush_txpool",
"get_peer_list", "get_peer_list",
"get_transaction_pool",
"mining_status", "mining_status",
"relay_tx", "relay_tx",
"save_bc", "save_bc",
+3 -30
View File
@@ -44,20 +44,12 @@
txpool_base::txpool_base() txpool_base::txpool_base()
: test_chain_unit_base() : test_chain_unit_base()
, m_broadcasted_tx_count(0)
, m_all_tx_count(0) , m_all_tx_count(0)
{ {
REGISTER_CALLBACK_METHOD(txpool_spend_key_public, increase_broadcasted_tx_count);
REGISTER_CALLBACK_METHOD(txpool_spend_key_public, increase_all_tx_count); REGISTER_CALLBACK_METHOD(txpool_spend_key_public, increase_all_tx_count);
REGISTER_CALLBACK_METHOD(txpool_spend_key_public, check_txpool_spent_keys); REGISTER_CALLBACK_METHOD(txpool_spend_key_public, check_txpool_spent_keys);
} }
bool txpool_base::increase_broadcasted_tx_count(cryptonote::core& /*c*/, size_t /*ev_index*/, const std::vector<test_event_entry>& /*events*/)
{
++m_broadcasted_tx_count;
return true;
}
bool txpool_base::increase_all_tx_count(cryptonote::core& /*c*/, size_t /*ev_index*/, const std::vector<test_event_entry>& /*events*/) bool txpool_base::increase_all_tx_count(cryptonote::core& /*c*/, size_t /*ev_index*/, const std::vector<test_event_entry>& /*events*/)
{ {
++m_all_tx_count; ++m_all_tx_count;
@@ -68,23 +60,7 @@ bool txpool_base::check_txpool_spent_keys(cryptonote::core& c, size_t /*ev_index
{ {
std::vector<cryptonote::tx_info> infos{}; std::vector<cryptonote::tx_info> infos{};
std::vector<cryptonote::spent_key_image_info> key_images{}; std::vector<cryptonote::spent_key_image_info> key_images{};
if (!c.get_pool_transactions_and_spent_keys_info(infos, key_images) || infos.size() != m_broadcasted_tx_count || key_images.size() != m_broadcasted_tx_count) if (!c.get_pool_transactions_and_spent_keys_info(infos, key_images) || infos.size() != m_all_tx_count || key_images.size() != m_all_tx_count)
{
MERROR("Failed broadcasted spent keys retrieval - Expected Broadcasted Count: " << m_broadcasted_tx_count << " Actual Info Count: " << infos.size() << " Actual Key Image Count: " << key_images.size());
return false;
}
infos.clear();
key_images.clear();
if (!c.get_pool_transactions_and_spent_keys_info(infos, key_images, false) || infos.size() != m_broadcasted_tx_count || key_images.size() != m_broadcasted_tx_count)
{
MERROR("Failed broadcasted spent keys retrieval - Expected Broadcasted Count: " << m_broadcasted_tx_count << " Actual Info Count: " << infos.size() << " Actual Key Image Count: " << key_images.size());
return false;
}
infos.clear();
key_images.clear();
if (!c.get_pool_transactions_and_spent_keys_info(infos, key_images, true) || infos.size() != m_all_tx_count || key_images.size() != m_all_tx_count)
{ {
MERROR("Failed all spent keys retrieval - Expected All Count: " << m_all_tx_count << " Actual Info Count: " << infos.size() << " Actual Key Image Count: " << key_images.size()); MERROR("Failed all spent keys retrieval - Expected All Count: " << m_all_tx_count << " Actual Info Count: " << infos.size() << " Actual Key Image Count: " << key_images.size());
return false; return false;
@@ -99,7 +75,6 @@ bool txpool_spend_key_public::generate(std::vector<test_event_entry>& events) co
DO_CALLBACK(events, "check_txpool_spent_keys"); DO_CALLBACK(events, "check_txpool_spent_keys");
MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0r); MAKE_TX(events, tx_0, miner_account, bob_account, send_amount, blk_0r);
DO_CALLBACK(events, "increase_broadcasted_tx_count");
DO_CALLBACK(events, "increase_all_tx_count"); DO_CALLBACK(events, "increase_all_tx_count");
DO_CALLBACK(events, "check_txpool_spent_keys"); DO_CALLBACK(events, "check_txpool_spent_keys");
@@ -245,7 +220,7 @@ bool txpool_double_spend_base::check_changed(cryptonote::core& c, const size_t e
{ {
std::vector<cryptonote::tx_info> infos{}; std::vector<cryptonote::tx_info> infos{};
std::vector<cryptonote::spent_key_image_info> key_images{}; std::vector<cryptonote::spent_key_image_info> key_images{};
if (!c.get_pool_transactions_and_spent_keys_info(infos, key_images, true) || infos.size() != m_all_hashes.size()) if (!c.get_pool_transactions_and_spent_keys_info(infos, key_images) || infos.size() != m_all_hashes.size())
{ {
MERROR("Unable to retrieve all txpool metadata"); MERROR("Unable to retrieve all txpool metadata");
return false; return false;
@@ -455,7 +430,7 @@ bool txpool_double_spend_base::check_changed(cryptonote::core& c, const size_t e
{ {
std::vector<cryptonote::rpc::tx_in_pool> infos{}; std::vector<cryptonote::rpc::tx_in_pool> infos{};
cryptonote::rpc::key_images_with_tx_hashes key_images{}; cryptonote::rpc::key_images_with_tx_hashes key_images{};
if (!c.get_pool_for_rpc(infos, key_images, true) || infos.size() != m_broadcasted_hashes.size() || key_images.size() != m_broadcasted_hashes.size()) if (!c.get_pool_for_rpc(infos, key_images) || infos.size() != m_broadcasted_hashes.size() || key_images.size() != m_broadcasted_hashes.size())
{ {
MERROR("Expected broadcasted rpc data to return " << m_broadcasted_hashes.size() << " but got " << infos.size() << " infos and " << key_images.size() << "key images"); MERROR("Expected broadcasted rpc data to return " << m_broadcasted_hashes.size() << " but got " << infos.size() << " infos and " << key_images.size() << "key images");
return false; return false;
@@ -553,7 +528,6 @@ bool txpool_double_spend_local::generate(std::vector<test_event_entry>& events)
SET_EVENT_VISITOR_SETT(events, 0); SET_EVENT_VISITOR_SETT(events, 0);
DO_CALLBACK(events, "timestamp_change_pause"); DO_CALLBACK(events, "timestamp_change_pause");
events.push_back(tx_0); events.push_back(tx_0);
DO_CALLBACK(events, "increase_broadcasted_tx_count");
DO_CALLBACK(events, "check_txpool_spent_keys"); DO_CALLBACK(events, "check_txpool_spent_keys");
DO_CALLBACK(events, "mark_timestamp_change"); DO_CALLBACK(events, "mark_timestamp_change");
DO_CALLBACK(events, "check_new_broadcasted"); DO_CALLBACK(events, "check_new_broadcasted");
@@ -624,7 +598,6 @@ bool txpool_stem_loop::generate(std::vector<test_event_entry>& events) const
DO_CALLBACK(events, "check_new_hidden"); DO_CALLBACK(events, "check_new_hidden");
DO_CALLBACK(events, "timestamp_change_pause"); DO_CALLBACK(events, "timestamp_change_pause");
events.push_back(tx_0); events.push_back(tx_0);
DO_CALLBACK(events, "increase_broadcasted_tx_count");
DO_CALLBACK(events, "check_txpool_spent_keys"); DO_CALLBACK(events, "check_txpool_spent_keys");
DO_CALLBACK(events, "mark_timestamp_change"); DO_CALLBACK(events, "mark_timestamp_change");
DO_CALLBACK(events, "check_new_broadcasted"); DO_CALLBACK(events, "check_new_broadcasted");
-2
View File
@@ -44,13 +44,11 @@ enum class relay_test
class txpool_base : public test_chain_unit_base class txpool_base : public test_chain_unit_base
{ {
size_t m_broadcasted_tx_count;
size_t m_all_tx_count; size_t m_all_tx_count;
public: public:
txpool_base(); txpool_base();
bool increase_broadcasted_tx_count(cryptonote::core& c, size_t /*ev_index*/, const std::vector<test_event_entry>& events);
bool increase_all_tx_count(cryptonote::core& c, size_t /*ev_index*/, const std::vector<test_event_entry>& events); bool increase_all_tx_count(cryptonote::core& c, size_t /*ev_index*/, const std::vector<test_event_entry>& events);
bool check_txpool_spent_keys(cryptonote::core& c, size_t /*ev_index*/, const std::vector<test_event_entry>& events); bool check_txpool_spent_keys(cryptonote::core& c, size_t /*ev_index*/, const std::vector<test_event_entry>& events);
}; };
+1
View File
@@ -88,6 +88,7 @@ TEST(ZmqRestrictedMethods, BasicCoverage)
{ {
EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("flush_txpool")); EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("flush_txpool"));
EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("get_peer_list")); EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("get_peer_list"));
EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("get_transaction_pool"));
EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("mining_status")); EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("mining_status"));
EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("relay_tx")); EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("relay_tx"));
EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("save_bc")); EXPECT_TRUE(cryptonote::rpc::is_blocked_in_restricted_mode("save_bc"));