Protect node privacy by proper filtering in restricted-mode RPC answers

This patch allows to filter out sensitive information for queries that rely on the pool state, when running in restricted mode.
This filtering is only applied to data sent back to RPC queries. Results of inline commands typed locally in the daemon are not affected.
In practice, when running with `--restricted-rpc`:
* get_transaction_pool will list relayed transactions with the fields "last relayed time" and "received time" set to zero.
* get_transaction_pool will not list transaction that have do_not_relay set to true, and will not list key images that are used only for such transactions
* get_transaction_pool_hashes.bin will not list such transaction
* get_transaction_pool_stats will not count such transactions in any of the aggregated values that are computed

The implementation does not make filtering the default, so developers should be mindful of this if they add new RPC functionality.
Fixes #2590.
This commit is contained in:
binaryFate 2017-11-08 13:06:41 +01:00
parent a2c2f4e4b0
commit 10013e9434
13 changed files with 140 additions and 75 deletions

View file

@ -4211,9 +4211,9 @@ void Blockchain::remove_txpool_tx(const crypto::hash &txid)
m_db->remove_txpool_tx(txid);
}
uint64_t Blockchain::get_txpool_tx_count() const
uint64_t Blockchain::get_txpool_tx_count(bool include_unrelayed_txes) const
{
return m_db->get_txpool_tx_count();
return m_db->get_txpool_tx_count(include_unrelayed_txes);
}
txpool_tx_meta_t Blockchain::get_txpool_tx_meta(const crypto::hash& txid) const
@ -4231,9 +4231,9 @@ cryptonote::blobdata Blockchain::get_txpool_tx_blob(const crypto::hash& txid) co
return m_db->get_txpool_tx_blob(txid);
}
bool Blockchain::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob) const
bool Blockchain::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, bool include_unrelayed_txes) const
{
return m_db->for_all_txpool_txes(f, include_blob);
return m_db->for_all_txpool_txes(f, include_blob, include_unrelayed_txes);
}
void Blockchain::set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync, blockchain_db_sync_mode sync_mode, bool fast_sync)

View file

@ -947,11 +947,11 @@ namespace cryptonote
void add_txpool_tx(transaction &tx, const txpool_tx_meta_t &meta);
void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta);
void remove_txpool_tx(const crypto::hash &txid);
uint64_t get_txpool_tx_count() const;
uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const;
txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const;
bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const;
cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const;
bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false) const;
bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = true) const;
bool is_within_compiled_block_hash_area(uint64_t height) const;
bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); }

View file

@ -1182,21 +1182,21 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::get_pool_transactions(std::list<transaction>& txs) const
bool core::get_pool_transactions(std::list<transaction>& txs, bool include_sensitive_data) const
{
m_mempool.get_transactions(txs);
m_mempool.get_transactions(txs, include_sensitive_data);
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::get_pool_transaction_hashes(std::vector<crypto::hash>& txs) const
bool core::get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_sensitive_data) const
{
m_mempool.get_transaction_hashes(txs);
m_mempool.get_transaction_hashes(txs, include_sensitive_data);
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::get_pool_transaction_stats(struct txpool_stats& stats) const
bool core::get_pool_transaction_stats(struct txpool_stats& stats, bool include_sensitive_data) const
{
m_mempool.get_transaction_stats(stats);
m_mempool.get_transaction_stats(stats, include_sensitive_data);
return true;
}
//-----------------------------------------------------------------------------------------------
@ -1210,9 +1210,9 @@ namespace cryptonote
return m_mempool.have_tx(id);
}
//-----------------------------------------------------------------------------------------------
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
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
{
return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos);
return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos, include_sensitive_data);
}
//-----------------------------------------------------------------------------------------------
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

View file

@ -420,11 +420,12 @@ namespace cryptonote
/**
* @copydoc tx_memory_pool::get_transactions
* @param include_unrelayed_txes include unrelayed txes in result
*
* @note see tx_memory_pool::get_transactions
*/
bool get_pool_transactions(std::list<transaction>& txs) const;
bool get_pool_transactions(std::list<transaction>& txs, bool include_unrelayed_txes = true) const;
/**
* @copydoc tx_memory_pool::get_txpool_backlog
*
@ -434,17 +435,19 @@ namespace cryptonote
/**
* @copydoc tx_memory_pool::get_transactions
* @param include_unrelayed_txes include unrelayed txes in result
*
* @note see tx_memory_pool::get_transactions
*/
bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs) const;
bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const;
/**
* @copydoc tx_memory_pool::get_transactions
* @param include_unrelayed_txes include unrelayed txes in result
*
* @note see tx_memory_pool::get_transactions
*/
bool get_pool_transaction_stats(struct txpool_stats& stats) const;
bool get_pool_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const;
/**
* @copydoc tx_memory_pool::get_transaction
@ -455,10 +458,11 @@ namespace cryptonote
/**
* @copydoc tx_memory_pool::get_pool_transactions_and_spent_keys_info
* @param include_unrelayed_txes include unrelayed txes in result
*
* @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) const;
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_unrelayed_txes = true) const;
/**
* @copydoc tx_memory_pool::get_pool_for_rpc

View file

@ -432,7 +432,7 @@ namespace cryptonote
remove.insert(txid);
}
return true;
});
}, false);
if (!remove.empty())
{
@ -494,7 +494,7 @@ namespace cryptonote
}
}
return true;
});
}, false);
return true;
}
//---------------------------------------------------------------------------------
@ -521,14 +521,14 @@ namespace cryptonote
}
}
//---------------------------------------------------------------------------------
size_t tx_memory_pool::get_transactions_count() const
size_t tx_memory_pool::get_transactions_count(bool include_unrelayed_txes) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
return m_blockchain.get_txpool_tx_count();
return m_blockchain.get_txpool_tx_count(include_unrelayed_txes);
}
//---------------------------------------------------------------------------------
void tx_memory_pool::get_transactions(std::list<transaction>& txs) const
void tx_memory_pool::get_transactions(std::list<transaction>& txs, bool include_unrelayed_txes) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
@ -542,20 +542,20 @@ namespace cryptonote
}
txs.push_back(tx);
return true;
}, true);
}, true, include_unrelayed_txes);
}
//------------------------------------------------------------------
void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs) const
void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
txs.push_back(txid);
return true;
});
}, false, include_unrelayed_txes);
}
//------------------------------------------------------------------
void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog) const
void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_unrelayed_txes) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
@ -563,16 +563,16 @@ namespace cryptonote
m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
backlog.push_back({meta.blob_size, meta.fee, meta.receive_time - now});
return true;
});
}, false, include_unrelayed_txes);
}
//------------------------------------------------------------------
void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats) const
void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
const uint64_t now = time(NULL);
std::map<uint64_t, txpool_histo> agebytes;
stats.txs_total = m_blockchain.get_txpool_tx_count();
stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes);
std::vector<uint32_t> sizes;
sizes.reserve(stats.txs_total);
m_blockchain.for_all_txpool_txes([&stats, &sizes, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
@ -595,7 +595,7 @@ namespace cryptonote
agebytes[age].txs++;
agebytes[age].bytes += meta.blob_size;
return true;
});
}, false, include_unrelayed_txes);
stats.bytes_med = epee::misc_utils::median(sizes);
if (stats.txs_total > 1)
{
@ -642,11 +642,11 @@ namespace cryptonote
}
//------------------------------------------------------------------
//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) 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, bool include_sensitive_data) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
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 *bd){
tx_info txi;
txi.id_hash = epee::string_tools::pod_to_hex(txid);
transaction tx;
@ -664,14 +664,17 @@ namespace cryptonote
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_id_hash = epee::string_tools::pod_to_hex(meta.last_failed_id);
txi.receive_time = meta.receive_time;
// In restricted mode we do not include this data:
txi.receive_time = include_sensitive_data ? meta.receive_time : 0;
txi.relayed = meta.relayed;
txi.last_relayed_time = meta.last_relayed_time;
// In restricted mode we do not include this data:
txi.last_relayed_time = include_sensitive_data ? meta.last_relayed_time : 0;
txi.do_not_relay = meta.do_not_relay;
tx_infos.push_back(txi);
return true;
}, true);
}, true, include_sensitive_data);
txpool_tx_meta_t meta;
for (const key_images_container::value_type& kee : m_spent_key_images) {
const crypto::key_image& k_image = kee.first;
const std::unordered_set<crypto::hash>& kei_image_set = kee.second;
@ -679,9 +682,26 @@ namespace cryptonote
ki.id_hash = epee::string_tools::pod_to_hex(k_image);
for (const crypto::hash& tx_id_hash : kei_image_set)
{
if (!include_sensitive_data)
{
try
{
meta = m_blockchain.get_txpool_tx_meta(tx_id_hash);
if (!meta.relayed)
// Do not include that transaction if in restricted mode and it's not relayed
continue;
}
catch (const std::exception &e)
{
MERROR("Failed to get tx meta from txpool: " << e.what());
return false;
}
}
ki.txs_hashes.push_back(epee::string_tools::pod_to_hex(tx_id_hash));
}
key_image_infos.push_back(ki);
// Only return key images for which we have at least one tx that we can show for them
if (!ki.txs_hashes.empty())
key_image_infos.push_back(ki);
}
return true;
}
@ -714,7 +734,7 @@ namespace cryptonote
txi.do_not_relay = meta.do_not_relay;
tx_infos.push_back(txi);
return true;
}, true);
}, true, false);
for (const key_images_container::value_type& kee : m_spent_key_images) {
std::vector<crypto::hash> tx_hashes;
@ -1044,7 +1064,7 @@ namespace cryptonote
remove.insert(txid);
}
return true;
});
}, false);
size_t n_removed = 0;
if (!remove.empty())

View file

@ -233,29 +233,37 @@ namespace cryptonote
* @brief get a list of all transactions in the pool
*
* @param txs return-by-reference the list of transactions
* @param include_unrelayed_txes include unrelayed txes in the result
*
*/
void get_transactions(std::list<transaction>& txs) const;
void get_transactions(std::list<transaction>& txs, bool include_unrelayed_txes = true) const;
/**
* @brief get a list of all transaction hashes in the pool
*
* @param txs return-by-reference the list of transactions
* @param include_unrelayed_txes include unrelayed txes in the result
*
*/
void get_transaction_hashes(std::vector<crypto::hash>& txs) const;
void get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const;
/**
* @brief get (size, fee, receive time) for all transaction in the pool
*
* @param txs return-by-reference that data
* @param include_unrelayed_txes include unrelayed txes in the result
*
*/
void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog) const;
void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_unrelayed_txes = true) const;
/**
* @brief get a summary statistics of all transaction hashes in the pool
*
* @param stats return-by-reference the pool statistics
* @param include_unrelayed_txes include unrelayed txes in the result
*
*/
void get_transaction_stats(struct txpool_stats& stats) const;
void get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const;
/**
* @brief get information about all transactions and key images in the pool
@ -264,10 +272,11 @@ namespace cryptonote
*
* @param tx_infos return-by-reference the transactions' information
* @param key_image_infos return-by-reference the spent key images' information
* @param include_sensitive_data include unrelayed txes and fields that are sensitive to the node privacy
*
* @return true
*/
bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const;
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 = true) const;
/**
* @brief get information about all transactions and key images in the pool
@ -308,6 +317,7 @@ namespace cryptonote
* nonzero fee
* hasn't been relayed too recently
* isn't old enough that relaying it is considered harmful
* Note a transaction can be "relayable" even if do_not_relay is true
*
* @param txs return-by-reference the transactions and their hashes
*
@ -327,7 +337,7 @@ namespace cryptonote
*
* @return the number of transactions in the pool
*/
size_t get_transactions_count() const;
size_t get_transactions_count(bool include_unrelayed_txes = true) const;
/**
* @brief get a string containing human-readable pool information