mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-11 16:17:54 +02:00
kernel: De-globalize script execution cache
Move its ownership to the ChainstateManager class. Next to simplifying usage of the kernel library by no longer requiring manual setup of the cache prior to using validation code, it also slims down the amount of memory allocated by BasicTestingSetup.
This commit is contained in:
@@ -134,6 +134,7 @@ const CBlockIndex* Chainstate::FindForkInGlobalIndex(const CBlockLocator& locato
|
||||
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
|
||||
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
|
||||
ValidationCache& validation_cache,
|
||||
std::vector<CScriptCheck>* pvChecks = nullptr)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
@@ -394,7 +395,8 @@ void Chainstate::MaybeUpdateMempoolForReorg(
|
||||
* */
|
||||
static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& view, const CTxMemPool& pool,
|
||||
unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip)
|
||||
unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip,
|
||||
ValidationCache& validation_cache)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
@@ -426,7 +428,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
|
||||
}
|
||||
|
||||
// Call CheckInputScripts() to cache signature and script validity against current tip consensus rules.
|
||||
return CheckInputScripts(tx, state, view, flags, /* cacheSigStore= */ true, /* cacheFullScriptStore= */ true, txdata);
|
||||
return CheckInputScripts(tx, state, view, flags, /* cacheSigStore= */ true, /* cacheFullScriptStore= */ true, txdata, validation_cache);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -716,6 +718,11 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
ValidationCache& GetValidationCache()
|
||||
{
|
||||
return m_active_chainstate.m_chainman.m_validation_cache;
|
||||
}
|
||||
|
||||
private:
|
||||
CTxMemPool& m_pool;
|
||||
CCoinsViewCache m_view;
|
||||
@@ -1231,13 +1238,13 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
|
||||
|
||||
// Check input scripts and signatures.
|
||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
||||
if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata)) {
|
||||
if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata, GetValidationCache())) {
|
||||
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
|
||||
// need to turn both off, and compare against just turning off CLEANSTACK
|
||||
// to see if the failure is specifically due to witness validation.
|
||||
TxValidationState state_dummy; // Want reported failures to be from first CheckInputScripts
|
||||
if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, ws.m_precomputed_txdata) &&
|
||||
!CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, ws.m_precomputed_txdata)) {
|
||||
if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, ws.m_precomputed_txdata, GetValidationCache()) &&
|
||||
!CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, ws.m_precomputed_txdata, GetValidationCache())) {
|
||||
// Only the witness is missing, so the transaction itself may be fine.
|
||||
state.Invalid(TxValidationResult::TX_WITNESS_STRIPPED,
|
||||
state.GetRejectReason(), state.GetDebugMessage());
|
||||
@@ -1273,7 +1280,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
|
||||
// transactions into the mempool can be exploited as a DoS attack.
|
||||
unsigned int currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)};
|
||||
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags,
|
||||
ws.m_precomputed_txdata, m_active_chainstate.CoinsTip())) {
|
||||
ws.m_precomputed_txdata, m_active_chainstate.CoinsTip(), GetValidationCache())) {
|
||||
LogPrintf("BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s\n", hash.ToString(), state.ToString());
|
||||
return Assume(false);
|
||||
}
|
||||
@@ -2087,10 +2094,9 @@ bool CScriptCheck::operator()() {
|
||||
return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *txdata), &error);
|
||||
}
|
||||
|
||||
static CuckooCache::cache<uint256, SignatureCacheHasher> g_scriptExecutionCache;
|
||||
static CSHA256 g_scriptExecutionCacheHasher;
|
||||
|
||||
bool InitScriptExecutionCache(size_t max_size_bytes)
|
||||
ValidationCache::ValidationCache(const size_t script_execution_cache_bytes)
|
||||
{
|
||||
// Setup the salted hasher
|
||||
uint256 nonce = GetRandHash();
|
||||
@@ -2100,10 +2106,9 @@ bool InitScriptExecutionCache(size_t max_size_bytes)
|
||||
g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
|
||||
g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
|
||||
|
||||
const auto [num_elems, approx_size_bytes] = g_scriptExecutionCache.setup_bytes(max_size_bytes);
|
||||
const auto [num_elems, approx_size_bytes] = m_script_execution_cache.setup_bytes(script_execution_cache_bytes);
|
||||
LogPrintf("Using %zu MiB out of %zu MiB requested for script execution cache, able to store %zu elements\n",
|
||||
approx_size_bytes >> 20, max_size_bytes >> 20, num_elems);
|
||||
return true;
|
||||
approx_size_bytes >> 20, script_execution_cache_bytes >> 20, num_elems);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2128,6 +2133,7 @@ bool InitScriptExecutionCache(size_t max_size_bytes)
|
||||
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
|
||||
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
|
||||
ValidationCache& validation_cache,
|
||||
std::vector<CScriptCheck>* pvChecks)
|
||||
{
|
||||
if (tx.IsCoinBase()) return true;
|
||||
@@ -2145,7 +2151,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
||||
CSHA256 hasher = g_scriptExecutionCacheHasher;
|
||||
hasher.Write(UCharCast(tx.GetWitnessHash().begin()), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
|
||||
AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
|
||||
if (g_scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
|
||||
if (validation_cache.m_script_execution_cache.contains(hashCacheEntry, !cacheFullScriptStore)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2206,7 +2212,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
||||
if (cacheFullScriptStore && !pvChecks) {
|
||||
// We executed all of the provided scripts, and were told to
|
||||
// cache the result. Do so now.
|
||||
g_scriptExecutionCache.insert(hashCacheEntry);
|
||||
validation_cache.m_script_execution_cache.insert(hashCacheEntry);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -2664,7 +2670,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
||||
std::vector<CScriptCheck> vChecks;
|
||||
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
|
||||
TxValidationState tx_state;
|
||||
if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], parallel_script_checks ? &vChecks : nullptr)) {
|
||||
if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache, parallel_script_checks ? &vChecks : nullptr)) {
|
||||
// Any transaction validation failure in ConnectBlock is a block consensus failure
|
||||
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
|
||||
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
|
||||
@@ -6226,7 +6232,8 @@ ChainstateManager::ChainstateManager(const util::SignalInterrupt& interrupt, Opt
|
||||
: m_script_check_queue{/*batch_size=*/128, options.worker_threads_num},
|
||||
m_interrupt{interrupt},
|
||||
m_options{Flatten(std::move(options))},
|
||||
m_blockman{interrupt, std::move(blockman_options)}
|
||||
m_blockman{interrupt, std::move(blockman_options)},
|
||||
m_validation_cache{m_options.script_execution_cache_bytes}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user