mirror of https://github.com/oxen-io/oxen-core.git
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:
parent
b83b5c8f8d
commit
360f07774e
|
@ -129,7 +129,8 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool, service_nodes::service_node_list
|
|||
m_difficulty_for_next_block_top_hash(crypto::null_hash),
|
||||
m_difficulty_for_next_block(1),
|
||||
m_service_node_list(service_node_list),
|
||||
m_deregister_vote_pool(deregister_vote_pool)
|
||||
m_deregister_vote_pool(deregister_vote_pool),
|
||||
m_btc_valid(false)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
}
|
||||
|
@ -610,6 +611,7 @@ block Blockchain::pop_block_from_blockchain()
|
|||
|
||||
update_next_cumulative_size_limit();
|
||||
m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
|
||||
invalidate_block_template_cache();
|
||||
|
||||
return popped_block;
|
||||
}
|
||||
|
@ -620,6 +622,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
|
|||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
m_timestamps_and_difficulties_height = 0;
|
||||
m_alternative_chains.clear();
|
||||
invalidate_block_template_cache();
|
||||
m_db->reset();
|
||||
m_hardfork->init();
|
||||
|
||||
|
@ -1251,9 +1254,26 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
size_t median_size;
|
||||
uint64_t already_generated_coins;
|
||||
uint64_t pool_cookie;
|
||||
|
||||
CRITICAL_REGION_BEGIN(m_blockchain_lock);
|
||||
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.minor_version = m_hardfork->get_ideal_version();
|
||||
|
@ -1281,6 +1301,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
{
|
||||
return false;
|
||||
}
|
||||
pool_cookie = m_tx_pool.cookie();
|
||||
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
|
||||
size_t real_txs_size = 0;
|
||||
uint64_t real_fee = 0;
|
||||
|
@ -1398,6 +1419,8 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size <<
|
||||
", cumulative size " << cumulative_size << " is now good");
|
||||
#endif
|
||||
|
||||
cache_block_template(b, miner_address, ex_nonce, diffic, expected_reward, pool_cookie);
|
||||
return true;
|
||||
}
|
||||
LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
|
||||
|
@ -3834,6 +3857,7 @@ leave:
|
|||
// appears to be a NOP *and* is called elsewhere. wat?
|
||||
m_tx_pool.on_blockchain_inc(new_height, id);
|
||||
get_difficulty_for_next_block(); // just to cache it
|
||||
invalidate_block_template_cache();
|
||||
|
||||
// New height is the height of the block we just mined. We want (new_height
|
||||
// + 1) because our age checks for deregister votes is now (age >=
|
||||
|
@ -4830,6 +4854,24 @@ void Blockchain::hook_validate_miner_tx(Blockchain::ValidateMinerTxHook& validat
|
|||
m_validate_miner_tx_hooks.push_back(&validate_miner_tx_hook);
|
||||
}
|
||||
|
||||
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 {
|
||||
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;
|
||||
|
|
|
@ -1107,6 +1107,15 @@ namespace cryptonote
|
|||
|
||||
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
|
||||
*
|
||||
|
@ -1462,5 +1471,17 @@ namespace cryptonote
|
|||
* that implicit data.
|
||||
*/
|
||||
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
|
||||
|
|
|
@ -106,7 +106,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)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -354,6 +354,8 @@ namespace cryptonote
|
|||
tvc.m_verifivation_failed = false;
|
||||
m_txpool_size += blob_size;
|
||||
|
||||
++m_cookie;
|
||||
|
||||
MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size));
|
||||
|
||||
prune(m_txpool_max_size);
|
||||
|
@ -389,6 +391,7 @@ namespace cryptonote
|
|||
bytes = m_txpool_max_size;
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
LockedTXN lock(m_blockchain);
|
||||
bool changed = false;
|
||||
|
||||
for (auto it = m_txs_by_fee_and_receive_time.begin(); it != m_txs_by_fee_and_receive_time.end(); )
|
||||
{
|
||||
|
@ -426,6 +429,7 @@ namespace cryptonote
|
|||
remove_transaction_keyimages(tx);
|
||||
MINFO("Pruned deregister tx " << txid << " from txpool");
|
||||
it = m_txs_by_fee_and_receive_time.erase(it);
|
||||
changed = true;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
@ -469,6 +473,7 @@ namespace cryptonote
|
|||
remove_transaction_keyimages(tx);
|
||||
MINFO("Pruned tx " << txid << " from txpool: size: " << txblob.size() << ", fee/byte: " << std::get<1>(it->first));
|
||||
it = --m_txs_by_fee_and_receive_time.erase(it);
|
||||
changed = true;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
@ -476,6 +481,8 @@ namespace cryptonote
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
++m_cookie;
|
||||
if (m_txpool_size > bytes)
|
||||
MINFO("Pool size after pruning is larger than limit: " << m_txpool_size << "/" << bytes);
|
||||
}
|
||||
|
@ -493,6 +500,7 @@ namespace cryptonote
|
|||
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");
|
||||
}
|
||||
++m_cookie;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
|
@ -527,6 +535,7 @@ namespace cryptonote
|
|||
}
|
||||
|
||||
}
|
||||
++m_cookie;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
|
@ -572,6 +581,7 @@ namespace cryptonote
|
|||
}
|
||||
|
||||
m_txs_by_fee_and_receive_time.erase(sorted_it);
|
||||
++m_cookie;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
|
@ -645,6 +655,7 @@ namespace cryptonote
|
|||
// ignore error
|
||||
}
|
||||
}
|
||||
++m_cookie;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1166,6 +1177,7 @@ namespace cryptonote
|
|||
{
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
bool changed = false;
|
||||
LockedTXN lock(m_blockchain);
|
||||
for(size_t i = 0; i!= tx.vin.size(); i++)
|
||||
{
|
||||
|
@ -1186,6 +1198,7 @@ namespace cryptonote
|
|||
{
|
||||
MDEBUG("Marking " << txid << " as double spending " << itk.k_image);
|
||||
meta.double_spend_seen = true;
|
||||
changed = true;
|
||||
try
|
||||
{
|
||||
m_blockchain.update_txpool_tx(txid, meta);
|
||||
|
@ -1199,6 +1212,8 @@ namespace cryptonote
|
|||
}
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
++m_cookie;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
std::string tx_memory_pool::print_pool(bool short_format) const
|
||||
|
@ -1420,6 +1435,8 @@ namespace cryptonote
|
|||
}
|
||||
}
|
||||
}
|
||||
if (n_removed > 0)
|
||||
++m_cookie;
|
||||
return n_removed;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
|
@ -1476,6 +1493,10 @@ namespace cryptonote
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_cookie = 0;
|
||||
|
||||
// Ignore deserialization error
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -360,6 +360,13 @@ namespace cryptonote
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
@ -555,6 +562,8 @@ private:
|
|||
//!< container for transactions organized by fee per size 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
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue