aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
authorluigi1111 <luigi1111w@gmail.com>2023-03-18 17:58:27 -0400
committerluigi1111 <luigi1111w@gmail.com>2023-03-18 17:58:27 -0400
commita51120222213843dbb8bc22c2694a81d69781b26 (patch)
tree0e732e939c8ffc158a1caec7f0461461d555e573 /src/rpc
parentMerge pull request #8737 (diff)
parentEnforce restricted # pool txs served via RPC + optimize chunked reqs (diff)
downloadmonero-a51120222213843dbb8bc22c2694a81d69781b26.tar.xz
Merge pull request #8076
c4af33e Enforce restricted # pool txs served via RPC + optimize chunked reqs (j-berman) 9752116 wallet2, RPC: Optimize RPC calls for periodic refresh from 3 down to 1 call (rbrunner7)
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/core_rpc_server.cpp235
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h47
2 files changed, 196 insertions, 86 deletions
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index c6d105fb4..56b8d1aa4 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -598,88 +598,165 @@ namespace cryptonote
CHECK_PAYMENT(req, res, 1);
- // quick check for noop
- if (!req.block_ids.empty())
- {
- uint64_t last_block_height;
- crypto::hash last_block_hash;
- m_core.get_blockchain_top(last_block_height, last_block_hash);
- if (last_block_hash == req.block_ids.front())
- {
- res.start_height = 0;
- res.current_height = m_core.get_current_blockchain_height();
- res.status = CORE_RPC_STATUS_OK;
+ res.daemon_time = (uint64_t)time(NULL);
+ // Always set daemon time, and set it early rather than late, as delivering some incremental pool
+ // info twice because of slightly overlapping time intervals is no problem, whereas producing gaps
+ // and never delivering something is
+
+ bool get_blocks = false;
+ bool get_pool = false;
+ switch (req.requested_info)
+ {
+ case COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY:
+ // Compatibility value 0: Clients that do not set 'requested_info' want blocks, and only blocks
+ get_blocks = true;
+ break;
+ case COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL:
+ get_blocks = true;
+ get_pool = true;
+ break;
+ case COMMAND_RPC_GET_BLOCKS_FAST::POOL_ONLY:
+ get_pool = true;
+ break;
+ default:
+ res.status = "Failed, wrong requested info";
return true;
- }
}
- size_t max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT;
- if (m_rpc_payment)
+ res.pool_info_extent = COMMAND_RPC_GET_BLOCKS_FAST::NONE;
+
+ if (get_pool)
{
- max_blocks = res.credits / COST_PER_BLOCK;
- if (max_blocks > COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT)
- max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT;
- if (max_blocks == 0)
+ 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();
+
+ 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 (success)
{
- res.status = CORE_RPC_STATUS_PAYMENT_REQUIRED;
+ res.added_pool_txs.clear();
+ if (m_rpc_payment)
+ {
+ 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 (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));
+ }
+ }
+ if (success)
+ {
+ res.pool_info_extent = incremental ? COMMAND_RPC_GET_BLOCKS_FAST::INCREMENTAL : COMMAND_RPC_GET_BLOCKS_FAST::FULL;
+ }
+ else
+ {
+ res.status = "Failed to get pool info";
return true;
}
}
- std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > > bs;
- if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, max_blocks, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
- {
- res.status = "Failed";
- add_host_fail(ctx);
- return true;
- }
-
- CHECK_PAYMENT_SAME_TS(req, res, bs.size() * COST_PER_BLOCK);
-
- size_t size = 0, ntxes = 0;
- res.blocks.reserve(bs.size());
- res.output_indices.reserve(bs.size());
- for(auto& bd: bs)
+ if (get_blocks)
{
- res.blocks.resize(res.blocks.size()+1);
- res.blocks.back().pruned = req.prune;
- res.blocks.back().block = bd.first.first;
- size += bd.first.first.size();
- res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices());
- ntxes += bd.second.size();
- res.output_indices.back().indices.reserve(1 + bd.second.size());
- if (req.no_miner_tx)
- res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
- res.blocks.back().txs.reserve(bd.second.size());
- for (std::vector<std::pair<crypto::hash, cryptonote::blobdata>>::iterator i = bd.second.begin(); i != bd.second.end(); ++i)
+ // quick check for noop
+ if (!req.block_ids.empty())
{
- res.blocks.back().txs.push_back({std::move(i->second), crypto::null_hash});
- i->second.clear();
- i->second.shrink_to_fit();
- size += res.blocks.back().txs.back().blob.size();
+ uint64_t last_block_height;
+ crypto::hash last_block_hash;
+ m_core.get_blockchain_top(last_block_height, last_block_hash);
+ if (last_block_hash == req.block_ids.front())
+ {
+ res.start_height = 0;
+ res.current_height = last_block_height + 1;
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
}
- const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 1);
- if (n_txes_to_lookup > 0)
+ size_t max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT;
+ if (m_rpc_payment)
{
- std::vector<std::vector<uint64_t>> indices;
- bool r = m_core.get_tx_outputs_gindexs(req.no_miner_tx ? bd.second.front().first : bd.first.second, n_txes_to_lookup, indices);
- if (!r)
+ max_blocks = res.credits / COST_PER_BLOCK;
+ if (max_blocks > COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT)
+ max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT;
+ if (max_blocks == 0)
{
- res.status = "Failed";
+ res.status = CORE_RPC_STATUS_PAYMENT_REQUIRED;
return true;
}
- if (indices.size() != n_txes_to_lookup || res.output_indices.back().indices.size() != (req.no_miner_tx ? 1 : 0))
+ }
+
+ std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > > bs;
+ if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, max_blocks, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
+ {
+ res.status = "Failed";
+ add_host_fail(ctx);
+ return true;
+ }
+
+ CHECK_PAYMENT_SAME_TS(req, res, bs.size() * COST_PER_BLOCK);
+
+ size_t size = 0, ntxes = 0;
+ res.blocks.reserve(bs.size());
+ res.output_indices.reserve(bs.size());
+ for(auto& bd: bs)
+ {
+ res.blocks.resize(res.blocks.size()+1);
+ res.blocks.back().pruned = req.prune;
+ res.blocks.back().block = bd.first.first;
+ size += bd.first.first.size();
+ res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices());
+ ntxes += bd.second.size();
+ res.output_indices.back().indices.reserve(1 + bd.second.size());
+ if (req.no_miner_tx)
+ res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
+ res.blocks.back().txs.reserve(bd.second.size());
+ for (std::vector<std::pair<crypto::hash, cryptonote::blobdata>>::iterator i = bd.second.begin(); i != bd.second.end(); ++i)
{
- res.status = "Failed";
- return true;
+ res.blocks.back().txs.push_back({std::move(i->second), crypto::null_hash});
+ i->second.clear();
+ i->second.shrink_to_fit();
+ size += res.blocks.back().txs.back().blob.size();
+ }
+
+ const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 1);
+ if (n_txes_to_lookup > 0)
+ {
+ std::vector<std::vector<uint64_t>> indices;
+ bool r = m_core.get_tx_outputs_gindexs(req.no_miner_tx ? bd.second.front().first : bd.first.second, n_txes_to_lookup, indices);
+ if (!r)
+ {
+ res.status = "Failed";
+ return true;
+ }
+ if (indices.size() != n_txes_to_lookup || res.output_indices.back().indices.size() != (req.no_miner_tx ? 1 : 0))
+ {
+ res.status = "Failed";
+ return true;
+ }
+ for (size_t i = 0; i < indices.size(); ++i)
+ res.output_indices.back().indices.push_back({std::move(indices[i])});
}
- for (size_t i = 0; i < indices.size(); ++i)
- res.output_indices.back().indices.push_back({std::move(indices[i])});
}
+ MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, size " << size);
}
- MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, size " << size);
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -919,17 +996,16 @@ namespace cryptonote
// try the pool for any missing txes
size_t found_in_pool = 0;
std::unordered_set<crypto::hash> pool_tx_hashes;
- std::unordered_map<crypto::hash, tx_info> per_tx_pool_tx_info;
+ std::unordered_map<crypto::hash, tx_memory_pool::tx_details> per_tx_pool_tx_details;
if (!missed_txs.empty())
{
- std::vector<tx_info> pool_tx_info;
- std::vector<spent_key_image_info> 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<std::pair<crypto::hash, tx_memory_pool::tx_details>> 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<std::tuple<crypto::hash, cryptonote::blobdata, crypto::hash, cryptonote::blobdata>> sorted_txs;
- std::vector<tx_info>::const_iterator i;
+ std::vector<std::pair<crypto::hash, tx_memory_pool::tx_details>>::const_iterator i;
unsigned txs_processed = 0;
for (const crypto::hash &h: vh)
{
@@ -949,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<crypto::hash, tx_memory_pool::tx_details> &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<true> ba(ss);
- bool r = const_cast<cryptonote::transaction&>(tx).serialize_base(ba);
+ bool r = const_cast<cryptonote::transaction&>(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;
}
}
@@ -1084,8 +1147,8 @@ namespace cryptonote
{
e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::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 e1222f6eb..70df2c41e 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -162,18 +162,29 @@ namespace cryptonote
struct COMMAND_RPC_GET_BLOCKS_FAST
{
+ enum REQUESTED_INFO
+ {
+ BLOCKS_ONLY = 0,
+ BLOCKS_AND_POOL = 1,
+ POOL_ONLY = 2
+ };
+
struct request_t: public rpc_access_request_base
{
+ uint8_t requested_info;
std::list<crypto::hash> block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */
uint64_t start_height;
bool prune;
bool no_miner_tx;
+ uint64_t pool_info_since;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_PARENT(rpc_access_request_base)
+ KV_SERIALIZE_OPT(requested_info, (uint8_t)0)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids)
KV_SERIALIZE(start_height)
KV_SERIALIZE(prune)
KV_SERIALIZE_OPT(no_miner_tx, false)
+ KV_SERIALIZE_OPT(pool_info_since, (uint64_t)0)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -196,12 +207,37 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
+ struct pool_tx_info
+ {
+ crypto::hash tx_hash;
+ blobdata tx_blob;
+ bool double_spend_seen;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_VAL_POD_AS_BLOB(tx_hash)
+ KV_SERIALIZE(tx_blob)
+ KV_SERIALIZE(double_spend_seen)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ enum POOL_INFO_EXTENT
+ {
+ NONE = 0,
+ INCREMENTAL = 1,
+ FULL = 2
+ };
+
struct response_t: public rpc_access_response_base
{
std::vector<block_complete_entry> blocks;
uint64_t start_height;
uint64_t current_height;
std::vector<block_output_indices> output_indices;
+ uint64_t daemon_time;
+ uint8_t pool_info_extent;
+ std::vector<pool_tx_info> added_pool_txs;
+ std::vector<crypto::hash> remaining_added_pool_txids;
+ std::vector<crypto::hash> removed_pool_txids;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_PARENT(rpc_access_response_base)
@@ -209,6 +245,17 @@ namespace cryptonote
KV_SERIALIZE(start_height)
KV_SERIALIZE(current_height)
KV_SERIALIZE(output_indices)
+ 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_t> response;