core: new /getrandom_rctouts.bin binary RPC call

to get random ringct outputs to mix with
This commit is contained in:
moneromooo-monero 2016-06-05 10:46:18 +01:00
parent c3a2e1450a
commit 4258dab4d6
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
7 changed files with 192 additions and 0 deletions

View file

@ -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<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& 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 <n> random outputs
// from BlockchainDB where <n> 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<uint64_t> 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>() % ((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__);

View file

@ -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<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, size_t i) const;
/**
* @brief checks if a transaction is unlocked (its outputs spendable)
*

View file

@ -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<uint64_t>& indexs) const
{
return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs);

View file

@ -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

View file

@ -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();

View file

@ -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);

View file

@ -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<out_entry> 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
{