Skip to content

Commit

Permalink
Fixed get_block_template_backlog performance
Browse files Browse the repository at this point in the history
Before the fix, it processed all transactions in the mempool which could be very slow when mempool grows to several MBs in size. I observed `get_block_template_backlog` taking up to 15 seconds of CPU time under high mempool load.

After the fix, only transactions that can potentially be mined in the next block will be processed (a bit more than the current block median weight).
  • Loading branch information
SChernykh committed Jul 26, 2022
1 parent 9750e1f commit f797655
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 17 deletions.
67 changes: 51 additions & 16 deletions src/cryptonote_core/tx_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -917,26 +917,61 @@ namespace cryptonote
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted;
backlog.reserve(m_blockchain.get_txpool_tx_count(include_sensitive));
txpool_tx_meta_t tmp_meta;
m_blockchain.for_all_txpool_txes([this, &backlog, &tmp_meta](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref *bd){
transaction tx;
if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, tx) : parse_and_validate_tx_from_blob(*bd, tx)))

std::vector<tx_block_template_backlog_entry> tmp;
uint64_t total_weight = 0;

// First get everything from the mempool, filter it later
m_blockchain.for_all_txpool_txes([&tmp, &total_weight](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref*){
tmp.emplace_back(tx_block_template_backlog_entry{txid, meta.weight, meta.fee});
total_weight += meta.weight;
return true;
}, false, include_sensitive ? relay_category::all : relay_category::broadcasted);

// Limit backlog to 112.5% of current median weight. This is enough to mine a full block with the optimal block reward
const uint64_t median_weight = m_blockchain.get_current_cumulative_block_weight_median();
const uint64_t max_backlog_weight = median_weight + (median_weight / 8);

// If the total weight is too high, choose the best paying transactions
if (total_weight > max_backlog_weight)
std::sort(tmp.begin(), tmp.end(), [](const auto& a, const auto& b){ return a.fee * b.weight > b.fee * a.weight; });

backlog.clear();
uint64_t w = 0;

std::unordered_set<crypto::key_image> k_images;

for (const tx_block_template_backlog_entry& e : tmp)
{
try
{
MERROR("Failed to parse tx from txpool");
// continue
return true;
}
tx.set_hash(txid);
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(e.id, meta))
continue;

tmp_meta = meta;
cryptonote::blobdata txblob;
if (!m_blockchain.get_txpool_tx_blob(e.id, txblob, relay_category::all))
continue;

if (is_transaction_ready_to_go(tmp_meta, txid, *bd, tx))
backlog.push_back({txid, meta.weight, meta.fee});
cryptonote::transaction tx;
if (is_transaction_ready_to_go(meta, e.id, txblob, tx))
{
if (have_key_images(k_images, tx))
continue;
append_key_images(k_images, tx);

return true;
}, true, category);
backlog.push_back(e);
w += e.weight;
if (w > max_backlog_weight)
break;
}
}
catch (const std::exception &e)
{
MERROR("Failed to check transaction readiness: " << e.what());
// continue, not fatal
}
}
}
//------------------------------------------------------------------
void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_sensitive) const
Expand Down
6 changes: 5 additions & 1 deletion src/cryptonote_core/tx_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,11 @@ namespace cryptonote
void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_sensitive = false) const;

/**
* @brief get (hash, weight, fee) for all transactions in the pool - the minimum required information to create a block template
* @brief get (hash, weight, fee) for transactions in the pool - the minimum required information to create a block template
*
* Not all transactions in the pool will be returned for performance reasons
* If there are too many transactions in the pool, only the highest-paying transactions
* will be returned - but enough for the miner to create a full block
*
* @param backlog return-by-reference that data
* @param include_sensitive return stempool, anonymity-pool, and unrelayed txes
Expand Down

0 comments on commit f797655

Please sign in to comment.