mirror of
https://github.com/monero-project/monero.git
synced 2025-12-13 05:47:21 -05:00
blockchain_db: add k-anonymity to txid fetching
Read more about k-anonymity [here](https://en.wikipedia.org/wiki/K-anonymity). We implement this feature in the monero daemon for transactions by providing a "Txid Template", which is simply a txid with all but `num_matching_bits` bits zeroed out, and the number `num_matching_bits`. We add an operation to `BlockchainLMDB` called `get_txids_loose` which takes a txid template and returns all txids in the database (chain and mempool) that satisfy that template. Thus, a client can ask about a specific transaction from a daemon without revealing the exact transaction they are inquiring about. The client can control the statistical chance that other TXIDs (besides the one in question) match the txid template sent to the daemon up to a power of 2. For example, if a client sets their `num_matching_bits` to 5, then statistically any txid has a 1/(2^5) chance to match. With `num_matching_bits`=10, there is a 1/(2^10) chance, so on and so forth. Co-authored-by: ACK-J <60232273+ACK-J@users.noreply.github.com>
This commit is contained in:
parent
00fd416a99
commit
b0bf49a65a
13 changed files with 770 additions and 1 deletions
|
|
@ -3533,6 +3533,82 @@ namespace cryptonote
|
|||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_get_txids_loose(const COMMAND_RPC_GET_TXIDS_LOOSE::request& req, COMMAND_RPC_GET_TXIDS_LOOSE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
|
||||
{
|
||||
RPC_TRACKER(get_txids_loose);
|
||||
|
||||
// Maybe don't use bootstrap since this endpoint is meant to retreive TXIDs w/ k-anonymity,
|
||||
// so shunting this request to a random node seems counterproductive.
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
const uint64_t max_num_txids = RESTRICTED_SPENT_KEY_IMAGES_COUNT * (m_restricted ? 1 : 10);
|
||||
|
||||
// Sanity check parameters
|
||||
if (req.num_matching_bits > 256)
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
|
||||
error_resp.message = "There are only 256 bits in a hash, you gave too many";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to guess when bit count is too low before fetching, within a certain margin of error
|
||||
const uint64_t num_txs_ever = m_core.get_blockchain_storage().get_db().get_tx_count();
|
||||
const uint64_t num_expected_fetch = (num_txs_ever >> std::min((int) req.num_matching_bits, 63));
|
||||
const uint64_t max_num_txids_with_margin = 2 * max_num_txids;
|
||||
if (num_expected_fetch > max_num_txids_with_margin)
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
|
||||
error_resp.message = "Trying to search with too few matching bits, detected before fetching";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert txid template to a crypto::hash
|
||||
crypto::hash search_hash;
|
||||
if (!epee::string_tools::hex_to_pod(req.txid_template, search_hash))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
|
||||
error_resp.message = "Could not decode hex txid";
|
||||
return false;
|
||||
}
|
||||
// Check that txid template is zeroed correctly for number of given matchign bits
|
||||
else if (search_hash != make_hash32_loose_template(req.num_matching_bits, search_hash))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
|
||||
error_resp.message = "Txid template is not zeroed correctly for number of bits. You could be leaking true txid!";
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Do the DB fetch
|
||||
const auto txids = m_core.get_blockchain_storage().get_db().get_txids_loose(search_hash, req.num_matching_bits, max_num_txids);
|
||||
// Fill out response form
|
||||
for (const auto& txid : txids)
|
||||
res.txids.emplace_back(epee::string_tools::pod_to_hex(txid));
|
||||
}
|
||||
catch (const TX_EXISTS&)
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
|
||||
error_resp.message = "Trying to search with too few matching bits";
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = std::string("Error during get_txids_loose: ") + e.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
#else // BYTE_ORDER == BIG_ENDIAN
|
||||
// BlockchainLMDB::compare_hash32 has different key ordering (thus different txid templates) on BE systems
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Due to implementation details, this feature is not available on big-endian daemons";
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_rpc_access_submit_nonce(const COMMAND_RPC_ACCESS_SUBMIT_NONCE::request& req, COMMAND_RPC_ACCESS_SUBMIT_NONCE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
|
||||
{
|
||||
RPC_TRACKER(rpc_access_submit_nonce);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue