core: new /getrandom_rctouts.bin binary RPC call
to get random ringct outputs to mix with
This commit is contained in:
parent
c3a2e1450a
commit
4258dab4d6
7 changed files with 192 additions and 0 deletions
|
@ -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__);
|
||||
|
|
|
@ -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)
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue