core: cache block template where possible

This avoids constant rechecking of the same things each time
a miner asks for the block template. The tx pool maintains
a cookie to allow users to detect when the pool state changed,
which means the block template needs rebuilding.
This commit is contained in:
moneromooo-monero 2016-04-17 11:04:01 +01:00
parent b780cf4db1
commit 82d1b74500
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
4 changed files with 94 additions and 2 deletions

View file

@ -158,7 +158,8 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool) :
m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_current_block_cumul_sz_median(0), m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_current_block_cumul_sz_median(0),
m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_cancel(false), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_cancel(false),
m_difficulty_for_next_block_top_hash(crypto::null_hash), m_difficulty_for_next_block_top_hash(crypto::null_hash),
m_difficulty_for_next_block(1) m_difficulty_for_next_block(1),
m_btc_valid(false)
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
} }
@ -632,6 +633,7 @@ block Blockchain::pop_block_from_blockchain()
update_next_cumulative_size_limit(); update_next_cumulative_size_limit();
m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id()); m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
invalidate_block_template_cache();
return popped_block; return popped_block;
} }
@ -642,6 +644,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
CRITICAL_REGION_LOCAL(m_blockchain_lock); CRITICAL_REGION_LOCAL(m_blockchain_lock);
m_timestamps_and_difficulties_height = 0; m_timestamps_and_difficulties_height = 0;
m_alternative_chains.clear(); m_alternative_chains.clear();
invalidate_block_template_cache();
m_db->reset(); m_db->reset();
m_hardfork->init(); m_hardfork->init();
@ -1212,9 +1215,26 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_size; size_t median_size;
uint64_t already_generated_coins; uint64_t already_generated_coins;
uint64_t pool_cookie;
CRITICAL_REGION_BEGIN(m_blockchain_lock); CRITICAL_REGION_BEGIN(m_blockchain_lock);
height = m_db->height(); height = m_db->height();
if (m_btc_valid) {
// The pool cookie is atomic. The lack of locking is OK, as if it changes
// just as we compare it, we'll just use a slightly old template, but
// this would be the case anyway if we'd lock, and the change happened
// just after the block template was created
if (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address)) && m_btc_nonce == ex_nonce && m_btc_pool_cookie == m_tx_pool.cookie()) {
MDEBUG("Using cached template");
m_btc.timestamp = time(NULL); // update timestamp unconditionally
b = m_btc;
diffic = m_btc_difficulty;
expected_reward = m_btc_expected_reward;
return true;
}
MDEBUG("Not using cached template: address " << (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address))) << ", nonce " << (m_btc_nonce == ex_nonce) << ", cookie " << (m_btc_pool_cookie == m_tx_pool.cookie()));
invalidate_block_template_cache();
}
b.major_version = m_hardfork->get_current_version(); b.major_version = m_hardfork->get_current_version();
b.minor_version = m_hardfork->get_ideal_version(); b.minor_version = m_hardfork->get_ideal_version();
@ -1241,6 +1261,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
{ {
return false; return false;
} }
pool_cookie = m_tx_pool.cookie();
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
size_t real_txs_size = 0; size_t real_txs_size = 0;
uint64_t real_fee = 0; uint64_t real_fee = 0;
@ -1355,6 +1376,8 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size << MDEBUG("Creating block template: miner tx size " << coinbase_blob_size <<
", cumulative size " << cumulative_size << " is now good"); ", cumulative size " << cumulative_size << " is now good");
#endif #endif
cache_block_template(b, miner_address, ex_nonce, diffic, expected_reward, pool_cookie);
return true; return true;
} }
LOG_ERROR("Failed to create_block_template with " << 10 << " tries"); LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
@ -3697,6 +3720,7 @@ leave:
// appears to be a NOP *and* is called elsewhere. wat? // appears to be a NOP *and* is called elsewhere. wat?
m_tx_pool.on_blockchain_inc(new_height, id); m_tx_pool.on_blockchain_inc(new_height, id);
get_difficulty_for_next_block(); // just to cache it get_difficulty_for_next_block(); // just to cache it
invalidate_block_template_cache();
return true; return true;
} }
@ -4666,6 +4690,24 @@ bool Blockchain::for_all_outputs(uint64_t amount, std::function<bool(uint64_t he
return m_db->for_all_outputs(amount, f);; return m_db->for_all_outputs(amount, f);;
} }
void Blockchain::invalidate_block_template_cache()
{
MDEBUG("Invalidating block template cache");
m_btc_valid = false;
}
void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t expected_reward, uint64_t pool_cookie)
{
MDEBUG("Setting block template cache");
m_btc = b;
m_btc_address = address;
m_btc_nonce = nonce;
m_btc_difficulty = diff;
m_btc_expected_reward = expected_reward;
m_btc_pool_cookie = pool_cookie;
m_btc_valid = true;
}
namespace cryptonote { namespace cryptonote {
template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::vector<transaction>&, std::vector<crypto::hash>&) const; template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::vector<transaction>&, std::vector<crypto::hash>&) const;
template bool Blockchain::get_transactions_blobs(const std::vector<crypto::hash>&, std::vector<cryptonote::blobdata>&, std::vector<crypto::hash>&, bool) const; template bool Blockchain::get_transactions_blobs(const std::vector<crypto::hash>&, std::vector<cryptonote::blobdata>&, std::vector<crypto::hash>&, bool) const;

View file

@ -1052,6 +1052,15 @@ namespace cryptonote
std::atomic<bool> m_cancel; std::atomic<bool> m_cancel;
// block template cache
block m_btc;
account_public_address m_btc_address;
blobdata m_btc_nonce;
difficulty_type m_btc_difficulty;
uint64_t m_btc_pool_cookie;
uint64_t m_btc_expected_reward;
bool m_btc_valid;
/** /**
* @brief collects the keys for all outputs being "spent" as an input * @brief collects the keys for all outputs being "spent" as an input
* *
@ -1407,5 +1416,17 @@ namespace cryptonote
* that implicit data. * that implicit data.
*/ */
bool expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys); bool expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys);
/**
* @brief invalidates any cached block template
*/
void invalidate_block_template_cache();
/**
* @brief stores a new cached block template
*
* At some point, may be used to push an update to miners
*/
void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t expected_reward, uint64_t pool_cookie);
}; };
} // namespace cryptonote } // namespace cryptonote

View file

@ -102,7 +102,7 @@ namespace cryptonote
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_size(DEFAULT_TXPOOL_MAX_SIZE), m_txpool_size(0) tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_size(DEFAULT_TXPOOL_MAX_SIZE), m_txpool_size(0), m_cookie(0)
{ {
} }
@ -306,6 +306,8 @@ namespace cryptonote
tvc.m_verifivation_failed = false; tvc.m_verifivation_failed = false;
m_txpool_size += blob_size; m_txpool_size += blob_size;
++m_cookie;
MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size)); MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size));
prune(m_txpool_max_size); prune(m_txpool_max_size);
@ -341,6 +343,7 @@ namespace cryptonote
bytes = m_txpool_max_size; bytes = m_txpool_max_size;
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
LockedTXN lock(m_blockchain); LockedTXN lock(m_blockchain);
bool changed = false;
// this will never remove the first one, but we don't care // this will never remove the first one, but we don't care
auto it = --m_txs_by_fee_and_receive_time.end(); auto it = --m_txs_by_fee_and_receive_time.end();
@ -377,6 +380,7 @@ namespace cryptonote
remove_transaction_keyimages(tx); remove_transaction_keyimages(tx);
MINFO("Pruned tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first); MINFO("Pruned tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first);
m_txs_by_fee_and_receive_time.erase(it--); m_txs_by_fee_and_receive_time.erase(it--);
changed = true;
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@ -384,6 +388,8 @@ namespace cryptonote
return; return;
} }
} }
if (changed)
++m_cookie;
if (m_txpool_size > bytes) if (m_txpool_size > bytes)
MINFO("Pool size after pruning is larger than limit: " << m_txpool_size << "/" << bytes); MINFO("Pool size after pruning is larger than limit: " << m_txpool_size << "/" << bytes);
} }
@ -401,6 +407,7 @@ namespace cryptonote
auto ins_res = kei_image_set.insert(id); auto ins_res = kei_image_set.insert(id);
CHECK_AND_ASSERT_MES(ins_res.second, false, "internal error: try to insert duplicate iterator in key_image set"); CHECK_AND_ASSERT_MES(ins_res.second, false, "internal error: try to insert duplicate iterator in key_image set");
} }
++m_cookie;
return true; return true;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -435,6 +442,7 @@ namespace cryptonote
} }
} }
++m_cookie;
return true; return true;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -480,6 +488,7 @@ namespace cryptonote
} }
m_txs_by_fee_and_receive_time.erase(sorted_it); m_txs_by_fee_and_receive_time.erase(sorted_it);
++m_cookie;
return true; return true;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -553,6 +562,7 @@ namespace cryptonote
// ignore error // ignore error
} }
} }
++m_cookie;
} }
return true; return true;
} }
@ -1051,6 +1061,7 @@ namespace cryptonote
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
bool changed = false;
LockedTXN lock(m_blockchain); LockedTXN lock(m_blockchain);
for(size_t i = 0; i!= tx.vin.size(); i++) for(size_t i = 0; i!= tx.vin.size(); i++)
{ {
@ -1071,6 +1082,7 @@ namespace cryptonote
{ {
MDEBUG("Marking " << txid << " as double spending " << itk.k_image); MDEBUG("Marking " << txid << " as double spending " << itk.k_image);
meta.double_spend_seen = true; meta.double_spend_seen = true;
changed = true;
try try
{ {
m_blockchain.update_txpool_tx(txid, meta); m_blockchain.update_txpool_tx(txid, meta);
@ -1084,6 +1096,8 @@ namespace cryptonote
} }
} }
} }
if (changed)
++m_cookie;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
std::string tx_memory_pool::print_pool(bool short_format) const std::string tx_memory_pool::print_pool(bool short_format) const
@ -1305,6 +1319,8 @@ namespace cryptonote
} }
} }
} }
if (n_removed > 0)
++m_cookie;
return n_removed; return n_removed;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -1361,6 +1377,10 @@ namespace cryptonote
} }
} }
} }
m_cookie = 0;
// Ignore deserialization error
return true; return true;
} }

View file

@ -362,6 +362,13 @@ namespace cryptonote
*/ */
size_t validate(uint8_t version); size_t validate(uint8_t version);
/**
* @brief return the cookie
*
* @return the cookie
*/
uint64_t cookie() const { return m_cookie; }
/** /**
* @brief get the cumulative txpool size in bytes * @brief get the cumulative txpool size in bytes
* *
@ -549,6 +556,8 @@ private:
//!< container for transactions organized by fee per size and receive time //!< container for transactions organized by fee per size and receive time
sorted_tx_container m_txs_by_fee_and_receive_time; sorted_tx_container m_txs_by_fee_and_receive_time;
std::atomic<uint64_t> m_cookie; //!< incremented at each change
/** /**
* @brief get an iterator to a transaction in the sorted container * @brief get an iterator to a transaction in the sorted container
* *