Refactor some things into more composable (smaller) functions

This commit refactors some of the rpc-related functions in the
Blockchain class to be more composable.  This change was made
in order to make implementing the new zmq rpc easier without
trampling on the old rpc.

New functions:
  Blockchain::get_num_mature_outputs
  Blockchain::get_random_outputs
  Blockchain::get_output_key
  Blockchain::get_output_key_mask_unlocked

  Blockchain::find_blockchain_supplement (overload)

functions which previously had this functionality inline now call these
functions as necessary.
This commit is contained in:
Thomas Winget 2017-04-06 15:01:29 -04:00
parent 9ac2ad0744
commit 5c1e08fe80
No known key found for this signature in database
GPG key ID: 58131A160789E630
2 changed files with 179 additions and 73 deletions

View file

@ -1571,20 +1571,9 @@ void Blockchain::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_A
output_data_t data = m_db->get_output_key(amount, i); output_data_t data = m_db->get_output_key(amount, i);
oen.out_key = data.pubkey; oen.out_key = data.pubkey;
} }
//------------------------------------------------------------------
// 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_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::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 uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const
// from BlockchainDB where <n> is req.outs_count (number of mixins). {
for (uint64_t amount : req.amounts)
{
auto num_outs = m_db->get_num_outputs(amount); auto num_outs = m_db->get_num_outputs(amount);
// ensure we don't include outputs that aren't yet eligible to be used // ensure we don't include outputs that aren't yet eligible to be used
// outpouts are sorted by height // outpouts are sorted by height
@ -1597,33 +1586,37 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT
--num_outs; --num_outs;
} }
// create outs_for_amount struct and populate amount field return num_outs;
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount()); }
result_outs.amount = amount;
std::vector<uint64_t> Blockchain::get_random_outputs(uint64_t amount, uint64_t count) const
{
uint64_t num_outs = get_num_mature_outputs(amount);
std::vector<uint64_t> indices;
std::unordered_set<uint64_t> seen_indices; std::unordered_set<uint64_t> seen_indices;
// if there aren't enough outputs to mix with (or just enough), // if there aren't enough outputs to mix with (or just enough),
// use all of them. Eventually this should become impossible. // use all of them. Eventually this should become impossible.
if (num_outs <= req.outs_count) if (num_outs <= count)
{ {
for (uint64_t i = 0; i < num_outs; i++) for (uint64_t i = 0; i < num_outs; i++)
{ {
// get tx_hash, tx_out_index from DB // get tx_hash, tx_out_index from DB
tx_out_index toi = m_db->get_output_tx_and_index(amount, i); tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
// if tx is unlocked, add output to result_outs // if tx is unlocked, add output to indices
if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
{ {
add_out_to_get_random_outs(result_outs, amount, i); indices.push_back(i);
} }
} }
} }
else else
{ {
// while we still need more mixins // while we still need more mixins
while (result_outs.outs.size() < req.outs_count) while (indices.size() < count)
{ {
// if we've gone through every possible output, we've gotten all we can // if we've gone through every possible output, we've gotten all we can
if (seen_indices.size() == num_outs) if (seen_indices.size() == num_outs)
@ -1656,10 +1649,47 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT
// our list. // our list.
if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
{ {
add_out_to_get_random_outs(result_outs, amount, i); indices.push_back(i);
} }
} }
} }
return indices;
}
crypto::public_key Blockchain::get_output_key(uint64_t amount, uint64_t global_index) const
{
output_data_t data = m_db->get_output_key(amount, global_index);
return data.pubkey;
}
//------------------------------------------------------------------
// 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_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::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).
for (uint64_t amount : req.amounts)
{
// create outs_for_amount struct and populate amount field
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount());
result_outs.amount = amount;
std::vector<uint64_t> indices = get_random_outputs(amount, req.outs_count);
for (auto i : indices)
{
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oe = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
oe.global_amount_index = i;
oe.out_key = get_output_key(amount, i);
}
} }
return true; return true;
} }
@ -1816,6 +1846,15 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
void Blockchain::get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const
{
const auto o_data = m_db->get_output_key(amount, index);
key = o_data.pubkey;
mask = o_data.commitment;
tx_out_index toi = m_db->get_output_tx_and_index(amount, index);
unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first));
}
//------------------------------------------------------------------
// This function takes a list of block hashes from another node // This function takes a list of block hashes from another node
// on the network to find where the split point is between us and them. // on the network to find where the split point is between us and them.
// This is used to see what to send another node that needs to sync. // This is used to see what to send another node that needs to sync.
@ -2025,28 +2064,39 @@ void Blockchain::print_blockchain_outs(const std::string& file) const
// Find the split point between us and foreign blockchain and return // Find the split point between us and foreign blockchain and return
// (by reference) the most recent common block hash along with up to // (by reference) the most recent common block hash along with up to
// BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes. // BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock); CRITICAL_REGION_LOCAL(m_blockchain_lock);
// if we can't find the split point, return false // if we can't find the split point, return false
if(!find_blockchain_supplement(qblock_ids, resp.start_height)) if(!find_blockchain_supplement(qblock_ids, start_height))
{ {
return false; return false;
} }
m_db->block_txn_start(true); m_db->block_txn_start(true);
resp.total_height = get_current_blockchain_height(); current_height = get_current_blockchain_height();
size_t count = 0; size_t count = 0;
for(size_t i = resp.start_height; i < resp.total_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) for(size_t i = start_height; i < current_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
{ {
resp.m_block_ids.push_back(m_db->get_block_hash_from_height(i)); hashes.push_back(m_db->get_block_hash_from_height(i));
} }
resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1);
m_db->block_txn_stop(); m_db->block_txn_stop();
return true; return true;
} }
bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height);
resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1);
return result;
}
//------------------------------------------------------------------ //------------------------------------------------------------------
//FIXME: change argument to std::vector, low priority //FIXME: change argument to std::vector, low priority
// find split point between ours and foreign blockchain (or start at // find split point between ours and foreign blockchain (or start at

View file

@ -366,6 +366,22 @@ namespace cryptonote
*/ */
bool get_short_chain_history(std::list<crypto::hash>& ids) const; bool get_short_chain_history(std::list<crypto::hash>& ids) const;
/**
* @brief get recent block hashes for a foreign chain
*
* Find the split point between us and foreign blockchain and return
* (by reference) the most recent common block hash along with up to
* BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
*
* @param qblock_ids the foreign chain's "short history" (see get_short_chain_history)
* @param hashes the hashes to be returned, return-by-reference
* @param start_height the start height, return-by-reference
* @param current_height the current blockchain height, return-by-reference
*
* @return true if a block found in common, else false
*/
bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const;
/** /**
* @brief get recent block hashes for a foreign chain * @brief get recent block hashes for a foreign chain
* *
@ -426,6 +442,35 @@ namespace cryptonote
*/ */
bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp); bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp);
/**
* @brief get number of outputs of an amount past the minimum spendable age
*
* @param amount the output amount
*
* @return the number of mature outputs
*/
uint64_t get_num_mature_outputs(uint64_t amount) const;
/**
* @brief get random outputs (indices) for an amount
*
* @param amount the amount
* @param count the number of random outputs to choose
*
* @return the outputs' amount-global indices
*/
std::vector<uint64_t> get_random_outputs(uint64_t amount, uint64_t count) const;
/**
* @brief get the public key for an output
*
* @param amount the output amount
* @param global_index the output amount-global index
*
* @return the public key
*/
crypto::public_key get_output_key(uint64_t amount, uint64_t global_index) const;
/** /**
* @brief gets random outputs to mix with * @brief gets random outputs to mix with
* *
@ -457,6 +502,17 @@ namespace cryptonote
*/ */
bool get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const; bool get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const;
/**
* @brief gets an output's key and unlocked state
*
* @param amount in - the output amount
* @param index in - the output global amount index
* @param mask out - the output's RingCT mask
* @param key out - the output's key
* @param unlocked out - the output's unlocked state
*/
void get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const;
/** /**
* @brief gets random ringct outputs to mix with * @brief gets random ringct outputs to mix with
* *