From 4258dab4d6dcbf563daa054c7e05e8a0027290b8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 5 Jun 2016 10:46:18 +0100 Subject: [PATCH] core: new /getrandom_rctouts.bin binary RPC call to get random ringct outputs to mix with --- src/cryptonote_core/blockchain.cpp | 98 +++++++++++++++++++++++++ src/cryptonote_core/blockchain.h | 25 +++++++ src/cryptonote_core/cryptonote_core.cpp | 5 ++ src/cryptonote_core/cryptonote_core.h | 8 ++ src/rpc/core_rpc_server.cpp | 24 ++++++ src/rpc/core_rpc_server.h | 2 + src/rpc/core_rpc_server_commands_defs.h | 30 ++++++++ 7 files changed, 192 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index cc6b48b6b..46ff7efbb 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1597,6 +1597,104 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT return true; } //------------------------------------------------------------------ +// This function adds the ringct output at index i to the list +// unlocked and other such checks should be done by here. +void Blockchain::add_out_to_get_rct_random_outs(std::list& outs, size_t i) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& oen = *outs.insert(outs.end(), COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry()); + oen.global_amount_index = i; + output_data_t data = m_db->get_output_key(0, i); + oen.out_key = data.pubkey; + oen.commitment = m_db->get_rct_commitment(i); +} +//------------------------------------------------------------------ +// This function takes an RPC request for mixins and creates an RPC response +// with the requested mixins. +// TODO: figure out why this returns boolean / if we should be returning false +// in some cases +bool Blockchain::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + // for each amount that we need to get mixins for, get random outputs + // from BlockchainDB where is req.outs_count (number of mixins). + auto num_outs = m_db->get_num_outputs(0); + // ensure we don't include outputs that aren't yet eligible to be used + // outpouts are sorted by height + while (num_outs > 0) + { + const tx_out_index toi = m_db->get_output_tx_and_index(0, num_outs - 1); + const uint64_t height = m_db->get_tx_block_height(toi.first); + if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height()) + break; + --num_outs; + } + + std::unordered_set seen_indices; + + // if there aren't enough outputs to mix with (or just enough), + // use all of them. Eventually this should become impossible. + if (num_outs <= req.outs_count) + { + for (uint64_t i = 0; i < num_outs; i++) + { + // get tx_hash, tx_out_index from DB + tx_out_index toi = m_db->get_output_tx_and_index(0, i); + + // if tx is unlocked, add output to result_outs + if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) + { + add_out_to_get_rct_random_outs(res.outs, i); + } + } + } + else + { + // while we still need more mixins + while (res.outs.size() < req.outs_count) + { + // if we've gone through every possible output, we've gotten all we can + if (seen_indices.size() == num_outs) + { + break; + } + + // get a random output index from the DB. If we've already seen it, + // return to the top of the loop and try again, otherwise add it to the + // list of output indices we've seen. + + // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit + uint64_t r = crypto::rand() % ((uint64_t)1 << 53); + double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); + uint64_t i = (uint64_t)(frac*num_outs); + // just in case rounding up to 1 occurs after sqrt + if (i == num_outs) + --i; + + if (seen_indices.count(i)) + { + continue; + } + seen_indices.emplace(i); + + // get tx_hash, tx_out_index from DB + tx_out_index toi = m_db->get_output_tx_and_index(0, i); + + // if the output's transaction is unlocked, add the output's index to + // our list. + if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) + { + add_out_to_get_rct_random_outs(res.outs, i); + } + } + } + return true; +} +//------------------------------------------------------------------ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const { LOG_PRINT_L3("Blockchain::" << __func__); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 1524693b8..701c17d9e 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -454,6 +454,23 @@ namespace cryptonote */ bool get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const; + /** + * @brief gets random ringct outputs to mix with + * + * This function takes an RPC request for outputs to mix with + * and creates an RPC response with the resultant output indices + * and the matching keys. + * + * Outputs to mix with are randomly selected from the utxo set + * for each output amount in the request. + * + * @param req the output amounts and number of mixins to select + * @param res return-by-reference the resultant output indices + * + * @return true + */ + bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const; + /** * @brief gets the global indices for outputs from a given transaction * @@ -1053,6 +1070,14 @@ namespace cryptonote */ void add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const; + /** + * @brief adds the given output to the requested set of random ringct outputs + * + * @param outs return-by-reference the set the output is to be added to + * @param i the rct output index + */ + void add_out_to_get_rct_random_outs(std::list& outs, size_t i) const; + /** * @brief checks if a transaction is unlocked (its outputs spendable) * diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 550c55759..b3a7cb98d 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -706,6 +706,11 @@ namespace cryptonote return m_blockchain_storage.get_outs(req, res); } //----------------------------------------------------------------------------------------------- + bool core::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const + { + return m_blockchain_storage.get_random_rct_outs(req, res); + } + //----------------------------------------------------------------------------------------------- bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) const { return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 84b1f27a8..d16bd6553 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -479,6 +479,14 @@ namespace cryptonote */ bool get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const; + /** + * + * @copydoc Blockchain::get_random_rct_outs + * + * @note see Blockchain::get_random_rct_outs + */ + bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const; + /** * @copydoc miner::pause diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index c15a647de..586b09179 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -250,6 +250,30 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) + { + CHECK_CORE_BUSY(); + res.status = "Failed"; + if(!m_core.get_random_rct_outs(req, res)) + { + return true; + } + + res.status = CORE_RPC_STATUS_OK; + std::stringstream ss; + typedef COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry out_entry; + CHECK_AND_ASSERT_MES(res.outs.size(), true, "internal error: res.outs.size() is empty"); + std::for_each(res.outs.begin(), res.outs.end(), [&](out_entry& oe) + { + ss << oe.global_amount_index << " "; + }); + ss << ENDL; + std::string s = ss.str(); + LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS: " << ENDL << s); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) { CHECK_CORE_BUSY(); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 6ebf41abc..9fcf3abf8 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -79,6 +79,7 @@ namespace cryptonote MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES) MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS) MAP_URI_AUTO_BIN2("/get_outs.bin", on_get_outs, COMMAND_RPC_GET_OUTPUTS) + MAP_URI_AUTO_BIN2("/getrandom_rctouts.bin", on_get_random_rct_outs, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS) MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS) MAP_URI_AUTO_JON2("/is_key_image_spent", on_is_key_image_spent, COMMAND_RPC_IS_KEY_IMAGE_SPENT) MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX) @@ -128,6 +129,7 @@ namespace cryptonote bool on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res); bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res); + bool on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res); bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res); bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 22c3590e1..bbea145e5 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -315,6 +315,36 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; + + struct COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS + { + struct request + { + uint64_t outs_count; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(outs_count) + END_KV_SERIALIZE_MAP() + }; + +#pragma pack (push, 1) + struct out_entry + { + uint64_t global_amount_index; + crypto::public_key out_key; + rct::key commitment; + }; +#pragma pack(pop) + + struct response + { + std::list outs; + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; //----------------------------------------------- struct COMMAND_RPC_SEND_RAW_TX {