From c4af33ededc8ca2394969f1bd8a1ea1cc5da160b Mon Sep 17 00:00:00 2001 From: j-berman Date: Tue, 13 Dec 2022 16:08:56 -0800 Subject: Enforce restricted # pool txs served via RPC + optimize chunked reqs - `/getblocks.bin` respects the `RESTRICTED_TX_COUNT` (=100) when returning pool txs via a restricted RPC daemon. - A restricted RPC daemon includes a max of `RESTRICTED_TX_COUNT` txs in the `added_pool_txs` field, and returns any remaining pool hashes in the `remaining_added_pool_txids` field. The client then requests the remaining txs via `/gettransactions` in chunks. - `/gettransactions` no longer does expensive no-ops for ALL pool txs if the client requests a subset of pool txs. Instead it searches for the txs the client explicitly requests. - Reset `m_pool_info_query_time` when a user: (1) rescans the chain (so the wallet re-requests the whole pool) (2) changes the daemon their wallets points to (a new daemon would have a different view of the pool) - `/getblocks.bin` respects the `req.prune` field when returning pool txs. - Pool extension fields in response to `/getblocks.bin` are optional with default 0'd values. --- src/rpc/core_rpc_server.cpp | 55 +++++++++++++-------------------- src/rpc/core_rpc_server_commands_defs.h | 16 +++++++--- 2 files changed, 34 insertions(+), 37 deletions(-) (limited to 'src/rpc') diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 0d48b6e7a..092cff753 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -630,31 +630,34 @@ namespace cryptonote const bool restricted = m_restricted && ctx; const bool request_has_rpc_origin = ctx != NULL; const bool allow_sensitive = !request_has_rpc_origin || !restricted; + const size_t max_tx_count = restricted ? RESTRICTED_TRANSACTIONS_COUNT : std::numeric_limits::max(); bool incremental; - std::vector added_pool_txs; - bool success = m_core.get_pool_info((time_t)req.pool_info_since, allow_sensitive, added_pool_txs, res.removed_pool_txids, incremental); + std::vector> added_pool_txs; + bool success = m_core.get_pool_info((time_t)req.pool_info_since, allow_sensitive, max_tx_count, added_pool_txs, res.remaining_added_pool_txids, res.removed_pool_txids, incremental); if (success) { res.added_pool_txs.clear(); if (m_rpc_payment) { - CHECK_PAYMENT_SAME_TS(req, res, added_pool_txs.size() * COST_PER_TX + res.removed_pool_txids.size() * COST_PER_POOL_HASH); + CHECK_PAYMENT_SAME_TS(req, res, added_pool_txs.size() * COST_PER_TX + (res.remaining_added_pool_txids.size() + res.removed_pool_txids.size()) * COST_PER_POOL_HASH); } - for (auto tx_detail: added_pool_txs) + for (const auto &added_pool_tx: added_pool_txs) { COMMAND_RPC_GET_BLOCKS_FAST::pool_tx_info info; - info.tx_hash = cryptonote::get_transaction_hash(tx_detail.tx); + info.tx_hash = added_pool_tx.first; std::stringstream oss; binary_archive ar(oss); - bool r = ::serialization::serialize(ar, tx_detail.tx); + bool r = req.prune + ? const_cast(added_pool_tx.second.tx).serialize_base(ar) + : ::serialization::serialize(ar, const_cast(added_pool_tx.second.tx)); if (!r) { res.status = "Failed to serialize transaction"; return true; } info.tx_blob = oss.str(); - info.double_spend_seen = tx_detail.double_spend_seen; + info.double_spend_seen = added_pool_tx.second.double_spend_seen; res.added_pool_txs.push_back(std::move(info)); } } @@ -993,17 +996,16 @@ namespace cryptonote // try the pool for any missing txes size_t found_in_pool = 0; std::unordered_set pool_tx_hashes; - std::unordered_map per_tx_pool_tx_info; + std::unordered_map per_tx_pool_tx_details; if (!missed_txs.empty()) { - std::vector pool_tx_info; - std::vector pool_key_image_info; - bool r = m_core.get_pool_transactions_and_spent_keys_info(pool_tx_info, pool_key_image_info, !request_has_rpc_origin || !restricted); + std::vector> pool_txs; + bool r = m_core.get_pool_transactions_info(missed_txs, pool_txs, !request_has_rpc_origin || !restricted); if(r) { // sort to match original request std::vector> sorted_txs; - std::vector::const_iterator i; + std::vector>::const_iterator i; unsigned txs_processed = 0; for (const crypto::hash &h: vh) { @@ -1023,36 +1025,23 @@ namespace cryptonote sorted_txs.push_back(std::move(txs[txs_processed])); ++txs_processed; } - else if ((i = std::find_if(pool_tx_info.begin(), pool_tx_info.end(), [h](const tx_info &txi) { return epee::string_tools::pod_to_hex(h) == txi.id_hash; })) != pool_tx_info.end()) + else if ((i = std::find_if(pool_txs.begin(), pool_txs.end(), [h](const std::pair &pt) { return h == pt.first; })) != pool_txs.end()) { - cryptonote::transaction tx; - if (!cryptonote::parse_and_validate_tx_from_blob(i->tx_blob, tx)) - { - res.status = "Failed to parse and validate tx from blob"; - return true; - } + const tx_memory_pool::tx_details &td = i->second; std::stringstream ss; binary_archive ba(ss); - bool r = const_cast(tx).serialize_base(ba); + bool r = const_cast(td.tx).serialize_base(ba); if (!r) { res.status = "Failed to serialize transaction base"; return true; } const cryptonote::blobdata pruned = ss.str(); - const crypto::hash prunable_hash = tx.version == 1 ? crypto::null_hash : get_transaction_prunable_hash(tx); - sorted_txs.push_back(std::make_tuple(h, pruned, prunable_hash, std::string(i->tx_blob, pruned.size()))); + const crypto::hash prunable_hash = td.tx.version == 1 ? crypto::null_hash : get_transaction_prunable_hash(td.tx); + sorted_txs.push_back(std::make_tuple(h, pruned, prunable_hash, std::string(td.tx_blob, pruned.size()))); missed_txs.erase(std::find(missed_txs.begin(), missed_txs.end(), h)); pool_tx_hashes.insert(h); - const std::string hash_string = epee::string_tools::pod_to_hex(h); - for (const auto &ti: pool_tx_info) - { - if (ti.id_hash == hash_string) - { - per_tx_pool_tx_info.insert(std::make_pair(h, ti)); - break; - } - } + per_tx_pool_tx_details.insert(std::make_pair(h, td)); ++found_in_pool; } } @@ -1148,8 +1137,8 @@ namespace cryptonote { e.block_height = e.block_timestamp = std::numeric_limits::max(); e.confirmations = 0; - auto it = per_tx_pool_tx_info.find(tx_hash); - if (it != per_tx_pool_tx_info.end()) + auto it = per_tx_pool_tx_details.find(tx_hash); + if (it != per_tx_pool_tx_details.end()) { e.double_spend_seen = it->second.double_spend_seen; e.relayed = it->second.relayed; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index a4876a395..70df2c41e 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -236,6 +236,7 @@ namespace cryptonote uint64_t daemon_time; uint8_t pool_info_extent; std::vector added_pool_txs; + std::vector remaining_added_pool_txids; std::vector removed_pool_txids; BEGIN_KV_SERIALIZE_MAP() @@ -244,10 +245,17 @@ namespace cryptonote KV_SERIALIZE(start_height) KV_SERIALIZE(current_height) KV_SERIALIZE(output_indices) - KV_SERIALIZE(daemon_time) - KV_SERIALIZE(pool_info_extent) - KV_SERIALIZE(added_pool_txs) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(removed_pool_txids) + KV_SERIALIZE_OPT(daemon_time, (uint64_t) 0) + KV_SERIALIZE_OPT(pool_info_extent, (uint8_t) 0) + if (pool_info_extent != POOL_INFO_EXTENT::NONE) + { + KV_SERIALIZE(added_pool_txs) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(remaining_added_pool_txids) + } + if (pool_info_extent == POOL_INFO_EXTENT::INCREMENTAL) + { + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(removed_pool_txids) + } END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init response; -- cgit v1.2.3