diff --git a/ci/dash/lint-tidy.sh b/ci/dash/lint-tidy.sh index 9899718039e1..b186650db63e 100755 --- a/ci/dash/lint-tidy.sh +++ b/ci/dash/lint-tidy.sh @@ -24,10 +24,12 @@ iwyu_tool.py \ "src/dbwrapper.cpp" \ "src/init" \ "src/node/chainstate.cpp" \ + "src/node/minisketchwrapper.cpp" \ "src/policy/feerate.cpp" \ "src/policy/packages.cpp" \ "src/policy/settings.cpp" \ "src/primitives/transaction.cpp" \ + "src/random.cpp" \ "src/rpc/fees.cpp" \ "src/rpc/signmessage.cpp" \ "src/test/fuzz/txorphan.cpp" \ diff --git a/configure.ac b/configure.ac index dec5c94755c3..10898c110c8e 100644 --- a/configure.ac +++ b/configure.ac @@ -1503,6 +1503,12 @@ if test "$use_usdt" != "no"; then fi AM_CONDITIONAL([ENABLE_USDT_TRACEPOINTS], [test "$use_usdt" = "yes"]) +if test "$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests" = "nononono"; then + use_upnp=no + use_natpmp=no + use_zmq=no +fi + dnl Check for libminiupnpc (optional) if test "$use_upnp" != "no"; then TEMP_CPPFLAGS="$CPPFLAGS" @@ -1512,14 +1518,15 @@ if test "$use_upnp" != "no"; then [AC_CHECK_LIB([miniupnpc], [upnpDiscover], [MINIUPNPC_LIBS="$MINIUPNPC_LIBS -lminiupnpc"], [have_miniupnpc=no], [$MINIUPNPC_LIBS])], [have_miniupnpc=no] ) - dnl The minimum supported miniUPnPc API version is set to 10. This keeps compatibility - dnl with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages. + + dnl The minimum supported miniUPnPc API version is set to 17. This excludes + dnl versions with known vulnerabilities. if test "$have_miniupnpc" != "no"; then AC_MSG_CHECKING([whether miniUPnPc API version is supported]) AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ - #if MINIUPNPC_API_VERSION >= 10 + #if MINIUPNPC_API_VERSION >= 17 // Everything is okay #else # error miniUPnPc API version is too old @@ -1528,7 +1535,7 @@ if test "$use_upnp" != "no"; then AC_MSG_RESULT([yes]) ],[ AC_MSG_RESULT([no]) - AC_MSG_WARN([miniUPnPc API version < 10 is unsupported, disabling UPnP support.]) + AC_MSG_WARN([miniUPnPc API version < 17 is unsupported, disabling UPnP support.]) have_miniupnpc=no ]) fi diff --git a/doc/dependencies.md b/doc/dependencies.md index e16bf1705095..aae6cd2b00fc 100644 --- a/doc/dependencies.md +++ b/doc/dependencies.md @@ -39,7 +39,7 @@ You can find installation instructions in the `build-*.md` file for your platfor | Dependency | Releases | Version used | Minimum required | Runtime | | --- | --- | --- | --- | --- | | [libnatpmp](../depends/packages/libnatpmp.mk) | [link](https://github.com/miniupnp/libnatpmp/) | commit [07004b9...](https://github.com/miniupnp/libnatpmp/tree/07004b97cf691774efebe70404cf22201e4d330d) | | No | -| [MiniUPnPc](../depends/packages/miniupnpc.mk) | [link](https://miniupnp.tuxfamily.org/) | [2.2.2](https://github.com/bitcoin/bitcoin/pull/20421) | 1.9 | No | +| [MiniUPnPc](../depends/packages/miniupnpc.mk) | [link](https://miniupnp.tuxfamily.org/) | [2.2.2](https://github.com/bitcoin/bitcoin/pull/20421) | 2.1 | No | ### Notifications | Dependency | Releases | Version used | Minimum required | Runtime | diff --git a/doc/release-notes-6901.md b/doc/release-notes-6901.md new file mode 100644 index 000000000000..6efdf775b694 --- /dev/null +++ b/doc/release-notes-6901.md @@ -0,0 +1,8 @@ +Notable changes +=============== + +New settings +------------ + +- The `shutdownnotify` option is used to specify a command to execute synchronously +before Dash Core has begun its shutdown sequence. diff --git a/src/Makefile.am b/src/Makefile.am index 7b5c4ca68402..313bc926866b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -259,6 +259,7 @@ BITCOIN_CORE_H = \ instantsend/instantsend.h \ instantsend/lock.h \ instantsend/signing.h \ + kernel/coinstats.h \ key.h \ key_io.h \ limitedmap.h \ @@ -306,7 +307,6 @@ BITCOIN_CORE_H = \ node/caches.h \ node/chainstate.h \ node/coin.h \ - node/coinstats.h \ node/connection_types.h \ node/context.h \ node/eviction.h \ @@ -530,6 +530,7 @@ libbitcoin_node_a_SOURCES = \ instantsend/instantsend.cpp \ instantsend/lock.cpp \ instantsend/signing.cpp \ + kernel/coinstats.cpp \ llmq/blockprocessor.cpp \ llmq/commitment.cpp \ llmq/context.cpp \ @@ -561,7 +562,6 @@ libbitcoin_node_a_SOURCES = \ node/caches.cpp \ node/chainstate.cpp \ node/coin.cpp \ - node/coinstats.cpp \ node/connection_types.cpp \ node/context.cpp \ node/eviction.cpp \ @@ -1087,7 +1087,21 @@ dash_wallet_SOURCES = bitcoin-wallet.cpp dash_wallet_CPPFLAGS = $(bitcoin_bin_cppflags) dash_wallet_CXXFLAGS = $(bitcoin_bin_cxxflags) dash_wallet_LDFLAGS = $(bitcoin_bin_ldflags) -dash_wallet_LDADD = $(LIBBITCOIN_WALLET_TOOL) $(bitcoin_bin_ldadd) +dash_wallet_LDADD = \ + $(LIBBITCOIN_WALLET_TOOL) \ + $(LIBBITCOIN_WALLET) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UTIL) \ + $(LIBUNIVALUE) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBDASHBLS) \ + $(LIBSECP256K1) \ + $(BACKTRACE_LIB) \ + $(BOOST_LIBS) \ + $(BDB_LIBS) \ + $(SQLITE_LIBS) \ + $(GMP_LIBS) if TARGET_WINDOWS dash_wallet_SOURCES += dash-wallet-res.rc diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp index 6bf43e28f7db..49c81ba1efbc 100644 --- a/src/index/coinstatsindex.cpp +++ b/src/index/coinstatsindex.cpp @@ -14,10 +14,11 @@ #include #include -using node::CCoinsStats; -using node::GetBogoSize; +using kernel::CCoinsStats; +using kernel::GetBogoSize; +using kernel::TxOutSer; + using node::ReadBlockFromDisk; -using node::TxOutSer; using node::UndoReadFromDisk; static constexpr uint8_t DB_BLOCK_HASH{'s'}; @@ -316,28 +317,31 @@ static bool LookUpOne(const CDBWrapper& db, const CBlockIndex* block_index, DBVa return db.Read(DBHashKey(block_index->GetBlockHash()), result); } -bool CoinStatsIndex::LookUpStats(const CBlockIndex* block_index, CCoinsStats& coins_stats) const +std::optional CoinStatsIndex::LookUpStats(const CBlockIndex* block_index) const { + CCoinsStats stats{Assert(block_index)->nHeight, block_index->GetBlockHash()}; + stats.index_used = true; + DBVal entry; if (!LookUpOne(*m_db, block_index, entry)) { - return false; + return std::nullopt; } - coins_stats.hashSerialized = entry.muhash; - coins_stats.nTransactionOutputs = entry.transaction_output_count; - coins_stats.nBogoSize = entry.bogo_size; - coins_stats.total_amount = entry.total_amount; - coins_stats.total_subsidy = entry.total_subsidy; - coins_stats.total_unspendable_amount = entry.total_unspendable_amount; - coins_stats.total_prevout_spent_amount = entry.total_prevout_spent_amount; - coins_stats.total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount; - coins_stats.total_coinbase_amount = entry.total_coinbase_amount; - coins_stats.total_unspendables_genesis_block = entry.total_unspendables_genesis_block; - coins_stats.total_unspendables_bip30 = entry.total_unspendables_bip30; - coins_stats.total_unspendables_scripts = entry.total_unspendables_scripts; - coins_stats.total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards; - - return true; + stats.hashSerialized = entry.muhash; + stats.nTransactionOutputs = entry.transaction_output_count; + stats.nBogoSize = entry.bogo_size; + stats.total_amount = entry.total_amount; + stats.total_subsidy = entry.total_subsidy; + stats.total_unspendable_amount = entry.total_unspendable_amount; + stats.total_prevout_spent_amount = entry.total_prevout_spent_amount; + stats.total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount; + stats.total_coinbase_amount = entry.total_coinbase_amount; + stats.total_unspendables_genesis_block = entry.total_unspendables_genesis_block; + stats.total_unspendables_bip30 = entry.total_unspendables_bip30; + stats.total_unspendables_scripts = entry.total_unspendables_scripts; + stats.total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards; + + return stats; } bool CoinStatsIndex::Init() diff --git a/src/index/coinstatsindex.h b/src/index/coinstatsindex.h index 0eb42b1ca35d..40b19ca9315c 100644 --- a/src/index/coinstatsindex.h +++ b/src/index/coinstatsindex.h @@ -9,7 +9,7 @@ #include #include #include -#include +#include /** * CoinStatsIndex maintains statistics on the UTXO set. @@ -56,7 +56,7 @@ class CoinStatsIndex final : public BaseIndex explicit CoinStatsIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false); // Look up stats for a specific block using CBlockIndex - bool LookUpStats(const CBlockIndex* block_index, node::CCoinsStats& coins_stats) const; + std::optional LookUpStats(const CBlockIndex* block_index) const; }; /// The global UTXO set hash object. diff --git a/src/init.cpp b/src/init.cpp index 18d1cafe92c8..e856e9bdeeca 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -32,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -134,10 +134,10 @@ #include #endif +using kernel::CoinStatsHashType; + using node::CacheSizes; using node::CalculateCacheSizes; -using node::CCoinsStats; -using node::CoinStatsHashType; using node::ChainstateLoadingError; using node::ChainstateLoadVerifyError; using node::DashChainstateSetupClose; @@ -224,8 +224,24 @@ static fs::path GetPidFile(const ArgsManager& args) // shutdown thing. // +#if HAVE_SYSTEM +static void ShutdownNotify(const ArgsManager& args) +{ + std::vector threads; + for (const auto& cmd : args.GetArgs("-shutdownnotify")) { + threads.emplace_back(runCommand, cmd); + } + for (auto& t : threads) { + t.join(); + } +} +#endif + void Interrupt(NodeContext& node) { +#if HAVE_SYSTEM + ShutdownNotify(*node.args); +#endif InterruptHTTPServer(); InterruptHTTPRPC(); InterruptRPC(); @@ -439,7 +455,6 @@ void Shutdown(NodeContext& node) LogPrintf("%s: Unable to remove PID file: %s\n", __func__, fsbridge::get_filesystem_error_message(e)); } - node.args = nullptr; LogPrintf("%s: done\n", __func__); } @@ -553,6 +568,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-syncmempool", strprintf("Sync mempool from other nodes on start (default: %u)", DEFAULT_SYNC_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); #if HAVE_SYSTEM argsman.AddArg("-startupnotify=", "Execute command on startup.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-shutdownnotify=", "Execute command immediately before beginning shutdown. The need for shutdown may be urgent, so be careful not to delay it long (if the command doesn't require interaction with the server, consider having it fork into the background).", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); #endif #ifndef WIN32 argsman.AddArg("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -840,15 +856,15 @@ static void PeriodicStats(NodeContext& node) ChainstateManager& chainman = *Assert(node.chainman); const CTxMemPool& mempool = *Assert(node.mempool); const llmq::CInstantSendManager& isman = *Assert(node.llmq_ctx->isman); - CCoinsStats stats{CoinStatsHashType::NONE}; chainman.ActiveChainstate().ForceFlushStateToDisk(); - if (WITH_LOCK(cs_main, return GetUTXOStats(&chainman.ActiveChainstate().CoinsDB(), chainman.m_blockman, stats, node.rpc_interruption_point, chainman.ActiveChain().Tip()))) { - ::g_stats_client->gauge("utxoset.tx", stats.nTransactions, 1.0f); - ::g_stats_client->gauge("utxoset.txOutputs", stats.nTransactionOutputs, 1.0f); - ::g_stats_client->gauge("utxoset.dbSizeBytes", stats.nDiskSize, 1.0f); - ::g_stats_client->gauge("utxoset.blockHeight", stats.nHeight, 1.0f); - if (stats.total_amount.has_value()) { - ::g_stats_client->gauge("utxoset.totalAmount", (double)stats.total_amount.value() / (double)COIN, 1.0f); + const auto maybe_stats = WITH_LOCK(::cs_main, return GetUTXOStats(&chainman.ActiveChainstate().CoinsDB(), chainman.m_blockman, /*hash_type=*/CoinStatsHashType::NONE, node.rpc_interruption_point, chainman.ActiveChain().Tip(), /*index_requested=*/true)); + if (maybe_stats.has_value()) { + ::g_stats_client->gauge("utxoset.tx", maybe_stats->nTransactions, 1.0f); + ::g_stats_client->gauge("utxoset.txOutputs", maybe_stats->nTransactionOutputs, 1.0f); + ::g_stats_client->gauge("utxoset.dbSizeBytes", maybe_stats->nDiskSize, 1.0f); + ::g_stats_client->gauge("utxoset.blockHeight", maybe_stats->nHeight, 1.0f); + if (maybe_stats->total_amount.has_value()) { + ::g_stats_client->gauge("utxoset.totalAmount", (double)maybe_stats->total_amount.value() / (double)COIN, 1.0f); } } else { // something went wrong @@ -2304,6 +2320,24 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) return false; } + int chain_active_height = WITH_LOCK(cs_main, return chainman.ActiveChain().Height()); + + // On first startup, warn on low block storage space + if (!fReindex && !fReindexChainState && chain_active_height <= 1) { + uint64_t additional_bytes_needed = fPruneMode ? nPruneTarget + : chainparams.AssumedBlockchainSize() * 1024 * 1024 * 1024; + + if (!CheckDiskSpace(args.GetBlocksDirPath(), additional_bytes_needed)) { + InitWarning(strprintf(_( + "Disk space for %s may not accommodate the block files. " \ + "Approximately %u GB of data will be stored in this directory." + ), + fs::quoted(fs::PathToString(args.GetBlocksDirPath())), + chainparams.AssumedBlockchainSize() + )); + } + } + // Either install a handler to notify us when genesis activates, or set fHaveGenesis directly. // No locking, as this happens before any background thread is started. boost::signals2::connection block_notify_genesis_wait_connection; @@ -2394,8 +2428,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ********************************************************* Step 12: start node - int chain_active_height; - //// debug print { LOCK(cs_main); diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 5a7414b84fa0..7646fc7f1ea1 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -5,13 +5,14 @@ #ifndef BITCOIN_INTERFACES_NODE_H #define BITCOIN_INTERFACES_NODE_H -#include // For CAmount -#include // For NodeId -#include // For banmap_t -#include // For Network -#include // For ConnectionDirection +#include // For CAmount +#include // For NodeId +#include // For banmap_t +#include // For Network +#include // For ConnectionDirection #include // For SecureString #include +#include // For util::SettingsValue #include #include @@ -193,6 +194,24 @@ class Node //! Return whether shutdown was requested. virtual bool shutdownRequested() = 0; + //! Return whether a particular setting in /settings.json is or + //! would be ignored because it is also specified in the command line. + virtual bool isSettingIgnored(const std::string& name) = 0; + + //! Return setting value from /settings.json or dash.conf. + virtual util::SettingsValue getPersistentSetting(const std::string& name) = 0; + + //! Update a setting in /settings.json. + virtual void updateRwSetting(const std::string& name, const util::SettingsValue& value) = 0; + + //! Force a setting value to be applied, overriding any other configuration + //! source, but not being persisted. + virtual void forceSetting(const std::string& name, const util::SettingsValue& value) = 0; + + //! Clear all settings in /settings.json and store a backup of + //! previous settings in /settings.json.bak. + virtual void resetSettings() = 0; + //! Map port. virtual void mapPort(bool use_upnp, bool use_natpmp) = 0; diff --git a/src/node/coinstats.cpp b/src/kernel/coinstats.cpp similarity index 74% rename from src/node/coinstats.cpp rename to src/kernel/coinstats.cpp index d824bf563de6..217ac9e810ec 100644 --- a/src/node/coinstats.cpp +++ b/src/kernel/coinstats.cpp @@ -1,14 +1,12 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2021 The Bitcoin Core developers +// Copyright (c) 2022 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include +#include #include #include #include -#include #include #include #include @@ -18,7 +16,12 @@ #include -namespace node { +namespace kernel { + +CCoinsStats::CCoinsStats(int block_height, const uint256& block_hash) + : nHeight(block_height), + hashBlock(block_hash) {} + // Database-independent metric indicating the UTXO set size uint64_t GetBogoSize(const CScript& script_pub_key) { @@ -94,24 +97,11 @@ static void ApplyStats(CCoinsStats& stats, const uint256& hash, const std::map -static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, T hash_obj, const std::function& interruption_point, const CBlockIndex* pindex) +static bool ComputeUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const std::function& interruption_point) { std::unique_ptr pcursor(view->Cursor()); assert(pcursor); - if (!pindex) { - LOCK(cs_main); - pindex = blockman.LookupBlockIndex(view->GetBestBlock()); - } - stats.nHeight = Assert(pindex)->nHeight; - stats.hashBlock = pindex->GetBlockHash(); - - // Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested - if ((stats.m_hash_type == CoinStatsHashType::MUHASH || stats.m_hash_type == CoinStatsHashType::NONE) && g_coin_stats_index && stats.index_requested) { - stats.index_used = true; - return g_coin_stats_index->LookUpStats(pindex, stats); - } - PrepareHash(hash_obj, stats); uint256 prevkey; @@ -142,25 +132,36 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& FinalizeHash(hash_obj, stats); stats.nDiskSize = view->EstimateSize(); + return true; } -bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats& stats, const std::function& interruption_point, const CBlockIndex* pindex) +std::optional ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, node::BlockManager& blockman, const std::function& interruption_point) { - switch (stats.m_hash_type) { - case(CoinStatsHashType::HASH_SERIALIZED): { - HashWriter ss{}; - return GetUTXOStats(view, blockman, stats, ss, interruption_point, pindex); - } - case(CoinStatsHashType::MUHASH): { - MuHash3072 muhash; - return GetUTXOStats(view, blockman, stats, muhash, interruption_point, pindex); - } - case(CoinStatsHashType::NONE): { - return GetUTXOStats(view, blockman, stats, nullptr, interruption_point, pindex); + CBlockIndex* pindex = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock())); + CCoinsStats stats{Assert(pindex)->nHeight, pindex->GetBlockHash()}; + + bool success = [&]() -> bool { + switch (hash_type) { + case(CoinStatsHashType::HASH_SERIALIZED): { + HashWriter ss{}; + return ComputeUTXOStats(view, stats, ss, interruption_point); + } + case(CoinStatsHashType::MUHASH): { + MuHash3072 muhash; + return ComputeUTXOStats(view, stats, muhash, interruption_point); + } + case(CoinStatsHashType::NONE): { + return ComputeUTXOStats(view, stats, nullptr, interruption_point); + } + } // no default case, so the compiler can warn about missing cases + assert(false); + }(); + + if (!success) { + return std::nullopt; } - } // no default case, so the compiler can warn about missing cases - assert(false); + return stats; } // The legacy hash serializes the hashBlock @@ -183,4 +184,5 @@ static void FinalizeHash(MuHash3072& muhash, CCoinsStats& stats) stats.hashSerialized = out; } static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {} -} // namespace node + +} // namespace kernel diff --git a/src/node/coinstats.h b/src/kernel/coinstats.h similarity index 75% rename from src/node/coinstats.h rename to src/kernel/coinstats.h index 4ca9274db35f..c103966568f2 100644 --- a/src/node/coinstats.h +++ b/src/kernel/coinstats.h @@ -1,10 +1,9 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2021 The Bitcoin Core developers +// Copyright (c) 2022 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_NODE_COINSTATS_H -#define BITCOIN_NODE_COINSTATS_H +#ifndef BITCOIN_KERNEL_COINSTATS_H +#define BITCOIN_KERNEL_COINSTATS_H #include #include @@ -21,17 +20,14 @@ namespace node { class BlockManager; } // namespace node -namespace node { -enum class CoinStatsHashType { +namespace kernel { +enum class CoinStatsHashType : uint8_t { HASH_SERIALIZED, MUHASH, NONE, }; struct CCoinsStats { - //! Which hash type to use - const CoinStatsHashType m_hash_type; - int nHeight{0}; uint256 hashBlock{}; uint64_t nTransactions{0}; @@ -45,8 +41,6 @@ struct CCoinsStats { //! The number of coins contained. uint64_t coins_count{0}; - //! Signals if the coinstatsindex should be used (when available). - bool index_requested{true}; //! Signals if the coinstatsindex was used to retrieve the statistics. bool index_used{false}; @@ -71,15 +65,15 @@ struct CCoinsStats { //! Total cumulative amount of coins lost due to unclaimed miner rewards up to and including this block CAmount total_unspendables_unclaimed_rewards{0}; - CCoinsStats(CoinStatsHashType hash_type) : m_hash_type(hash_type) {} + CCoinsStats() = default; + CCoinsStats(int block_height, const uint256& block_hash); }; -//! Calculate statistics about the unspent transaction output set -bool GetUTXOStats(CCoinsView* view, node::BlockManager& blockman, CCoinsStats& stats, const std::function& interruption_point = {}, const CBlockIndex* pindex = nullptr); - uint64_t GetBogoSize(const CScript& script_pub_key); CDataStream TxOutSer(const COutPoint& outpoint, const Coin& coin); -} // namespace node -#endif // BITCOIN_NODE_COINSTATS_H +std::optional ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, node::BlockManager& blockman, const std::function& interruption_point = {}); +} // namespace kernel + +#endif // BITCOIN_KERNEL_COINSTATS_H diff --git a/src/logging.cpp b/src/logging.cpp index 1b8dc8a91ae4..293ac4463d34 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -395,7 +395,7 @@ std::string BCLog::Logger::LogTimestampStr(const std::string& str) if (m_started_new_line) { int64_t nTimeMicros = GetTimeMicros(); strStamped = FormatISO8601DateTime(nTimeMicros/1000000); - if (m_log_time_micros) { + if (m_log_time_micros && !strStamped.empty()) { strStamped.pop_back(); strStamped += strprintf(".%06dZ", nTimeMicros%1000000); } diff --git a/src/mapport.cpp b/src/mapport.cpp index 85d65e39c5c5..d45ff9b3bd4b 100644 --- a/src/mapport.cpp +++ b/src/mapport.cpp @@ -26,9 +26,9 @@ #include #include #include -// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility -// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages. -static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed"); +// The minimum supported miniUPnPc API version is set to 17. This excludes +// versions with known vulnerabilities. +static_assert(MINIUPNPC_API_VERSION >= 17, "miniUPnPc API version >= 17 assumed"); #endif // USE_UPNP #include @@ -158,11 +158,7 @@ static bool ProcessUpnp() char lanaddr[64]; int error = 0; -#if MINIUPNPC_API_VERSION < 14 - devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error); -#else devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error); -#endif struct UPNPUrls urls; struct IGDdatas data; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 11578a3c7242..e9ce4a525ece 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2760,9 +2760,9 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic std::vector parent_ids_to_add; { LOCK(m_mempool.cs); - auto txiter = m_mempool.GetIter(tx->GetHash()); - if (txiter) { - const CTxMemPoolEntry::Parents& parents = (*txiter)->GetMemPoolParentsConst(); + auto tx_iter = m_mempool.GetIter(tx->GetHash()); + if (tx_iter) { + const CTxMemPoolEntry::Parents& parents = (*tx_iter)->GetMemPoolParentsConst(); parent_ids_to_add.reserve(parents.size()); for (const CTxMemPoolEntry& parent : parents) { if (parent.GetTime() > now - UNCONDITIONAL_RELAY_DELAY) { diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index f090523b238d..8d8579b62e2f 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -444,6 +444,46 @@ class NodeImpl : public Node } } bool shutdownRequested() override { return ShutdownRequested(); } + bool isSettingIgnored(const std::string& name) override + { + bool ignored = false; + gArgs.LockSettings([&](util::Settings& settings) { + if (auto* options = util::FindKey(settings.command_line_options, name)) { + ignored = !options->empty(); + } + }); + return ignored; + } + util::SettingsValue getPersistentSetting(const std::string& name) override { return gArgs.GetPersistentSetting(name); } + void updateRwSetting(const std::string& name, const util::SettingsValue& value) override + { + gArgs.LockSettings([&](util::Settings& settings) { + if (value.isNull()) { + settings.rw_settings.erase(name); + } else { + settings.rw_settings[name] = value; + } + }); + gArgs.WriteSettingsFile(); + } + void forceSetting(const std::string& name, const util::SettingsValue& value) override + { + gArgs.LockSettings([&](util::Settings& settings) { + if (value.isNull()) { + settings.forced_settings.erase(name); + } else { + settings.forced_settings[name] = value; + } + }); + } + void resetSettings() override + { + gArgs.WriteSettingsFile(/*errors=*/nullptr, /*backup=*/true); + gArgs.LockSettings([&](util::Settings& settings) { + settings.rw_settings.clear(); + }); + gArgs.WriteSettingsFile(); + } void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); } bool getProxy(Network net, Proxy& proxy_info) override { return GetProxy(net, proxy_info); } size_t getNodeCount(ConnectionDirection flags) override diff --git a/src/node/minisketchwrapper.cpp b/src/node/minisketchwrapper.cpp index 67e823cb6827..96707f7a0a95 100644 --- a/src/node/minisketchwrapper.cpp +++ b/src/node/minisketchwrapper.cpp @@ -23,17 +23,17 @@ static constexpr uint32_t BITS = 32; uint32_t FindBestImplementation() { - std::optional> best; + std::optional> best; uint32_t max_impl = Minisketch::MaxImplementation(); for (uint32_t impl = 0; impl <= max_impl; ++impl) { - std::vector benches; + std::vector benches; uint64_t offset = 0; /* Run a little benchmark with capacity 32, adding 184 entries, and decoding 11 of them once. */ for (int b = 0; b < 11; ++b) { if (!Minisketch::ImplementationSupported(BITS, impl)) break; Minisketch sketch(BITS, impl, 32); - auto start = GetTimeMicros(); + auto start = SteadyClock::now(); for (uint64_t e = 0; e < 100; ++e) { sketch.Add(e*1337 + b*13337 + offset); } @@ -41,7 +41,7 @@ uint32_t FindBestImplementation() sketch.Add(e*1337 + b*13337 + offset); } offset += (*sketch.Decode(32))[0]; - auto stop = GetTimeMicros(); + auto stop = SteadyClock::now(); benches.push_back(stop - start); } /* Remember which implementation has the best median benchmark time. */ diff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp index bde00572071b..c05c02f423e7 100644 --- a/src/policy/feerate.cpp +++ b/src/policy/feerate.cpp @@ -7,6 +7,8 @@ #include #include +#include + CFeeRate::CFeeRate(const CAmount& nFeePaid, uint32_t num_bytes) { const int64_t nSize{num_bytes}; @@ -22,7 +24,9 @@ CAmount CFeeRate::GetFee(uint32_t num_bytes) const { const int64_t nSize{num_bytes}; - CAmount nFee = nSatoshisPerK * nSize / 1000; + // Be explicit that we're converting from a double to int64_t (CAmount) here. + // We've previously had issues with the silent double->int64_t conversion. + CAmount nFee{static_cast(std::ceil(nSatoshisPerK * nSize / 1000.0))}; if (nFee == 0 && nSize != 0) { if (nSatoshisPerK > 0) nFee = CAmount(1); diff --git a/src/policy/feerate.h b/src/policy/feerate.h index 0fa18a1a2e6b..7b7e6c5d38dd 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -54,6 +54,7 @@ class CFeeRate /** * Return the fee in satoshis for the given size in vbytes. + * If the calculated fee would have fractional satoshis, then the returned fee will always be rounded up to the nearest satoshi. */ CAmount GetFee(uint32_t num_bytes) const; diff --git a/src/random.cpp b/src/random.cpp index a7626224603b..8f565000d9d6 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -295,14 +295,14 @@ static void SeedHardwareSlow(CSHA512& hasher) noexcept { } /** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */ -static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA512& hasher) noexcept +static void Strengthen(const unsigned char (&seed)[32], SteadyClock::duration dur, CSHA512& hasher) noexcept { CSHA512 inner_hasher; inner_hasher.Write(seed, sizeof(seed)); // Hash loop unsigned char buffer[64]; - int64_t stop = GetTimeMicros() + microseconds; + const auto stop{SteadyClock::now() + dur}; do { for (int i = 0; i < 1000; ++i) { inner_hasher.Finalize(buffer); @@ -312,7 +312,7 @@ static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA51 // Benchmark operation and feed it into outer hasher. int64_t perf = GetPerformanceCounter(); hasher.Write((const unsigned char*)&perf, sizeof(perf)); - } while (GetTimeMicros() < stop); + } while (SteadyClock::now() < stop); // Produce output from inner state and feed it to outer hasher. inner_hasher.Finalize(buffer); @@ -566,13 +566,13 @@ static void SeedSlow(CSHA512& hasher, RNGState& rng) noexcept } /** Extract entropy from rng, strengthen it, and feed it into hasher. */ -static void SeedStrengthen(CSHA512& hasher, RNGState& rng, int microseconds) noexcept +static void SeedStrengthen(CSHA512& hasher, RNGState& rng, SteadyClock::duration dur) noexcept { // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher. unsigned char strengthen_seed[32]; rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false); // Strengthen the seed, and feed it into hasher. - Strengthen(strengthen_seed, microseconds, hasher); + Strengthen(strengthen_seed, dur, hasher); } static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept @@ -592,7 +592,7 @@ static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept LogPrint(BCLog::RANDOM, "Feeding %i bytes of dynamic environment data into RNG\n", hasher.Size() - old_size); // Strengthen for 10 ms - SeedStrengthen(hasher, rng, 10000); + SeedStrengthen(hasher, rng, 10ms); } static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept @@ -612,7 +612,7 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept LogPrint(BCLog::RANDOM, "Feeding %i bytes of environment data into RNG\n", hasher.Size() - old_size); // Strengthen for 100 ms - SeedStrengthen(hasher, rng, 100000); + SeedStrengthen(hasher, rng, 100ms); } enum class RNGLevel { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 9d27a2b8d0f5..6432ba835f51 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -20,9 +20,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -63,9 +63,10 @@ #include #include +using kernel::CCoinsStats; +using kernel::CoinStatsHashType; + using node::BlockManager; -using node::CCoinsStats; -using node::CoinStatsHashType; using node::NodeContext; using node::ReadBlockFromDisk; using node::SnapshotMetadata; @@ -1103,6 +1104,31 @@ CoinStatsHashType ParseHashType(const std::string& hash_type_input) } } +std::optional GetUTXOStats(CCoinsView* view, node::BlockManager& blockman, + kernel::CoinStatsHashType hash_type, + const std::function& interruption_point, + const CBlockIndex* pindex, + bool index_requested) +{ + // Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested + if ((hash_type == kernel::CoinStatsHashType::MUHASH || hash_type == kernel::CoinStatsHashType::NONE) && g_coin_stats_index && index_requested) { + if (pindex) { + return g_coin_stats_index->LookUpStats(pindex); + } else { + CBlockIndex* block_index = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock())); + return g_coin_stats_index->LookUpStats(block_index); + } + } + + // If the coinstats index isn't requested or is otherwise not usable, the + // pindex should either be null or equal to the view's best block. This is + // because without the coinstats index we can only get coinstats about the + // best block. + CHECK_NONFATAL(!pindex || pindex->GetBlockHash() == view->GetBestBlock()); + + return kernel::ComputeUTXOStats(hash_type, view, blockman, interruption_point); +} + static RPCHelpMan gettxoutsetinfo() { return RPCHelpMan{"gettxoutsetinfo", @@ -1158,8 +1184,7 @@ static RPCHelpMan gettxoutsetinfo() const CBlockIndex* pindex{nullptr}; const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())}; - CCoinsStats stats{hash_type}; - stats.index_requested = request.params[2].isNull() || request.params[2].get_bool(); + bool index_requested = request.params[2].isNull() || request.params[2].get_bool(); const NodeContext& node = EnsureAnyNodeContext(request.context); ChainstateManager& chainman = EnsureChainman(node); @@ -1180,17 +1205,17 @@ static RPCHelpMan gettxoutsetinfo() throw JSONRPCError(RPC_INVALID_PARAMETER, "Querying specific block heights requires coinstatsindex"); } - if (stats.m_hash_type == CoinStatsHashType::HASH_SERIALIZED) { + if (hash_type == CoinStatsHashType::HASH_SERIALIZED) { throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_2 hash type cannot be queried for a specific block"); } - if (!stats.index_requested) { + if (!index_requested) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot set use_index to false when querying for a specific block"); } pindex = ParseHashOrHeight(request.params[1], chainman); } - if (stats.index_requested && g_coin_stats_index) { + if (index_requested && g_coin_stats_index) { if (!g_coin_stats_index->BlockUntilSyncedToCurrentChain()) { const IndexSummary summary{g_coin_stats_index->GetSummary()}; @@ -1202,7 +1227,9 @@ static RPCHelpMan gettxoutsetinfo() } } - if (GetUTXOStats(coins_view, *blockman, stats, node.rpc_interruption_point, pindex)) { + const std::optional maybe_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex, index_requested); + if (maybe_stats.has_value()) { + const CCoinsStats& stats = maybe_stats.value(); ret.pushKV("height", (int64_t)stats.nHeight); ret.pushKV("bestblock", stats.hashBlock.GetHex()); ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs); @@ -1221,10 +1248,13 @@ static RPCHelpMan gettxoutsetinfo() } else { ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount)); - CCoinsStats prev_stats{hash_type}; - + CCoinsStats prev_stats{}; if (pindex->nHeight > 0) { - GetUTXOStats(coins_view, *blockman, prev_stats, node.rpc_interruption_point, pindex->pprev); + const std::optional maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested); + if (!maybe_prev_stats) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); + } + prev_stats = maybe_prev_stats.value(); } UniValue block_info(UniValue::VOBJ); @@ -2701,7 +2731,7 @@ UniValue CreateUTXOSnapshot( const fs::path& temppath) { std::unique_ptr pcursor; - CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED}; + std::optional maybe_stats; const CBlockIndex* tip; { @@ -2721,19 +2751,20 @@ UniValue CreateUTXOSnapshot( chainstate.ForceFlushStateToDisk(); - if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, node.rpc_interruption_point)) { + maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, node.rpc_interruption_point); + if (!maybe_stats) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); } pcursor = chainstate.CoinsDB().Cursor(); - tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(stats.hashBlock)); + tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(maybe_stats->hashBlock)); } LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)", tip->nHeight, tip->GetBlockHash().ToString(), fs::PathToString(path), fs::PathToString(temppath))); - SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, tip->nChainTx}; + SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count, tip->nChainTx}; afile << metadata; @@ -2755,11 +2786,11 @@ UniValue CreateUTXOSnapshot( afile.fclose(); UniValue result(UniValue::VOBJ); - result.pushKV("coins_written", stats.coins_count); + result.pushKV("coins_written", maybe_stats->coins_count); result.pushKV("base_hash", tip->GetBlockHash().ToString()); result.pushKV("base_height", tip->nHeight); result.pushKV("path", path.utf8string()); - result.pushKV("txoutset_hash", stats.hashSerialized.ToString()); + result.pushKV("txoutset_hash", maybe_stats->hashSerialized.ToString()); // Cast required because univalue doesn't have serialization specified for // `unsigned int`, nChainTx's type. result.pushKV("nchaintx", uint64_t{tip->nChainTx}); diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 824c9d70ba74..0ef7c2bd30e6 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -8,10 +8,13 @@ #include #include #include +#include #include #include -#include +#include +#include +#include #include extern RecursiveMutex cs_main; @@ -19,7 +22,10 @@ extern RecursiveMutex cs_main; class CBlock; class CBlockIndex; class CChainState; -class UniValue; +class CCoinsView; +namespace kernel { +enum class CoinStatsHashType : uint8_t; +} namespace llmq { class CChainLocksHandler; class CInstantSendManager; @@ -29,6 +35,8 @@ class BlockManager; struct NodeContext; } // namespace node +class UniValue; + static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5; /** @@ -62,4 +70,15 @@ UniValue CreateUTXOSnapshot( const fs::path& path, const fs::path& tmppath); +/** + * Calculate statistics about the unspent transaction output set + * + * @param[in] index_requested Signals if the coinstatsindex should be used (when available). + */ +std::optional GetUTXOStats(CCoinsView* view, node::BlockManager& blockman, + kernel::CoinStatsHashType hash_type, + const std::function& interruption_point = {}, + const CBlockIndex* pindex = nullptr, + bool index_requested = true); + #endif // BITCOIN_RPC_BLOCKCHAIN_H diff --git a/src/rpc/util.h b/src/rpc/util.h index a9f5d6246fd1..ef19b689377b 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -5,7 +5,6 @@ #ifndef BITCOIN_RPC_UTIL_H #define BITCOIN_RPC_UTIL_H -#include #include #include #include diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index c6d2c5a82a15..02b8236e6718 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -620,7 +620,7 @@ class DescriptorImpl : public Descriptor assert(outscripts.size() == 1); subscripts.emplace_back(std::move(outscripts[0])); } - out = Merge(std::move(out), std::move(subprovider)); + out.Merge(std::move(subprovider)); std::vector pubkeys; pubkeys.reserve(entries.size()); @@ -938,19 +938,28 @@ std::unique_ptr ParseScript(uint32_t& key_exp_index, Span(std::move(pubkey)); } if (Func("pkh", expr)) { auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error); - if (!pubkey) return nullptr; + if (!pubkey) { + error = strprintf("pkh(): %s", error); + return nullptr; + } ++key_exp_index; return std::make_unique(std::move(pubkey)); } if (ctx == ParseScriptContext::TOP && Func("combo", expr)) { auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error); - if (!pubkey) return nullptr; + if (!pubkey) { + error = strprintf("combo(): %s", error); + return nullptr; + } ++key_exp_index; return std::make_unique(std::move(pubkey)); } else if (Func("combo", expr)) { @@ -973,7 +982,10 @@ std::unique_ptr ParseScript(uint32_t& key_exp_index, SpanGetSize() + 1; providers.emplace_back(std::move(pk)); key_exp_index++; diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp index ab147e55a585..3f6c9c2ad7c8 100644 --- a/src/script/signingprovider.cpp +++ b/src/script/signingprovider.cpp @@ -55,18 +55,13 @@ bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) } bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); } -FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b) -{ - FlatSigningProvider ret; - ret.scripts = a.scripts; - ret.scripts.insert(b.scripts.begin(), b.scripts.end()); - ret.pubkeys = a.pubkeys; - ret.pubkeys.insert(b.pubkeys.begin(), b.pubkeys.end()); - ret.keys = a.keys; - ret.keys.insert(b.keys.begin(), b.keys.end()); - ret.origins = a.origins; - ret.origins.insert(b.origins.begin(), b.origins.end()); - return ret; +FlatSigningProvider& FlatSigningProvider::Merge(FlatSigningProvider&& b) +{ + scripts.merge(b.scripts); + pubkeys.merge(b.pubkeys); + keys.merge(b.keys); + origins.merge(b.origins); + return *this; } bool FillableSigningProvider::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h index c231fb0c6544..f06cb47bb14c 100644 --- a/src/script/signingprovider.h +++ b/src/script/signingprovider.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_SCRIPT_SIGNINGPROVIDER_H #define BITCOIN_SCRIPT_SIGNINGPROVIDER_H +#include #include #include #include