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;
|
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
|
bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const
|
||||||
{
|
{
|
||||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
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;
|
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
|
* @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;
|
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)
|
* @brief checks if a transaction is unlocked (its outputs spendable)
|
||||||
*
|
*
|
||||||
|
|
|
@ -706,6 +706,11 @@ namespace cryptonote
|
||||||
return m_blockchain_storage.get_outs(req, res);
|
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
|
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);
|
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;
|
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
|
* @copydoc miner::pause
|
||||||
|
|
|
@ -250,6 +250,30 @@ namespace cryptonote
|
||||||
return true;
|
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)
|
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();
|
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("/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("/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("/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("/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("/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)
|
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_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_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_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_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_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);
|
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()
|
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
|
struct COMMAND_RPC_SEND_RAW_TX
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue