mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-05 14:06:27 -05: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:
parent
ab14d1d6a4
commit
13a3661aba
11 changed files with 70 additions and 41 deletions
|
@ -68,7 +68,6 @@ int main(int argc, char* argv[])
|
|||
// performing the check with the signature cache.
|
||||
kernel::ValidationCacheSizes validation_cache_sizes{};
|
||||
Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
|
||||
Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
|
||||
|
||||
ValidationSignals validation_signals{std::make_unique<util::ImmediateTaskRunner>()};
|
||||
|
||||
|
|
|
@ -1157,7 +1157,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
ValidationCacheSizes validation_cache_sizes{};
|
||||
ApplyArgsManOptions(args, validation_cache_sizes);
|
||||
(void)InitSignatureCache(validation_cache_sizes.signature_cache_bytes);
|
||||
(void)InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes);
|
||||
|
||||
assert(!node.scheduler);
|
||||
node.scheduler = std::make_unique<CScheduler>();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <arith_uint256.h>
|
||||
#include <dbwrapper.h>
|
||||
#include <script/sigcache.h>
|
||||
#include <txdb.h>
|
||||
#include <uint256.h>
|
||||
#include <util/time.h>
|
||||
|
@ -48,6 +49,7 @@ struct ChainstateManagerOpts {
|
|||
ValidationSignals* signals{nullptr};
|
||||
//! Number of script check worker threads. Zero means no parallel verification.
|
||||
int worker_threads_num{0};
|
||||
size_t script_execution_cache_bytes{DEFAULT_SCRIPT_EXECUTION_CACHE_BYTES};
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
namespace kernel {
|
||||
struct ValidationCacheSizes {
|
||||
size_t signature_cache_bytes{DEFAULT_MAX_SIG_CACHE_BYTES / 2};
|
||||
size_t script_execution_cache_bytes{DEFAULT_MAX_SIG_CACHE_BYTES / 2};
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,15 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& args, ChainstateManage
|
|||
opts.worker_threads_num = std::clamp(script_threads - 1, 0, MAX_SCRIPTCHECK_THREADS);
|
||||
LogPrintf("Script verification uses %d additional threads\n", opts.worker_threads_num);
|
||||
|
||||
if (auto max_size = args.GetIntArg("-maxsigcachesize")) {
|
||||
// 1. When supplied with a max_size of 0, both the signature cache and
|
||||
// script execution cache create the minimum possible cache (2
|
||||
// elements). Therefore, we can use 0 as a floor here.
|
||||
// 2. Multiply first, divide after to avoid integer truncation.
|
||||
size_t clamped_size_each = std::max<int64_t>(*max_size, 0) * (1 << 20) / 2;
|
||||
opts.script_execution_cache_bytes = clamped_size_each;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
} // namespace node
|
||||
|
|
|
@ -27,7 +27,6 @@ void ApplyArgsManOptions(const ArgsManager& argsman, ValidationCacheSizes& cache
|
|||
size_t clamped_size_each = std::max<int64_t>(*max_size, 0) * (1 << 20) / 2;
|
||||
cache_sizes = {
|
||||
.signature_cache_bytes = clamped_size_each,
|
||||
.script_execution_cache_bytes = clamped_size_each,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
// systems). Due to how we count cache size, actual memory usage is slightly
|
||||
// more (~32.25 MiB)
|
||||
static constexpr size_t DEFAULT_MAX_SIG_CACHE_BYTES{32 << 20};
|
||||
static constexpr size_t DEFAULT_SCRIPT_EXECUTION_CACHE_BYTES{DEFAULT_MAX_SIG_CACHE_BYTES / 2};
|
||||
|
||||
class CPubKey;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <consensus/validation.h>
|
||||
#include <key.h>
|
||||
#include <random.h>
|
||||
#include <script/sigcache.h>
|
||||
#include <script/sign.h>
|
||||
#include <script/signingprovider.h>
|
||||
#include <test/util/setup_common.h>
|
||||
|
@ -22,6 +23,7 @@ struct Dersig100Setup : public TestChain100Setup {
|
|||
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) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)
|
||||
|
@ -118,7 +120,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
|
|||
// should fail.
|
||||
// Capture this interaction with the upgraded_nop argument: set it when evaluating
|
||||
// any script flag that is implemented as an upgraded NOP code.
|
||||
static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip, ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
{
|
||||
PrecomputedTransactionData txdata;
|
||||
|
||||
|
@ -140,7 +142,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
|
|||
// WITNESS requires P2SH
|
||||
test_flags |= SCRIPT_VERIFY_P2SH;
|
||||
}
|
||||
bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, nullptr);
|
||||
bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, nullptr);
|
||||
// CheckInputScripts should succeed iff test_flags doesn't intersect with
|
||||
// failing_flags
|
||||
bool expected_return_value = !(test_flags & failing_flags);
|
||||
|
@ -150,13 +152,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
|
|||
if (ret && add_to_cache) {
|
||||
// Check that we get a cache hit if the tx was valid
|
||||
std::vector<CScriptCheck> scriptchecks;
|
||||
BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
|
||||
BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, &scriptchecks));
|
||||
BOOST_CHECK(scriptchecks.empty());
|
||||
} else {
|
||||
// Check that we get script executions to check, if the transaction
|
||||
// was invalid, or we didn't add to cache.
|
||||
std::vector<CScriptCheck> scriptchecks;
|
||||
BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
|
||||
BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, &scriptchecks));
|
||||
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
|
||||
}
|
||||
}
|
||||
|
@ -214,20 +216,20 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
|
|||
TxValidationState state;
|
||||
PrecomputedTransactionData ptd_spend_tx;
|
||||
|
||||
BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
|
||||
BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, m_node.chainman->m_validation_cache, nullptr));
|
||||
|
||||
// If we call again asking for scriptchecks (as happens in
|
||||
// ConnectBlock), we should add a script check object for this -- we're
|
||||
// not caching invalidity (if that changes, delete this test case).
|
||||
std::vector<CScriptCheck> scriptchecks;
|
||||
BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
|
||||
BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, m_node.chainman->m_validation_cache, &scriptchecks));
|
||||
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
|
||||
|
||||
// Test that CheckInputScripts returns true iff DERSIG-enforcing flags are
|
||||
// not present. Don't add these checks to the cache, so that we can
|
||||
// test later that block validation works fine in the absence of cached
|
||||
// successes.
|
||||
ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip());
|
||||
ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
|
||||
}
|
||||
|
||||
// And if we produce a block with this tx, it should be valid (DERSIG not
|
||||
|
@ -253,7 +255,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
|
|||
std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());
|
||||
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
|
||||
|
||||
ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip());
|
||||
ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
|
||||
}
|
||||
|
||||
// Test CHECKLOCKTIMEVERIFY
|
||||
|
@ -276,13 +278,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
|
|||
vchSig.push_back((unsigned char)SIGHASH_ALL);
|
||||
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
|
||||
|
||||
ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
|
||||
ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
|
||||
|
||||
// Make it valid, and check again
|
||||
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
|
||||
TxValidationState state;
|
||||
PrecomputedTransactionData txdata;
|
||||
BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
|
||||
BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, m_node.chainman->m_validation_cache, nullptr));
|
||||
}
|
||||
|
||||
// TEST CHECKSEQUENCEVERIFY
|
||||
|
@ -304,13 +306,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
|
|||
vchSig.push_back((unsigned char)SIGHASH_ALL);
|
||||
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
|
||||
|
||||
ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
|
||||
ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
|
||||
|
||||
// Make it valid, and check again
|
||||
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
|
||||
TxValidationState state;
|
||||
PrecomputedTransactionData txdata;
|
||||
BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
|
||||
BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, m_node.chainman->m_validation_cache, nullptr));
|
||||
}
|
||||
|
||||
// TODO: add tests for remaining script flags
|
||||
|
@ -333,11 +335,11 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
|
|||
UpdateInput(valid_with_witness_tx.vin[0], sigdata);
|
||||
|
||||
// This should be valid under all script flags.
|
||||
ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
|
||||
ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
|
||||
|
||||
// Remove the witness, and check that it is now invalid.
|
||||
valid_with_witness_tx.vin[0].scriptWitness.SetNull();
|
||||
ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip());
|
||||
ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -362,7 +364,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
|
|||
}
|
||||
|
||||
// This should be valid under all script flags
|
||||
ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
|
||||
ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
|
||||
|
||||
// Check that if the second input is invalid, but the first input is
|
||||
// valid, the transaction is not cached.
|
||||
|
@ -372,12 +374,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
|
|||
TxValidationState state;
|
||||
PrecomputedTransactionData txdata;
|
||||
// This transaction is now invalid under segwit, because of the second input.
|
||||
BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
|
||||
BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, m_node.chainman->m_validation_cache, nullptr));
|
||||
|
||||
std::vector<CScriptCheck> scriptchecks;
|
||||
// Make sure this transaction was not cached (ie because the first
|
||||
// input was valid)
|
||||
BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
|
||||
BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, m_node.chainman->m_validation_cache, &scriptchecks));
|
||||
// Should get 2 script checks back -- caching is on a whole-transaction basis.
|
||||
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
|
||||
}
|
||||
|
|
|
@ -191,7 +191,6 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
|
|||
ValidationCacheSizes validation_cache_sizes{};
|
||||
ApplyArgsManOptions(*m_node.args, validation_cache_sizes);
|
||||
Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
|
||||
Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
|
||||
|
||||
m_node.chain = interfaces::MakeChain(m_node);
|
||||
static bool noui_connected = false;
|
||||
|
|
|
@ -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}
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
#include <attributes.h>
|
||||
#include <chain.h>
|
||||
#include <checkqueue.h>
|
||||
#include <kernel/chain.h>
|
||||
#include <consensus/amount.h>
|
||||
#include <cuckoocache.h>
|
||||
#include <deploymentstatus.h>
|
||||
#include <kernel/chain.h>
|
||||
#include <kernel/chainparams.h>
|
||||
#include <kernel/chainstatemanager_opts.h>
|
||||
#include <kernel/cs_main.h> // IWYU pragma: export
|
||||
|
@ -360,8 +361,19 @@ static_assert(std::is_nothrow_move_assignable_v<CScriptCheck>);
|
|||
static_assert(std::is_nothrow_move_constructible_v<CScriptCheck>);
|
||||
static_assert(std::is_nothrow_destructible_v<CScriptCheck>);
|
||||
|
||||
/** Initializes the script-execution cache */
|
||||
[[nodiscard]] bool InitScriptExecutionCache(size_t max_size_bytes);
|
||||
/**
|
||||
* Convenience class for initializing and passing the script execution cache.
|
||||
*/
|
||||
class ValidationCache
|
||||
{
|
||||
public:
|
||||
CuckooCache::cache<uint256, SignatureCacheHasher> m_script_execution_cache;
|
||||
|
||||
ValidationCache(size_t script_execution_cache_bytes);
|
||||
|
||||
ValidationCache(const ValidationCache&) = delete;
|
||||
ValidationCache& operator=(const ValidationCache&) = delete;
|
||||
};
|
||||
|
||||
/** Functions for validating blocks and updating the block tree */
|
||||
|
||||
|
@ -796,7 +808,6 @@ private:
|
|||
friend ChainstateManager;
|
||||
};
|
||||
|
||||
|
||||
enum class SnapshotCompletionResult {
|
||||
SUCCESS,
|
||||
SKIPPED,
|
||||
|
@ -970,6 +981,8 @@ public:
|
|||
//! chainstate to avoid duplicating block metadata.
|
||||
node::BlockManager m_blockman;
|
||||
|
||||
ValidationCache m_validation_cache;
|
||||
|
||||
/**
|
||||
* Whether initial block download has ended and IsInitialBlockDownload
|
||||
* should return false from now on.
|
||||
|
|
Loading…
Add table
Reference in a new issue