diff --git a/doc/release-notes-31483.md b/doc/release-notes-31483.md new file mode 100644 index 00000000000..9076aca3eff --- /dev/null +++ b/doc/release-notes-31483.md @@ -0,0 +1,6 @@ +Updated settings +------ + +- On 32-bit systems an error is returned on startup if the user passes a + `-dbcache` value exceeding 4GiB. + diff --git a/src/init.cpp b/src/init.cpp index a3edeb55241..1ae968760d3 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1605,7 +1605,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) ReadNotificationArgs(args, kernel_notifications); // cache size calculations - auto [index_cache_sizes, kernel_cache_sizes] = CalculateCacheSizes(args, g_enabled_filter_types.size()); + const auto cache_sizes = CalculateCacheSizes(args, g_enabled_filter_types.size()); + if (!cache_sizes) { + return InitError(_("Failed to calculate cache sizes. Try lowering the -dbcache value.")); + } + auto [index_cache_sizes, kernel_cache_sizes] = *cache_sizes; LogInfo("Cache configuration:"); LogInfo("* Using %.1f MiB for block index database", kernel_cache_sizes.block_tree_db * (1.0 / 1024 / 1024)); diff --git a/src/node/caches.cpp b/src/node/caches.cpp index 21e2654b4e4..9dbbccd3045 100644 --- a/src/node/caches.cpp +++ b/src/node/caches.cpp @@ -7,8 +7,10 @@ #include #include #include +#include #include +#include #include // Unlike for the UTXO database, for the txindex scenario the leveldb cache make @@ -19,19 +21,27 @@ static constexpr int64_t MAX_TX_INDEX_CACHE{1024}; static constexpr int64_t MAX_FILTER_INDEX_CACHE{1024}; namespace node { -CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes) +std::optional CalculateCacheSizes(const ArgsManager& args, size_t n_indexes) { - int64_t nTotalCache = (args.GetIntArg("-dbcache", DEFAULT_DB_CACHE) << 20); - nTotalCache = std::max(nTotalCache, MIN_DB_CACHE << 20); - IndexCacheSizes sizes; - sizes.tx_index = std::min(nTotalCache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? MAX_TX_INDEX_CACHE << 20 : 0); - nTotalCache -= sizes.tx_index; - sizes.filter_index = 0; - if (n_indexes > 0) { - int64_t max_cache = std::min(nTotalCache / 8, MAX_FILTER_INDEX_CACHE << 20); - sizes.filter_index = max_cache / n_indexes; - nTotalCache -= sizes.filter_index * n_indexes; + int64_t db_cache = args.GetIntArg("-dbcache", DEFAULT_DB_CACHE); + if (static_cast(db_cache << 20) > std::numeric_limits::max()) { + LogWarning("Cannot allocate more than %d MiB in total for db caches.", static_cast(std::numeric_limits::max()) * (1.0 / 1024 / 1024)); + return std::nullopt; } - return {sizes, kernel::CacheSizes{static_cast(nTotalCache)}}; + + // negative values are permitted, but interpreted as zero. + db_cache = std::max(int64_t{0}, db_cache); + size_t total_cache = std::max(MiBToBytes(db_cache), MiBToBytes(MIN_DB_CACHE)); + + IndexCacheSizes index_sizes; + index_sizes.tx_index = std::min(total_cache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? MiBToBytes(MAX_TX_INDEX_CACHE) : 0); + total_cache -= index_sizes.tx_index; + index_sizes.filter_index = 0; + if (n_indexes > 0) { + int64_t max_cache = std::min(total_cache / 8, MiBToBytes(MAX_FILTER_INDEX_CACHE)); + index_sizes.filter_index = max_cache / n_indexes; + total_cache -= index_sizes.filter_index * n_indexes; + } + return {{index_sizes, kernel::CacheSizes{total_cache}}}; } } // namespace node diff --git a/src/node/caches.h b/src/node/caches.h index 45a1623e735..fd6b8b4ab85 100644 --- a/src/node/caches.h +++ b/src/node/caches.h @@ -9,6 +9,7 @@ #include #include +#include class ArgsManager; @@ -19,14 +20,14 @@ static constexpr int64_t DEFAULT_DB_CACHE{DEFAULT_KERNEL_CACHE}; namespace node { struct IndexCacheSizes { - int64_t tx_index; - int64_t filter_index; + size_t tx_index; + size_t filter_index; }; struct CacheSizes { IndexCacheSizes index; kernel::CacheSizes kernel; }; -CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes = 0); +std::optional CalculateCacheSizes(const ArgsManager& args, size_t n_indexes = 0); } // namespace node #endif // BITCOIN_NODE_CACHES_H diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 660d2c3289c..3ee557ae622 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -46,7 +46,7 @@ static ChainstateLoadResult CompleteChainstateInitialization( try { pblocktree = std::make_unique(DBParams{ .path = chainman.m_options.datadir / "blocks" / "index", - .cache_bytes = static_cast(cache_sizes.block_tree_db), + .cache_bytes = cache_sizes.block_tree_db, .memory_only = options.block_tree_db_in_memory, .wipe_data = options.wipe_block_tree_db, .options = chainman.m_options.block_tree_db}); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 5ede59b82a1..1c7bfa85d51 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -252,7 +252,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, TestOpts opts) LOCK(m_node.chainman->GetMutex()); m_node.chainman->m_blockman.m_block_tree_db = std::make_unique(DBParams{ .path = m_args.GetDataDirNet() / "blocks" / "index", - .cache_bytes = static_cast(m_kernel_cache_sizes.block_tree_db), + .cache_bytes = m_kernel_cache_sizes.block_tree_db, .memory_only = true, }); }; diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index 33ad2584573..7d7c7b7cd43 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -104,7 +104,7 @@ struct BasicTestingSetup { * initialization behaviour. */ struct ChainTestingSetup : public BasicTestingSetup { - kernel::CacheSizes m_kernel_cache_sizes{node::CalculateCacheSizes(m_args).kernel}; + kernel::CacheSizes m_kernel_cache_sizes{node::CalculateCacheSizes(m_args).value().kernel}; bool m_coins_db_in_memory{true}; bool m_block_tree_db_in_memory{true}; std::function m_make_chainman{}; diff --git a/src/validation.h b/src/validation.h index aea7a4621bb..f75337604ad 100644 --- a/src/validation.h +++ b/src/validation.h @@ -1067,11 +1067,11 @@ public: //! The total number of bytes available for us to use across all in-memory //! coins caches. This will be split somehow across chainstates. - int64_t m_total_coinstip_cache{0}; + size_t m_total_coinstip_cache{0}; // //! The total number of bytes available for us to use across all leveldb //! coins databases. This will be split somehow across chainstates. - int64_t m_total_coinsdb_cache{0}; + size_t m_total_coinsdb_cache{0}; //! Instantiate a new chainstate. //!