blockchain_db: speedup tx output gathering
We know all the data we'll want for getblocks.bin is contiguous
This commit is contained in:
parent
6bc0c7e685
commit
008647d7eb
|
@ -1329,10 +1329,11 @@ public:
|
|||
* If an output cannot be found, the subclass should throw OUTPUT_DNE.
|
||||
*
|
||||
* @param tx_id a transaction ID
|
||||
* @param n_txes how many txes to get data for, starting with tx_id
|
||||
*
|
||||
* @return a list of amount-specific output indices
|
||||
*/
|
||||
virtual std::vector<uint64_t> get_tx_amount_output_indices(const uint64_t tx_id) const = 0;
|
||||
virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_id, size_t n_txes = 1) const = 0;
|
||||
|
||||
/**
|
||||
* @brief check if a key image is stored as spent
|
||||
|
|
|
@ -1019,7 +1019,8 @@ void BlockchainLMDB::remove_tx_outputs(const uint64_t tx_id, const transaction&
|
|||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
|
||||
std::vector<uint64_t> amount_output_indices = get_tx_amount_output_indices(tx_id);
|
||||
std::vector<std::vector<uint64_t>> amount_output_indices_set = get_tx_amount_output_indices(tx_id, 1);
|
||||
const std::vector<uint64_t> &amount_output_indices = amount_output_indices_set.front();
|
||||
|
||||
if (amount_output_indices.empty())
|
||||
{
|
||||
|
@ -2628,7 +2629,7 @@ tx_out_index BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, con
|
|||
return indices[0];
|
||||
}
|
||||
|
||||
std::vector<uint64_t> BlockchainLMDB::get_tx_amount_output_indices(const uint64_t tx_id) const
|
||||
std::vector<std::vector<uint64_t>> BlockchainLMDB::get_tx_amount_output_indices(uint64_t tx_id, size_t n_txes) const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
|
||||
|
@ -2637,35 +2638,40 @@ std::vector<uint64_t> BlockchainLMDB::get_tx_amount_output_indices(const uint64_
|
|||
TXN_PREFIX_RDONLY();
|
||||
RCURSOR(tx_outputs);
|
||||
|
||||
int result = 0;
|
||||
MDB_val_set(k_tx_id, tx_id);
|
||||
MDB_val v;
|
||||
std::vector<uint64_t> amount_output_indices;
|
||||
std::vector<std::vector<uint64_t>> amount_output_indices_set;
|
||||
amount_output_indices_set.reserve(n_txes);
|
||||
|
||||
result = mdb_cursor_get(m_cur_tx_outputs, &k_tx_id, &v, MDB_SET);
|
||||
if (result == MDB_NOTFOUND)
|
||||
LOG_PRINT_L0("WARNING: Unexpected: tx has no amount indices stored in "
|
||||
"tx_outputs, but it should have an empty entry even if it's a tx without "
|
||||
"outputs");
|
||||
else if (result)
|
||||
throw0(DB_ERROR(lmdb_error("DB error attempting to get data for tx_outputs[tx_index]", result).c_str()));
|
||||
|
||||
const uint64_t* indices = (const uint64_t*)v.mv_data;
|
||||
int num_outputs = v.mv_size / sizeof(uint64_t);
|
||||
|
||||
amount_output_indices.reserve(num_outputs);
|
||||
for (int i = 0; i < num_outputs; ++i)
|
||||
MDB_cursor_op op = MDB_SET;
|
||||
while (n_txes-- > 0)
|
||||
{
|
||||
// LOG_PRINT_L0("amount output index[" << 2*i << "]" << ": " << paired_indices[2*i] << " global output index: " << paired_indices[2*i+1]);
|
||||
amount_output_indices.push_back(indices[i]);
|
||||
int result = mdb_cursor_get(m_cur_tx_outputs, &k_tx_id, &v, op);
|
||||
if (result == MDB_NOTFOUND)
|
||||
LOG_PRINT_L0("WARNING: Unexpected: tx has no amount indices stored in "
|
||||
"tx_outputs, but it should have an empty entry even if it's a tx without "
|
||||
"outputs");
|
||||
else if (result)
|
||||
throw0(DB_ERROR(lmdb_error("DB error attempting to get data for tx_outputs[tx_index]", result).c_str()));
|
||||
|
||||
op = MDB_NEXT;
|
||||
|
||||
const uint64_t* indices = (const uint64_t*)v.mv_data;
|
||||
size_t num_outputs = v.mv_size / sizeof(uint64_t);
|
||||
|
||||
amount_output_indices_set.resize(amount_output_indices_set.size() + 1);
|
||||
std::vector<uint64_t> &amount_output_indices = amount_output_indices_set.back();
|
||||
amount_output_indices.reserve(num_outputs);
|
||||
for (size_t i = 0; i < num_outputs; ++i)
|
||||
{
|
||||
amount_output_indices.push_back(indices[i]);
|
||||
}
|
||||
}
|
||||
indices = nullptr;
|
||||
|
||||
TXN_POSTFIX_RDONLY();
|
||||
return amount_output_indices;
|
||||
return amount_output_indices_set;
|
||||
}
|
||||
|
||||
|
||||
bool BlockchainLMDB::has_key_image(const crypto::key_image& img) const
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
|
|
|
@ -252,7 +252,7 @@ public:
|
|||
virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const;
|
||||
virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<tx_out_index> &indices) const;
|
||||
|
||||
virtual std::vector<uint64_t> get_tx_amount_output_indices(const uint64_t tx_id) const;
|
||||
virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_id, size_t n_txes) const;
|
||||
|
||||
virtual bool has_key_image(const crypto::key_image& img) const;
|
||||
|
||||
|
|
|
@ -2297,6 +2297,22 @@ bool Blockchain::check_for_double_spend(const transaction& tx, key_images_contai
|
|||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, size_t n_txes, std::vector<std::vector<uint64_t>>& indexs) const
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
uint64_t tx_index;
|
||||
if (!m_db->tx_exists(tx_id, tx_index))
|
||||
{
|
||||
MERROR_VER("get_tx_outputs_gindexs failed to find transaction with id = " << tx_id);
|
||||
return false;
|
||||
}
|
||||
indexs = m_db->get_tx_amount_output_indices(tx_index, n_txes);
|
||||
CHECK_AND_ASSERT_MES(n_txes == indexs.size(), false, "Wrong indexs size");
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
|
@ -2307,16 +2323,9 @@ bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<u
|
|||
MERROR_VER("get_tx_outputs_gindexs failed to find transaction with id = " << tx_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get amount output indexes, currently referred to in parts as "output global indices", but they are actually specific to amounts
|
||||
indexs = m_db->get_tx_amount_output_indices(tx_index);
|
||||
if (indexs.empty())
|
||||
{
|
||||
// empty indexs is only valid if the vout is empty, which is legal but rare
|
||||
cryptonote::transaction tx = m_db->get_tx(tx_id);
|
||||
CHECK_AND_ASSERT_MES(tx.vout.empty(), false, "internal error: global indexes for transaction " << tx_id << " is empty, and tx vout is not");
|
||||
}
|
||||
|
||||
std::vector<std::vector<uint64_t>> indices = m_db->get_tx_amount_output_indices(tx_index, 1);
|
||||
CHECK_AND_ASSERT_MES(indices.size() == 1, false, "Wrong indices size");
|
||||
indexs = indices.front();
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
|
|
|
@ -516,10 +516,12 @@ namespace cryptonote
|
|||
*
|
||||
* @param tx_id the hash of the transaction to fetch indices for
|
||||
* @param indexs return-by-reference the global indices for the transaction's outputs
|
||||
* @param n_txes how many txes in a row to get results for
|
||||
*
|
||||
* @return false if the transaction does not exist, or if no indices are found, otherwise true
|
||||
*/
|
||||
bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const;
|
||||
bool get_tx_outputs_gindexs(const crypto::hash& tx_id, size_t n_txes, std::vector<std::vector<uint64_t>>& indexs) const;
|
||||
|
||||
/**
|
||||
* @brief stores the blockchain
|
||||
|
|
|
@ -1220,6 +1220,11 @@ namespace cryptonote
|
|||
return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, size_t n_txes, std::vector<std::vector<uint64_t>>& indexs) const
|
||||
{
|
||||
return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, n_txes, indexs);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
void core::pause_mine()
|
||||
{
|
||||
m_miner.pause();
|
||||
|
|
|
@ -534,6 +534,7 @@ namespace cryptonote
|
|||
* @note see Blockchain::get_tx_outputs_gindexs
|
||||
*/
|
||||
bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const;
|
||||
bool get_tx_outputs_gindexs(const crypto::hash& tx_id, size_t n_txes, std::vector<std::vector<uint64_t>>& indexs) const;
|
||||
|
||||
/**
|
||||
* @copydoc Blockchain::get_tail_id
|
||||
|
|
|
@ -252,19 +252,11 @@ namespace cryptonote
|
|||
pruned_size += bd.first.first.size();
|
||||
unpruned_size += bd.first.first.size();
|
||||
res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices());
|
||||
res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
|
||||
if (!req.no_miner_tx)
|
||||
{
|
||||
bool r = m_core.get_tx_outputs_gindexs(bd.first.second, res.output_indices.back().indices.back().indices);
|
||||
if (!r)
|
||||
{
|
||||
res.status = "Failed";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ntxes += bd.second.size();
|
||||
res.output_indices.back().indices.reserve(1 + bd.second.size());
|
||||
if (req.no_miner_tx)
|
||||
res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
|
||||
res.blocks.back().txs.reserve(bd.second.size());
|
||||
res.output_indices.back().indices.reserve(bd.second.size());
|
||||
for (std::vector<std::pair<crypto::hash, cryptonote::blobdata>>::iterator i = bd.second.begin(); i != bd.second.end(); ++i)
|
||||
{
|
||||
unpruned_size += i->second.size();
|
||||
|
@ -272,14 +264,25 @@ namespace cryptonote
|
|||
i->second.clear();
|
||||
i->second.shrink_to_fit();
|
||||
pruned_size += res.blocks.back().txs.back().size();
|
||||
}
|
||||
|
||||
res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
|
||||
bool r = m_core.get_tx_outputs_gindexs(i->first, res.output_indices.back().indices.back().indices);
|
||||
const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 1);
|
||||
if (n_txes_to_lookup > 0)
|
||||
{
|
||||
std::vector<std::vector<uint64_t>> indices;
|
||||
bool r = m_core.get_tx_outputs_gindexs(req.no_miner_tx ? bd.second.front().first : bd.first.second, n_txes_to_lookup, indices);
|
||||
if (!r)
|
||||
{
|
||||
res.status = "Failed";
|
||||
return false;
|
||||
}
|
||||
if (indices.size() != n_txes_to_lookup || res.output_indices.back().indices.size() != (req.no_miner_tx ? 1 : 0))
|
||||
{
|
||||
res.status = "Failed";
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < indices.size(); ++i)
|
||||
res.output_indices.back().indices.push_back({std::move(indices[i])});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ public:
|
|||
virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<cryptonote::output_data_t> &outputs, bool allow_partial = false) {}
|
||||
virtual bool can_thread_bulk_indices() const { return false; }
|
||||
virtual std::vector<uint64_t> get_tx_output_indices(const crypto::hash& h) const { return std::vector<uint64_t>(); }
|
||||
virtual std::vector<uint64_t> get_tx_amount_output_indices(const uint64_t tx_index) const { return std::vector<uint64_t>(); }
|
||||
virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_index, size_t n_txes) const { return std::vector<std::vector<uint64_t>>(); }
|
||||
virtual bool has_key_image(const crypto::key_image& img) const { return false; }
|
||||
virtual void remove_block() { }
|
||||
virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const cryptonote::transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) {return 0;}
|
||||
|
|
Loading…
Reference in New Issue