validation: add -prevoutfetchthreads configuration option
What changed, and why it matters
This commit adds a new user-configurable setting called -prevoutfetchthreads that controls how many background worker threads Bitcoin Core uses to fetch previous transaction outputs (prevouts) from the UTXO database while connecting a new block. It is a performance and resource-tuning feature, not a security fix or vulnerability. There is no indication in the commit that it addresses any security issue.
No security action required. Treat as a normal feature/performance configuration change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change introduces a new ArgsManager option -prevoutfetchthreads=
Changed components
src/init.cppsrc/kernel/chainstatemanager_opts.hsrc/node/chainstatemanager_args.cppsrc/test/validation_chainstatemanager_tests.cppsrc/validation.hdoc/reduce-memory.mdInspect captured patch +21 / −0
diff --git a/doc/reduce-memory.md b/doc/reduce-memory.md
index 44d90bcf..348d98bd 100644
--- a/doc/reduce-memory.md
+++ b/doc/reduce-memory.md
@@ -49,6 +49,7 @@ threads take up 8MiB for the thread stack on a 64-bit system, and 4MiB in a
- `-par=<n>` - the number of script verification threads, defaults to the number of cores in the system minus one.
- `-rpcthreads=<n>` - the number of threads used for processing RPC requests, defaults to `16`.
+- `-prevoutfetchthreads=<n>` - the number of threads used to fetch block input prevouts, defaults to `8`.
## Linux specific
diff --git a/src/init.cpp b/src/init.cpp
index 0e72443c..f2346fde 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -538,6 +538,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
argsman.AddArg("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet3: %s, testnet4: %s, signet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnet4ChainParams->GetConsensus().nMinimumChainWork.GetHex(), signetChainParams->GetConsensus().nMinimumChainWork.GetHex()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-par=<n>", strprintf("Set the number of script verification threads (0 = auto, up to %d, <0 = leave that many cores free, default: %d)",
MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-prevoutfetchthreads=<n>", strprintf("Set the number of threads used to prefetch block input prevouts from the chainstate database (0 disables, up to %d, default: %d). Negative values are rejected.", MAX_PREVOUTFETCH_THREADS, DEFAULT_PREVOUTFETCH_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-persistmempoolv1",
strprintf("Whether a mempool.dat file created by -persistmempool or the savemempool RPC will be written in the legacy format "
diff --git a/src/kernel/chainstatemanager_opts.h b/src/kernel/chainstatemanager_opts.h
index 134b9319..554d032e 100644
--- a/src/kernel/chainstatemanager_opts.h
+++ b/src/kernel/chainstatemanager_opts.h
@@ -22,6 +22,7 @@ class CChainParams;
class ValidationSignals;
static constexpr auto DEFAULT_MAX_TIP_AGE{24h};
+static constexpr int32_t DEFAULT_PREVOUTFETCH_THREADS{8};
namespace kernel {
@@ -46,6 +47,8 @@ struct ChainstateManagerOpts {
ValidationSignals* signals{nullptr};
//! Number of script check worker threads. Zero means no parallel verification.
int worker_threads_num{0};
+ //! Number of worker threads used for prefetching block input prevouts. Zero means no parallel fetching.
+ int32_t prevoutfetch_threads_num{DEFAULT_PREVOUTFETCH_THREADS};
size_t script_execution_cache_bytes{DEFAULT_SCRIPT_EXECUTION_CACHE_BYTES};
size_t signature_cache_bytes{DEFAULT_SIGNATURE_CACHE_BYTES};
};
diff --git a/src/node/chainstatemanager_args.cpp b/src/node/chainstatemanager_args.cpp
index 46a12bf2..b4431490 100644
--- a/src/node/chainstatemanager_args.cpp
+++ b/src/node/chainstatemanager_args.cpp
@@ -60,6 +60,13 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& args, ChainstateManage
// Subtract 1 because the main thread counts towards the par threads.
opts.worker_threads_num = script_threads - 1;
+ if (auto value{args.GetArg<int32_t>("-prevoutfetchthreads")}) {
+ if (*value < 0) {
+ return util::Error{Untranslated(strprintf("-prevoutfetchthreads must be non-negative (got %d). Use 0 to disable parallel input fetching.", *value))};
+ }
+ opts.prevoutfetch_threads_num = std::min(*value, MAX_PREVOUTFETCH_THREADS);
+ }
+
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
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 9818b51e..eae3fcdc 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -991,6 +991,12 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_args, BasicTestingSetup)
BOOST_CHECK(!get_opts({"-minimumchainwork=xyz"})); // invalid hex characters
BOOST_CHECK(!get_opts({"-minimumchainwork=01234567890123456789012345678901234567890123456789012345678901234"})); // > 64 hex chars
+
+ BOOST_CHECK_EQUAL(get_valid_opts({}).prevoutfetch_threads_num, DEFAULT_PREVOUTFETCH_THREADS);
+ BOOST_CHECK_EQUAL(get_valid_opts({"-prevoutfetchthreads=0"}).prevoutfetch_threads_num, 0);
+ BOOST_CHECK_EQUAL(get_valid_opts({"-prevoutfetchthreads=3"}).prevoutfetch_threads_num, 3);
+ BOOST_CHECK_EQUAL(get_valid_opts({"-prevoutfetchthreads=100"}).prevoutfetch_threads_num, MAX_PREVOUTFETCH_THREADS);
+ BOOST_CHECK(!get_opts({"-prevoutfetchthreads=-1"}));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/validation.h b/src/validation.h
index 4cb5dc63..2472dd60 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -89,6 +89,9 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES{550_MiB};
/** Maximum number of dedicated script-checking threads allowed */
static constexpr int MAX_SCRIPTCHECK_THREADS{15};
+/** Maximum number of dedicated threads allowed for prefetching block input prevouts */
+static constexpr int32_t MAX_PREVOUTFETCH_THREADS{16};
+
/** Current sync state passed to tip changed callbacks. */
enum class SynchronizationState {
INIT_REINDEX,
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.