Skip to content

Commit 13a3661

Browse files
committed
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.
1 parent ab14d1d commit 13a3661

11 files changed

+70
-41
lines changed

src/bitcoin-chainstate.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ int main(int argc, char* argv[])
6868
// performing the check with the signature cache.
6969
kernel::ValidationCacheSizes validation_cache_sizes{};
7070
Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
71-
Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
7271

7372
ValidationSignals validation_signals{std::make_unique<util::ImmediateTaskRunner>()};
7473

src/init.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,7 +1157,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
11571157
ValidationCacheSizes validation_cache_sizes{};
11581158
ApplyArgsManOptions(args, validation_cache_sizes);
11591159
(void)InitSignatureCache(validation_cache_sizes.signature_cache_bytes);
1160-
(void)InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes);
11611160

11621161
assert(!node.scheduler);
11631162
node.scheduler = std::make_unique<CScheduler>();

src/kernel/chainstatemanager_opts.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <arith_uint256.h>
1111
#include <dbwrapper.h>
12+
#include <script/sigcache.h>
1213
#include <txdb.h>
1314
#include <uint256.h>
1415
#include <util/time.h>
@@ -48,6 +49,7 @@ struct ChainstateManagerOpts {
4849
ValidationSignals* signals{nullptr};
4950
//! Number of script check worker threads. Zero means no parallel verification.
5051
int worker_threads_num{0};
52+
size_t script_execution_cache_bytes{DEFAULT_SCRIPT_EXECUTION_CACHE_BYTES};
5153
};
5254

5355
} // namespace kernel

src/kernel/validation_cache_sizes.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
namespace kernel {
1414
struct ValidationCacheSizes {
1515
size_t signature_cache_bytes{DEFAULT_MAX_SIG_CACHE_BYTES / 2};
16-
size_t script_execution_cache_bytes{DEFAULT_MAX_SIG_CACHE_BYTES / 2};
1716
};
1817
}
1918

src/node/chainstatemanager_args.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& args, ChainstateManage
5656
opts.worker_threads_num = std::clamp(script_threads - 1, 0, MAX_SCRIPTCHECK_THREADS);
5757
LogPrintf("Script verification uses %d additional threads\n", opts.worker_threads_num);
5858

59+
if (auto max_size = args.GetIntArg("-maxsigcachesize")) {
60+
// 1. When supplied with a max_size of 0, both the signature cache and
61+
// script execution cache create the minimum possible cache (2
62+
// elements). Therefore, we can use 0 as a floor here.
63+
// 2. Multiply first, divide after to avoid integer truncation.
64+
size_t clamped_size_each = std::max<int64_t>(*max_size, 0) * (1 << 20) / 2;
65+
opts.script_execution_cache_bytes = clamped_size_each;
66+
}
67+
5968
return {};
6069
}
6170
} // namespace node

src/node/validation_cache_args.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ void ApplyArgsManOptions(const ArgsManager& argsman, ValidationCacheSizes& cache
2727
size_t clamped_size_each = std::max<int64_t>(*max_size, 0) * (1 << 20) / 2;
2828
cache_sizes = {
2929
.signature_cache_bytes = clamped_size_each,
30-
.script_execution_cache_bytes = clamped_size_each,
3130
};
3231
}
3332
}

src/script/sigcache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// systems). Due to how we count cache size, actual memory usage is slightly
1717
// more (~32.25 MiB)
1818
static constexpr size_t DEFAULT_MAX_SIG_CACHE_BYTES{32 << 20};
19+
static constexpr size_t DEFAULT_SCRIPT_EXECUTION_CACHE_BYTES{DEFAULT_MAX_SIG_CACHE_BYTES / 2};
1920

2021
class CPubKey;
2122

src/test/txvalidationcache_tests.cpp

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <consensus/validation.h>
66
#include <key.h>
77
#include <random.h>
8+
#include <script/sigcache.h>
89
#include <script/sign.h>
910
#include <script/signingprovider.h>
1011
#include <test/util/setup_common.h>
@@ -22,6 +23,7 @@ struct Dersig100Setup : public TestChain100Setup {
2223
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
2324
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
2425
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
26+
ValidationCache& validation_cache,
2527
std::vector<CScriptCheck>* pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
2628

2729
BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)
@@ -118,7 +120,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
118120
// should fail.
119121
// Capture this interaction with the upgraded_nop argument: set it when evaluating
120122
// any script flag that is implemented as an upgraded NOP code.
121-
static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
123+
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)
122124
{
123125
PrecomputedTransactionData txdata;
124126

@@ -140,7 +142,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
140142
// WITNESS requires P2SH
141143
test_flags |= SCRIPT_VERIFY_P2SH;
142144
}
143-
bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, nullptr);
145+
bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, nullptr);
144146
// CheckInputScripts should succeed iff test_flags doesn't intersect with
145147
// failing_flags
146148
bool expected_return_value = !(test_flags & failing_flags);
@@ -150,13 +152,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
150152
if (ret && add_to_cache) {
151153
// Check that we get a cache hit if the tx was valid
152154
std::vector<CScriptCheck> scriptchecks;
153-
BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
155+
BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, &scriptchecks));
154156
BOOST_CHECK(scriptchecks.empty());
155157
} else {
156158
// Check that we get script executions to check, if the transaction
157159
// was invalid, or we didn't add to cache.
158160
std::vector<CScriptCheck> scriptchecks;
159-
BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
161+
BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, &scriptchecks));
160162
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
161163
}
162164
}
@@ -214,20 +216,20 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
214216
TxValidationState state;
215217
PrecomputedTransactionData ptd_spend_tx;
216218

217-
BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
219+
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));
218220

219221
// If we call again asking for scriptchecks (as happens in
220222
// ConnectBlock), we should add a script check object for this -- we're
221223
// not caching invalidity (if that changes, delete this test case).
222224
std::vector<CScriptCheck> scriptchecks;
223-
BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
225+
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));
224226
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
225227

226228
// Test that CheckInputScripts returns true iff DERSIG-enforcing flags are
227229
// not present. Don't add these checks to the cache, so that we can
228230
// test later that block validation works fine in the absence of cached
229231
// successes.
230-
ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip());
232+
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);
231233
}
232234

233235
// 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)
253255
std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());
254256
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
255257

256-
ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip());
258+
ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
257259
}
258260

259261
// Test CHECKLOCKTIMEVERIFY
@@ -276,13 +278,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
276278
vchSig.push_back((unsigned char)SIGHASH_ALL);
277279
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
278280

279-
ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
281+
ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
280282

281283
// Make it valid, and check again
282284
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
283285
TxValidationState state;
284286
PrecomputedTransactionData txdata;
285-
BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
287+
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));
286288
}
287289

288290
// TEST CHECKSEQUENCEVERIFY
@@ -304,13 +306,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
304306
vchSig.push_back((unsigned char)SIGHASH_ALL);
305307
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
306308

307-
ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
309+
ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
308310

309311
// Make it valid, and check again
310312
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
311313
TxValidationState state;
312314
PrecomputedTransactionData txdata;
313-
BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
315+
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));
314316
}
315317

316318
// TODO: add tests for remaining script flags
@@ -333,11 +335,11 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
333335
UpdateInput(valid_with_witness_tx.vin[0], sigdata);
334336

335337
// This should be valid under all script flags.
336-
ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
338+
ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
337339

338340
// Remove the witness, and check that it is now invalid.
339341
valid_with_witness_tx.vin[0].scriptWitness.SetNull();
340-
ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip());
342+
ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
341343
}
342344

343345
{
@@ -362,7 +364,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
362364
}
363365

364366
// This should be valid under all script flags
365-
ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
367+
ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
366368

367369
// Check that if the second input is invalid, but the first input is
368370
// valid, the transaction is not cached.
@@ -372,12 +374,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
372374
TxValidationState state;
373375
PrecomputedTransactionData txdata;
374376
// This transaction is now invalid under segwit, because of the second input.
375-
BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
377+
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));
376378

377379
std::vector<CScriptCheck> scriptchecks;
378380
// Make sure this transaction was not cached (ie because the first
379381
// input was valid)
380-
BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
382+
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));
381383
// Should get 2 script checks back -- caching is on a whole-transaction basis.
382384
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
383385
}

src/test/util/setup_common.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
191191
ValidationCacheSizes validation_cache_sizes{};
192192
ApplyArgsManOptions(*m_node.args, validation_cache_sizes);
193193
Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
194-
Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
195194

196195
m_node.chain = interfaces::MakeChain(m_node);
197196
static bool noui_connected = false;

0 commit comments

Comments
 (0)