rpc: replace get_blocks.bin Levin pool budget

/get_blocks.bin is HTTP binary RPC serialized with portable storage, not a
P2P Levin message. The previously added pool limiter used
LEVIN_DEFAULT_MAX_PACKET_SIZE as a response budget, but that constant does
not apply to the RPC path.
This commit is contained in:
selsta
2026-07-15 01:58:59 +02:00
parent 0f84863fed
commit 85b38a52a6
6 changed files with 49 additions and 77 deletions
+35 -48
View File
@@ -75,6 +75,8 @@ using namespace epee;
#define RESTRICTED_SPENT_KEY_IMAGES_COUNT 5000
#define RESTRICTED_BLOCK_COUNT 1000
static constexpr size_t GET_BLOCKS_BIN_MAX_ADDED_POOL_TX_BODIES = 20000;
#define RPC_TRACKER(rpc) \
PERF_TIMER(rpc); \
RPCTracker tracker(#rpc, PERF_TIMER_NAME(rpc))
@@ -519,10 +521,8 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::handle_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, const connection_context *ctx, std::size_t &cumul_block_data_size)
bool core_rpc_server::handle_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, const connection_context *ctx)
{
cumul_block_data_size = 0;
// quick check for noop
if (req.start_height > 0 || !req.block_ids.empty())
{
@@ -552,6 +552,7 @@ namespace cryptonote
return false;
}
size_t cumul_block_data_size = 0;
size_t ntxes = 0;
res.blocks.reserve(bs.size());
res.output_indices.reserve(bs.size());
@@ -647,10 +648,9 @@ namespace cryptonote
res.pool_info_extent = COMMAND_RPC_GET_BLOCKS_FAST::NONE;
size_t cumul_block_data_size = 0;
if (get_blocks)
{
if (!handle_get_blocks(req, res, ctx, cumul_block_data_size))
if (!handle_get_blocks(req, res, ctx))
{
res.status = "Failed";
return true;
@@ -662,58 +662,45 @@ 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<size_t>::max();
const size_t max_tx_count = restricted
? std::min<size_t>(RESTRICTED_TRANSACTIONS_COUNT, GET_BLOCKS_BIN_MAX_ADDED_POOL_TX_BODIES)
: GET_BLOCKS_BIN_MAX_ADDED_POOL_TX_BODIES;
const size_t limit = LEVIN_DEFAULT_MAX_PACKET_SIZE * 0.9;
bool incremental;
std::vector<std::pair<crypto::hash, tx_memory_pool::tx_details>> 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 the blocks alone already consume the safe packet budget, skip pool info
if (cumul_block_data_size >= limit)
if (!success)
{
LOG_ERROR("on_get_blocks: omitting pool info, response already "
<< cumul_block_data_size << " bytes (limit " << limit << ")");
// res.pool_info_extent stays as NONE (set earlier), which tells wallets
// that there is no pool info in this response.
res.status = "Failed to get pool info";
return true;
}
else
res.added_pool_txs.clear();
for (const auto &added_pool_tx: added_pool_txs)
{
const size_t pool_limit = limit - cumul_block_data_size;
bool incremental;
std::vector<std::pair<crypto::hash, tx_memory_pool::tx_details>> 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, pool_limit);
if (!success)
COMMAND_RPC_GET_BLOCKS_FAST::pool_tx_info info;
info.tx_hash = added_pool_tx.first;
std::stringstream oss;
binary_archive<true> ar(oss);
bool r = req.prune
? const_cast<cryptonote::transaction&>(added_pool_tx.second.tx).serialize_base(ar)
: ::serialization::serialize(ar, const_cast<cryptonote::transaction&>(added_pool_tx.second.tx));
if (!r)
{
res.status = "Failed to get pool info";
res.status = "Failed to serialize transaction";
return true;
}
res.added_pool_txs.clear();
for (const auto &added_pool_tx: added_pool_txs)
{
COMMAND_RPC_GET_BLOCKS_FAST::pool_tx_info info;
info.tx_hash = added_pool_tx.first;
std::stringstream oss;
binary_archive<true> ar(oss);
bool r = req.prune
? const_cast<cryptonote::transaction&>(added_pool_tx.second.tx).serialize_base(ar)
: ::serialization::serialize(ar, const_cast<cryptonote::transaction&>(added_pool_tx.second.tx));
if (!r)
{
res.status = "Failed to serialize transaction";
return true;
}
info.tx_blob = oss.str();
info.double_spend_seen = added_pool_tx.second.double_spend_seen;
res.added_pool_txs.push_back(std::move(info));
}
res.pool_info_extent = incremental
? COMMAND_RPC_GET_BLOCKS_FAST::INCREMENTAL
: COMMAND_RPC_GET_BLOCKS_FAST::FULL;
info.tx_blob = oss.str();
info.double_spend_seen = added_pool_tx.second.double_spend_seen;
res.added_pool_txs.push_back(std::move(info));
}
res.pool_info_extent = incremental
? COMMAND_RPC_GET_BLOCKS_FAST::INCREMENTAL
: COMMAND_RPC_GET_BLOCKS_FAST::FULL;
}
res.status = CORE_RPC_STATUS_OK;