aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cryptonote_core/blockchain.cpp196
-rw-r--r--src/cryptonote_core/blockchain.h56
2 files changed, 179 insertions, 73 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index dec0b42ba..3c855d6cd 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -1571,6 +1571,98 @@ 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);
oen.out_key = data.pubkey;
}
+
+uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const
+{
+ auto num_outs = m_db->get_num_outputs(amount);
+ // 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(amount, 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;
+ }
+
+ return num_outs;
+}
+
+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;
+
+ // if there aren't enough outputs to mix with (or just enough),
+ // use all of them. Eventually this should become impossible.
+ if (num_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(amount, i);
+
+ // if tx is unlocked, add output to indices
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ indices.push_back(i);
+ }
+ }
+ }
+ else
+ {
+ // while we still need more mixins
+ while (indices.size() < 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(amount, 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)))
+ {
+ 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.
@@ -1585,80 +1677,18 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT
// 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);
- // 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(amount, 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;
- }
-
// 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::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(amount, 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_random_outs(result_outs, amount, i);
- }
+ std::vector<uint64_t> indices = get_random_outputs(amount, req.outs_count);
- }
- }
- else
+ for (auto i : indices)
{
- // while we still need more mixins
- while (result_outs.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;
- }
+ 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());
- // 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(amount, 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_random_outs(result_outs, amount, i);
- }
- }
+ oe.global_amount_index = i;
+ oe.out_key = get_output_key(amount, i);
}
}
return true;
@@ -1816,6 +1846,15 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA
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
// 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.
@@ -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
// (by reference) the most recent common block hash along with up to
// 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__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
// 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;
}
m_db->block_txn_start(true);
- resp.total_height = get_current_blockchain_height();
+ current_height = get_current_blockchain_height();
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();
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
// find split point between ours and foreign blockchain (or start at
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 7a138a634..e2da535cd 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -374,6 +374,22 @@ namespace cryptonote
* 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
+ *
+ * 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 resp return-by-reference the split height and subsequent blocks' hashes
*
* @return true if a block found in common, else false
@@ -427,6 +443,35 @@ namespace cryptonote
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
*
* This function takes an RPC request for outputs to mix with
@@ -458,6 +503,17 @@ namespace cryptonote
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
*
* This function takes an RPC request for outputs to mix with