diff --git a/.cirrus.yml b/.cirrus.yml index 8885cc93a2b82..eb269e283135b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -20,7 +20,7 @@ persistent_worker_template: &PERSISTENT_WORKER_TEMPLATE base_template: &BASE_TEMPLATE merge_base_script: # Unconditionally install git (used in fingerprint_script). - - bash -c "$PACKAGE_MANAGER_INSTALL git" + - git --version || bash -c "$PACKAGE_MANAGER_INSTALL git" - if [ "$CIRRUS_PR" = "" ]; then exit 0; fi - git fetch --depth=1 $CIRRUS_REPO_CLONE_URL "pull/${CIRRUS_PR}/merge" - git checkout FETCH_HEAD # Use merged changes to detect silent merge conflicts diff --git a/ci/README.md b/ci/README.md index 7cf3b1f563b36..b4edd4b191730 100644 --- a/ci/README.md +++ b/ci/README.md @@ -14,10 +14,10 @@ testing compared to other parts of the codebase. If you want to keep the work tr system in a virtual machine with a Linux operating system of your choice. To allow for a wide range of tested environments, but also ensure reproducibility to some extent, the test stage -requires `bash`, `docker`, and `python3` to be installed. To install all requirements on Ubuntu, run +requires `bash`, `docker`, and `python3` to be installed. To run on different architectures than the host `qemu` is also required. To install all requirements on Ubuntu, run ``` -sudo apt install bash docker.io python3 +sudo apt install bash docker.io python3 qemu-user-static ``` It is recommended to run the ci system in a clean env. To run the test stage diff --git a/ci/lint/04_install.sh b/ci/lint/04_install.sh index 476417d04b9ee..47cb8864c226e 100644 --- a/ci/lint/04_install.sh +++ b/ci/lint/04_install.sh @@ -10,10 +10,11 @@ export PATH=$PWD/ci/retry:$PATH ${CI_RETRY_EXE} apt-get update # Lint dependencies: +# - automake pkg-config libtool (for lint_includes_build_config) # - curl/xz-utils (to install shellcheck) # - git (used in many lint scripts) # - gpg (used by verify-commits) -${CI_RETRY_EXE} apt-get install -y curl xz-utils git gpg +${CI_RETRY_EXE} apt-get install -y automake pkg-config libtool curl xz-utils git gpg PYTHON_PATH="/python_build" if [ ! -d "${PYTHON_PATH}/bin" ]; then diff --git a/ci/test/02_run_container.sh b/ci/test/02_run_container.sh index 8a7a978994673..e6c4a613417f2 100755 --- a/ci/test/02_run_container.sh +++ b/ci/test/02_run_container.sh @@ -12,9 +12,9 @@ set -ex if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then # Export all env vars to avoid missing some. # Though, exclude those with newlines to avoid parsing problems. - python3 -c 'import os; [print(f"{key}={value}") for key, value in os.environ.items() if "\n" not in value and "HOME" != key and "PATH" != key and "USER" != key]' | tee /tmp/env + python3 -c 'import os; [print(f"{key}={value}") for key, value in os.environ.items() if "\n" not in value and "HOME" != key and "PATH" != key and "USER" != key]' | tee "/tmp/env-$USER-$CONTAINER_NAME" # System-dependent env vars must be kept as is. So read them from the container. - docker run --rm "${CI_IMAGE_NAME_TAG}" bash -c "env | grep --extended-regexp '^(HOME|PATH|USER)='" | tee --append /tmp/env + docker run --rm "${CI_IMAGE_NAME_TAG}" bash -c "env | grep --extended-regexp '^(HOME|PATH|USER)='" | tee --append "/tmp/env-$USER-$CONTAINER_NAME" echo "Creating $CI_IMAGE_NAME_TAG container to run in" DOCKER_BUILDKIT=1 docker build \ --file "${BASE_READ_ONLY_DIR}/ci/test_imagefile" \ @@ -44,6 +44,8 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then # When detecting podman-docker, `--external` should be added. docker image prune --force --filter "label=$CI_IMAGE_LABEL" + # Append $USER to /tmp/env to support multi-user systems and $CONTAINER_NAME + # to allow support starting multiple runs simultaneously by the same user. # shellcheck disable=SC2086 CI_CONTAINER_ID=$(docker run --cap-add LINUX_IMMUTABLE $CI_CONTAINER_CAP --rm --interactive --detach --tty \ --mount "type=bind,src=$BASE_READ_ONLY_DIR,dst=$BASE_READ_ONLY_DIR,readonly" \ @@ -52,7 +54,7 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then --mount "type=volume,src=${CONTAINER_NAME}_depends_sources,dst=$DEPENDS_DIR/sources" \ --mount "type=volume,src=${CONTAINER_NAME}_depends_SDKs_android,dst=$DEPENDS_DIR/SDKs/android" \ --mount "type=volume,src=${CONTAINER_NAME}_previous_releases,dst=$PREVIOUS_RELEASES_DIR" \ - --env-file /tmp/env \ + --env-file /tmp/env-$USER-$CONTAINER_NAME \ --name "$CONTAINER_NAME" \ "$CONTAINER_NAME") export CI_CONTAINER_ID diff --git a/ci/test/03_test_script.sh b/ci/test/03_test_script.sh index 9918ef66cd86d..242a12de48ba3 100755 --- a/ci/test/03_test_script.sh +++ b/ci/test/03_test_script.sh @@ -36,8 +36,8 @@ export HOST=${HOST:-$("$BASE_ROOT_DIR/depends/config.guess")} # CI, so as a temporary minimal fix to work around UB and CI failures, leave # bytes_written unmodified. # See https://github.com/bitcoin/bitcoin/pull/28359#issuecomment-1698694748 - echo 'diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc -index 65e31724bc..f61b471953 100644 + # Tee patch to stdout to make it clear CI is testing modified code. + tee >(patch -p1) <<'EOF' --- a/src/leveldb/db/db_impl.cc +++ b/src/leveldb/db/db_impl.cc @@ -1028,9 +1028,6 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { @@ -49,8 +49,8 @@ index 65e31724bc..f61b471953 100644 - } mutex_.Lock(); - stats_[compact->compaction->level() + 1].Add(stats);' | patch -p1 - git diff + stats_[compact->compaction->level() + 1].Add(stats); +EOF ) mkdir -p "${BASE_SCRATCH_DIR}/sanitizer-output/" diff --git a/contrib/devtools/test_utxo_snapshots.sh b/contrib/devtools/test_utxo_snapshots.sh index 075da001a1f04..3e073b93d12d8 100644 --- a/contrib/devtools/test_utxo_snapshots.sh +++ b/contrib/devtools/test_utxo_snapshots.sh @@ -183,8 +183,8 @@ echo "-- Initial state of the client:" client_rpc getchainstates echo -echo "-- Loading UTXO snapshot into client..." -client_rpc loadtxoutset "$UTXO_DAT_FILE" +echo "-- Loading UTXO snapshot into client. Calling RPC in a loop..." +while ! client_rpc loadtxoutset "$UTXO_DAT_FILE" ; do sleep 10; done watch -n 0.3 "( tail -n 14 $CLIENT_DATADIR/debug.log ; echo ; ./src/groestlcoin-cli -rpcport=$CLIENT_RPC_PORT -datadir=$CLIENT_DATADIR getchainstates) | cat" diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk index d5c8387efd05a..8beb25819bf7e 100644 --- a/depends/packages/bdb.mk +++ b/depends/packages/bdb.mk @@ -17,7 +17,6 @@ $(package)_config_opts_android=--with-pic $(package)_cflags+=-Wno-error=implicit-function-declaration -Wno-error=format-security -Wno-error=implicit-int $(package)_cppflags_freebsd=-D_XOPEN_SOURCE=600 -D__BSD_VISIBLE=1 $(package)_cppflags_netbsd=-D_XOPEN_SOURCE=600 -$(package)_cppflags_openbsd=-D_XOPEN_SOURCE=600 $(package)_cppflags_mingw32=-DUNICODE -D_UNICODE endef diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md index 00794a44d9df7..ef2e753d773d8 100644 --- a/doc/build-openbsd.md +++ b/doc/build-openbsd.md @@ -1,6 +1,6 @@ # OpenBSD Build Guide -**Updated for OpenBSD [7.3](https://www.openbsd.org/73.html)** +**Updated for OpenBSD [7.4](https://www.openbsd.org/74.html)** This guide describes how to build groestlcoind, command-line utilities, and GUI on OpenBSD. @@ -43,6 +43,8 @@ BerkeleyDB is only required to support legacy wallets. It is recommended to use Berkeley DB 5.3. You cannot use the BerkeleyDB library from ports. However you can build it yourself, [using depends](/depends). +Refer to [depends/README.md](/depends/README.md) for detailed instructions. + ```bash gmake -C depends NO_BOOST=1 NO_LIBEVENT=1 NO_QT=1 NO_SQLITE=1 NO_NATPMP=1 NO_UPNP=1 NO_ZMQ=1 NO_USDT=1 ... diff --git a/doc/developer-notes.md b/doc/developer-notes.md index f4bcb48b33e07..a15b9232dba25 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -113,6 +113,8 @@ code. between integer types, use functional casts such as `int(x)` or `int{x}` instead of `(int) x`. When casting between more complex types, use `static_cast`. Use `reinterpret_cast` and `const_cast` as appropriate. + - Prefer [`list initialization ({})`](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-list) where possible. + For example `int x{0};` instead of `int x = 0;` or `int x(0);` For function calls a namespace should be specified explicitly, unless such functions have been declared within it. Otherwise, [argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl), also known as ADL, could be @@ -138,7 +140,7 @@ int main() Block style example: ```c++ -int g_count = 0; +int g_count{0}; namespace foo { class Class @@ -150,7 +152,7 @@ public: { // Comment summarising what this section of code does for (int i = 0; i < n; ++i) { - int total_sum = 0; + int total_sum{0}; // When something fails, return early if (!Something()) return false; ... diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 9e5366f0b403d..b24405ce19c75 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -88,6 +88,8 @@ bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp bench_bench_bitcoin_SOURCES += bench/wallet_create.cpp bench_bench_bitcoin_SOURCES += bench/wallet_loading.cpp bench_bench_bitcoin_SOURCES += bench/wallet_create_tx.cpp +bench_bench_bitcoin_SOURCES += bench/wallet_ismine.cpp + bench_bench_bitcoin_LDADD += $(BDB_LIBS) $(SQLITE_LIBS) endif diff --git a/src/addrdb.cpp b/src/addrdb.cpp index 8cf932bcb6396..fd2a363b8adc4 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/addrman.cpp b/src/addrman.cpp index 542c4359506d6..c37334b23f350 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp index 0aa208dd4e1c0..0fed15b6076f5 100644 --- a/src/bench/verify_script.cpp +++ b/src/bench/verify_script.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #if defined(HAVE_CONSENSUS_LIB) diff --git a/src/bench/wallet_create.cpp b/src/bench/wallet_create.cpp index 993c4c4b3fde8..32f55f51e153e 100644 --- a/src/bench/wallet_create.cpp +++ b/src/bench/wallet_create.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #include diff --git a/src/bench/wallet_ismine.cpp b/src/bench/wallet_ismine.cpp new file mode 100644 index 0000000000000..3f922e18a58de --- /dev/null +++ b/src/bench/wallet_ismine.cpp @@ -0,0 +1,77 @@ +// 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. + +#if defined(HAVE_CONFIG_H) +#include +#endif // HAVE_CONFIG_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace wallet { +static void WalletIsMine(benchmark::Bench& bench, bool legacy_wallet, int num_combo = 0) +{ + const auto test_setup = MakeNoLogFileContext(); + + WalletContext context; + context.args = &test_setup->m_args; + context.chain = test_setup->m_node.chain.get(); + + // Setup the wallet + // Loading the wallet will also create it + uint64_t create_flags = 0; + if (!legacy_wallet) { + create_flags = WALLET_FLAG_DESCRIPTORS; + } + auto database = CreateMockableWalletDatabase(); + auto wallet = TestLoadWallet(std::move(database), context, create_flags); + + // For a descriptor wallet, fill with num_combo combo descriptors with random keys + // This benchmarks a non-HD wallet migrated to descriptors + if (!legacy_wallet && num_combo > 0) { + LOCK(wallet->cs_wallet); + for (int i = 0; i < num_combo; ++i) { + CKey key; + key.MakeNewKey(/*fCompressed=*/true); + FlatSigningProvider keys; + std::string error; + std::unique_ptr desc = Parse("combo(" + EncodeSecret(key) + ")", keys, error, /*require_checksum=*/false); + WalletDescriptor w_desc(std::move(desc), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/0, /*next_index=*/0); + auto spkm = wallet->AddWalletDescriptor(w_desc, keys, /*label=*/"", /*internal=*/false); + assert(spkm); + } + } + + const CScript script = GetScriptForDestination(DecodeDestination(ADDRESS_BCRT1_UNSPENDABLE)); + + bench.run([&] { + LOCK(wallet->cs_wallet); + isminetype mine = wallet->IsMine(script); + assert(mine == ISMINE_NO); + }); + + TestUnloadWallet(std::move(wallet)); +} + +#ifdef USE_BDB +static void WalletIsMineLegacy(benchmark::Bench& bench) { WalletIsMine(bench, /*legacy_wallet=*/true); } +BENCHMARK(WalletIsMineLegacy, benchmark::PriorityLevel::LOW); +#endif + +#ifdef USE_SQLITE +static void WalletIsMineDescriptors(benchmark::Bench& bench) { WalletIsMine(bench, /*legacy_wallet=*/false); } +static void WalletIsMineMigratedDescriptors(benchmark::Bench& bench) { WalletIsMine(bench, /*legacy_wallet=*/false, /*num_combo=*/2000); } +BENCHMARK(WalletIsMineDescriptors, benchmark::PriorityLevel::LOW); +BENCHMARK(WalletIsMineMigratedDescriptors, benchmark::PriorityLevel::LOW); +#endif +} // namespace wallet diff --git a/src/bench/wallet_loading.cpp b/src/bench/wallet_loading.cpp index b17c7fe05a8a3..6305126c7d452 100644 --- a/src/bench/wallet_loading.cpp +++ b/src/bench/wallet_loading.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #include diff --git a/src/clientversion.cpp b/src/clientversion.cpp index 1be81424e5645..0fc6c8d2d76c2 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/common/system.cpp b/src/common/system.cpp index ba42c6df50235..1fa53a5f34cab 100644 --- a/src/common/system.cpp +++ b/src/common/system.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/compat/compat.h b/src/compat/compat.h index 435a403552450..9ff9a335f8119 100644 --- a/src/compat/compat.h +++ b/src/compat/compat.h @@ -6,10 +6,6 @@ #ifndef BITCOIN_COMPAT_COMPAT_H #define BITCOIN_COMPAT_COMPAT_H -#if defined(HAVE_CONFIG_H) -#include -#endif - // Windows defines FD_SETSIZE to 64 (see _fd_types.h in mingw-w64), // which is too small for our usage, but allows us to redefine it safely. // We redefine it to be 1024, to match glibc, see typesizes.h. diff --git a/src/crypto/chacha20poly1305.cpp b/src/crypto/chacha20poly1305.cpp index 59671d304c449..3e8051c2dc4b9 100644 --- a/src/crypto/chacha20poly1305.cpp +++ b/src/crypto/chacha20poly1305.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/crypto/muhash.h b/src/crypto/muhash.h index 53c5a91a03d9a..cb53e1743ea2f 100644 --- a/src/crypto/muhash.h +++ b/src/crypto/muhash.h @@ -5,10 +5,6 @@ #ifndef BITCOIN_CRYPTO_MUHASH_H #define BITCOIN_CRYPTO_MUHASH_H -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index 11aabeb1da9ee..36ef6d9a1a9e4 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/netaddress.h b/src/netaddress.h index 0bbde43dd7ad9..c697b7e0a3375 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -5,10 +5,6 @@ #ifndef BITCOIN_NETADDRESS_H #define BITCOIN_NETADDRESS_H -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include #include diff --git a/src/netbase.h b/src/netbase.h index 41e36066c05d4..8272e9b8fb892 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -5,10 +5,6 @@ #ifndef BITCOIN_NETBASE_H #define BITCOIN_NETBASE_H -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include #include diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 05e58191fce20..f2f9371c650e0 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -2,10 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 246dff0069215..a4771bbb82944 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -2,10 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 3e177af53cd79..ad80922c8b582 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index ff7405d1395af..c31e06e88e5d3 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 5bf9338669ea4..a5e583a6272b4 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -2,10 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 1fa83c6708c33..956405bb2b255 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index b9182d6160f4e..2dfea449234ee 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index 88bc33098a3fc..2021e5f9dc552 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 488230da5e07a..267a457d8bef9 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -2,10 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index f73aa1e61e9af..b9ea97d5ec507 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -32,10 +32,6 @@ // sends them to the server. // -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 5d118f23335e5..cc7b69150af79 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -406,6 +406,7 @@ void SendCoinsDialog::presentPSBT(PartiallySignedTransaction& psbtx) msgBox.setInformativeText(tr("The PSBT has been copied to the clipboard. You can also save it.")); msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard); msgBox.setDefaultButton(QMessageBox::Discard); + msgBox.setObjectName("psbt_copied_message"); switch (msgBox.exec()) { case QMessageBox::Save: { QString selectedFilter; diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 9d14f873041ac..67d7089e2a47e 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -2,10 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include diff --git a/src/qt/sendcoinsrecipient.h b/src/qt/sendcoinsrecipient.h index 787a52b8b3411..aa2ea0498e87d 100644 --- a/src/qt/sendcoinsrecipient.h +++ b/src/qt/sendcoinsrecipient.h @@ -5,10 +5,6 @@ #ifndef BITCOIN_QT_SENDCOINSRECIPIENT_H #define BITCOIN_QT_SENDCOINSRECIPIENT_H -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp index 9007b183aa6e5..10abcb00eb816 100644 --- a/src/qt/test/apptests.cpp +++ b/src/qt/test/apptests.cpp @@ -14,10 +14,6 @@ #include #include -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include #include diff --git a/src/qt/test/optiontests.cpp b/src/qt/test/optiontests.cpp index 7100603616cee..b76e9ef499f21 100644 --- a/src/qt/test/optiontests.cpp +++ b/src/qt/test/optiontests.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #include diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index f5b86f44a61bf..603df0b15f2c7 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -418,7 +418,7 @@ void TestGUIWatchOnly(interfaces::Node& node, TestChain100Setup& test) timer.setInterval(500); QObject::connect(&timer, &QTimer::timeout, [&](){ for (QWidget* widget : QApplication::topLevelWidgets()) { - if (widget->inherits("QMessageBox")) { + if (widget->inherits("QMessageBox") && widget->objectName().compare("psbt_copied_message") == 0) { QMessageBox* dialog = qobject_cast(widget); QAbstractButton* button = dialog->button(QMessageBox::Discard); button->setEnabled(true); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index a916e4ead6f21..b848b8fe94f2a 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -2,10 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index c2da76fc15e52..1bdf94d3b57c4 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -2,10 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 68218b0c1b37f..503ee16823c5a 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -5,10 +5,6 @@ #ifndef BITCOIN_QT_WALLETMODEL_H #define BITCOIN_QT_WALLETMODEL_H -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 61ccd9dd82ecf..8222672bf3331 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -2,10 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include diff --git a/src/random.cpp b/src/random.cpp index ce4266a5675d1..4fc90997048ba 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/rest.cpp b/src/rest.cpp index fbbf6cfa847b8..91184745c888d 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index fef1423d1fe0f..5e7f62cffa7c6 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -2571,7 +2571,7 @@ static RPCHelpMan dumptxoutset() { return RPCHelpMan{ "dumptxoutset", - "Write the serialized UTXO set to disk.", + "Write the serialized UTXO set to a file.", { {"path", RPCArg::Type::STR, RPCArg::Optional::NO, "Path to the output file. If relative, will be prefixed by datadir."}, }, @@ -2699,7 +2699,7 @@ static RPCHelpMan loadtxoutset() { return RPCHelpMan{ "loadtxoutset", - "Load the serialized UTXO set from disk.\n" + "Load the serialized UTXO set from a file.\n" "Once this snapshot is loaded, its contents will be " "deserialized into a second chainstate data structure, which is then used to sync to " "the network's tip. " @@ -2753,34 +2753,14 @@ static RPCHelpMan loadtxoutset() throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to load UTXO snapshot, " "assumeutxo block hash in snapshot metadata not recognized (%s)", base_blockhash.ToString())); } - int max_secs_to_wait_for_headers = 60 * 10; - CBlockIndex* snapshot_start_block = nullptr; - - LogPrintf("[snapshot] waiting to see blockheader %s in headers chain before snapshot activation\n", - base_blockhash.ToString()); - - while (max_secs_to_wait_for_headers > 0) { - snapshot_start_block = WITH_LOCK(::cs_main, + CBlockIndex* snapshot_start_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(base_blockhash)); - max_secs_to_wait_for_headers -= 1; - - if (!IsRPCRunning()) { - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); - } - - if (!snapshot_start_block) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } else { - break; - } - } if (!snapshot_start_block) { - LogPrintf("[snapshot] timed out waiting for snapshot start blockheader %s\n", - base_blockhash.ToString()); throw JSONRPCError( RPC_INTERNAL_ERROR, - "Timed out waiting for base block header to appear in headers chain"); + strprintf("The base block header (%s) must appear in the headers chain. Make sure all headers are syncing, and call this RPC again.", + base_blockhash.ToString())); } if (!chainman.ActivateSnapshot(afile, metadata, false)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to load UTXO snapshot " + fs::PathToString(path)); diff --git a/src/rpc/external_signer.cpp b/src/rpc/external_signer.cpp index d0cbbd3d1af89..b835c60e339ac 100644 --- a/src/rpc/external_signer.cpp +++ b/src/rpc/external_signer.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #include diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 2e7a6ad0fe7d8..4e78b5982441b 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #include diff --git a/src/rpc/node.cpp b/src/rpc/node.cpp index a5d5af71d13e5..9a5b3cad3c7d1 100644 --- a/src/rpc/node.cpp +++ b/src/rpc/node.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #include diff --git a/src/rpc/register.h b/src/rpc/register.h index c88f49ecf0458..fd23dde75b714 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -5,6 +5,10 @@ #ifndef BITCOIN_RPC_REGISTER_H #define BITCOIN_RPC_REGISTER_H +#if defined(HAVE_CONFIG_H) +#include +#endif + /** These are in one header file to avoid creating tons of single-function * headers for everything under src/rpc/ */ class CRPCTable; diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index e1ac510cbaa8a..e7d1e3db4e590 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 93debb7b68aee..1c1bc8f3a34a1 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #include diff --git a/src/serialize.h b/src/serialize.h index a0b012b25c8be..7b336ce1af18e 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -6,6 +6,10 @@ #ifndef BITCOIN_SERIALIZE_H #define BITCOIN_SERIALIZE_H +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include // IWYU pragma: keep #include diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index f92d1d8fb7dd9..fe3ba38cde26c 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -5,10 +5,6 @@ #include #include -#if defined(HAVE_CONFIG_H) -#include -#endif - #ifdef WIN32 #include #else diff --git a/src/sync.cpp b/src/sync.cpp index 58752a9f182de..a8bdfc1dea0ee 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -2,10 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index ca279c8c0c2cc..e74d828034a59 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/test/span_tests.cpp b/src/test/span_tests.cpp index f6cac10b09bc5..aae61990f790b 100644 --- a/src/test/span_tests.cpp +++ b/src/test/span_tests.cpp @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_SUITE(span_tests) // aren't compatible with Spans at compile time. // // Previously there was a bug where writing a SFINAE check for vector was -// not possible, because in libstdc++ vector has a data() memeber +// not possible, because in libstdc++ vector has a data() member // returning void*, and the Span template guide ignored the data() return value, // so the template substitution would succeed, but the constructor would fail, // resulting in a fatal compile error, rather than a SFINAE error that could be diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index 7d1ac5a19a520..0903f987f6244 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -29,7 +29,14 @@ BOOST_AUTO_TEST_CASE(xor_file) BOOST_CHECK_EXCEPTION(xor_file.ignore(1), std::ios_base::failure, HasReason{"AutoFile::ignore: file handle is nullpt"}); } { - AutoFile xor_file{raw_file("wbx"), xor_pat}; +#ifdef __MINGW64__ + // Our usage of mingw-w64 and the msvcrt runtime does not support + // the x modifier for the _wfopen(). + const char* mode = "wb"; +#else + const char* mode = "wbx"; +#endif + AutoFile xor_file{raw_file(mode), xor_pat}; xor_file << test1 << test2; } { diff --git a/src/test/system_tests.cpp b/src/test/system_tests.cpp index 6a96b60db0e41..76a8f80ba18ae 100644 --- a/src/test/system_tests.cpp +++ b/src/test/system_tests.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // + +#if defined(HAVE_CONFIG_H) +#include +#endif #include #include #include diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 9f587d7ec0ba7..1f8dbac5bedb8 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/validation.cpp b/src/validation.cpp index ace742ae3c08d..78d1f6cf72daa 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/validation.h b/src/validation.h index fd9b53df8f2d7..94765bfbcd8a6 100644 --- a/src/validation.h +++ b/src/validation.h @@ -6,10 +6,6 @@ #ifndef BITCOIN_VALIDATION_H #define BITCOIN_VALIDATION_H -#if defined(HAVE_CONFIG_H) -#include -#endif - #include #include #include diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 088343458cc90..f151fad740283 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #include diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp index e32bddf00e5de..bcae1e6f0b720 100644 --- a/src/wallet/rpc/addresses.cpp +++ b/src/wallet/rpc/addresses.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #include diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp index c5cf9b7729c4d..0353d3cb423e4 100644 --- a/src/wallet/rpc/backup.cpp +++ b/src/wallet/rpc/backup.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #include diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index ee86b5b39a287..791af14323950 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include #include diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 090369cbbe983..e93db1ff40057 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -1288,6 +1288,9 @@ bool LegacyScriptPubKeyMan::TopUp(unsigned int kpSize) } if (!batch.TxnCommit()) throw std::runtime_error(strprintf("Error during keypool top up. Cannot commit changes for wallet %s", m_storage.GetDisplayName())); NotifyCanGetAddressesChanged(); + // Note: Unlike with DescriptorSPKM, LegacySPKM does not need to call + // m_storage.TopUpCallback() as we do not know what new scripts the LegacySPKM is + // watching for. CWallet's scriptPubKey cache is not used for LegacySPKMs. return true; } @@ -2152,6 +2155,7 @@ bool DescriptorScriptPubKeyMan::TopUp(unsigned int size) bool DescriptorScriptPubKeyMan::TopUpWithDB(WalletBatch& batch, unsigned int size) { LOCK(cs_desc_man); + std::set new_spks; unsigned int target_size; if (size > 0) { target_size = size; @@ -2182,6 +2186,7 @@ bool DescriptorScriptPubKeyMan::TopUpWithDB(WalletBatch& batch, unsigned int siz if (!m_wallet_descriptor.descriptor->Expand(i, provider, scripts_temp, out_keys, &temp_cache)) return false; } // Add all of the scriptPubKeys to the scriptPubKey set + new_spks.insert(scripts_temp.begin(), scripts_temp.end()); for (const CScript& script : scripts_temp) { m_map_script_pub_keys[script] = i; } @@ -2207,6 +2212,7 @@ bool DescriptorScriptPubKeyMan::TopUpWithDB(WalletBatch& batch, unsigned int siz // By this point, the cache size should be the size of the entire range assert(m_wallet_descriptor.range_end - 1 == m_max_cached_index); + m_storage.TopUpCallback(new_spks, this); NotifyCanGetAddressesChanged(); return true; } @@ -2620,6 +2626,7 @@ uint256 DescriptorScriptPubKeyMan::GetID() const void DescriptorScriptPubKeyMan::SetCache(const DescriptorCache& cache) { LOCK(cs_desc_man); + std::set new_spks; m_wallet_descriptor.cache = cache; for (int32_t i = m_wallet_descriptor.range_start; i < m_wallet_descriptor.range_end; ++i) { FlatSigningProvider out_keys; @@ -2628,6 +2635,7 @@ void DescriptorScriptPubKeyMan::SetCache(const DescriptorCache& cache) throw std::runtime_error("Error: Unable to expand wallet descriptor from cache"); } // Add all of the scriptPubKeys to the scriptPubKey set + new_spks.insert(scripts_temp.begin(), scripts_temp.end()); for (const CScript& script : scripts_temp) { if (m_map_script_pub_keys.count(script) != 0) { throw std::runtime_error(strprintf("Error: Already loaded script at index %d as being at index %d", i, m_map_script_pub_keys[script])); @@ -2645,6 +2653,8 @@ void DescriptorScriptPubKeyMan::SetCache(const DescriptorCache& cache) } m_max_cached_index++; } + // Make sure the wallet knows about our new spks + m_storage.TopUpCallback(new_spks, this); } bool DescriptorScriptPubKeyMan::AddKey(const CKeyID& key_id, const CKey& key) diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index 1c99e5ffcd43d..2d83ae556f7dc 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -31,6 +31,7 @@ struct bilingual_str; namespace wallet { struct MigrationData; +class ScriptPubKeyMan; // Wallet storage things that ScriptPubKeyMans need in order to be able to store things to the wallet database. // It provides access to things that are part of the entire wallet and not specific to a ScriptPubKeyMan such as @@ -51,6 +52,8 @@ class WalletStorage virtual bool WithEncryptionKey(std::function cb) const = 0; virtual bool HasEncryptionKeys() const = 0; virtual bool IsLocked() const = 0; + //! Callback function for after TopUp completes containing any scripts that were added by a SPKMan + virtual void TopUpCallback(const std::set&, ScriptPubKeyMan*) = 0; }; //! Constant representing an unknown spkm creation time diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp index 2cbeedbde6d68..34f18bf0b1cde 100644 --- a/src/wallet/sqlite.cpp +++ b/src/wallet/sqlite.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp index c933366354d41..f783424df8b37 100644 --- a/src/wallet/test/db_tests.cpp +++ b/src/wallet/test/db_tests.cpp @@ -2,6 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/wallet/test/fuzz/coinselection.cpp b/src/wallet/test/fuzz/coinselection.cpp index 3ffeecdf34384..297432de9efc5 100644 --- a/src/wallet/test/fuzz/coinselection.cpp +++ b/src/wallet/test/fuzz/coinselection.cpp @@ -158,7 +158,7 @@ FUZZ_TARGET(coin_grinder_is_optimal) // Only make UTXOs with positive effective value const CAmount input_fee = coin_params.m_effective_feerate.GetFee(n_input_bytes); // Ensure that each UTXO has at least an effective value of 1 sat - const CAmount eff_value{fuzzed_data_provider.ConsumeIntegralInRange(1, MAX_MONEY - max_spendable - max_output_groups + group_pos.size())}; + const CAmount eff_value{fuzzed_data_provider.ConsumeIntegralInRange(1, MAX_MONEY + group_pos.size() - max_spendable - max_output_groups)}; const CAmount amount{eff_value + input_fee}; std::vector temp_utxo_pool; diff --git a/src/wallet/test/util.h b/src/wallet/test/util.h index 8bd238648ff6d..9f2974ece6b22 100644 --- a/src/wallet/test/util.h +++ b/src/wallet/test/util.h @@ -5,6 +5,10 @@ #ifndef BITCOIN_WALLET_TEST_UTIL_H #define BITCOIN_WALLET_TEST_UTIL_H +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index cfe5d0b1cda6d..1c3cdb28ed126 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1571,11 +1571,22 @@ isminetype CWallet::IsMine(const CTxDestination& dest) const isminetype CWallet::IsMine(const CScript& script) const { AssertLockHeld(cs_wallet); - isminetype result = ISMINE_NO; - for (const auto& spk_man_pair : m_spk_managers) { - result = std::max(result, spk_man_pair.second->IsMine(script)); + + // Search the cache so that IsMine is called only on the relevant SPKMs instead of on everything in m_spk_managers + const auto& it = m_cached_spks.find(script); + if (it != m_cached_spks.end()) { + isminetype res = ISMINE_NO; + for (const auto& spkm : it->second) { + res = std::max(res, spkm->IsMine(script)); + } + Assume(res == ISMINE_SPENDABLE); + return res; } - return result; + + // Legacy wallet + if (IsLegacy()) return GetLegacyScriptPubKeyMan()->IsMine(script); + + return ISMINE_NO; } bool CWallet::IsMine(const CTransaction& tx) const @@ -3474,12 +3485,18 @@ ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const OutputType& type, bool intern std::set CWallet::GetScriptPubKeyMans(const CScript& script) const { std::set spk_mans; - SignatureData sigdata; - for (const auto& spk_man_pair : m_spk_managers) { - if (spk_man_pair.second->CanProvide(script, sigdata)) { - spk_mans.insert(spk_man_pair.second.get()); - } + + // Search the cache for relevant SPKMs instead of iterating m_spk_managers + const auto& it = m_cached_spks.find(script); + if (it != m_cached_spks.end()) { + spk_mans.insert(it->second.begin(), it->second.end()); } + SignatureData sigdata; + Assume(std::all_of(spk_mans.begin(), spk_mans.end(), [&script, &sigdata](ScriptPubKeyMan* spkm) { return spkm->CanProvide(script, sigdata); })); + + // Legacy wallet + if (IsLegacy() && GetLegacyScriptPubKeyMan()->CanProvide(script, sigdata)) spk_mans.insert(GetLegacyScriptPubKeyMan()); + return spk_mans; } @@ -3499,11 +3516,17 @@ std::unique_ptr CWallet::GetSolvingProvider(const CScript& scri std::unique_ptr CWallet::GetSolvingProvider(const CScript& script, SignatureData& sigdata) const { - for (const auto& spk_man_pair : m_spk_managers) { - if (spk_man_pair.second->CanProvide(script, sigdata)) { - return spk_man_pair.second->GetSolvingProvider(script); - } + // Search the cache for relevant SPKMs instead of iterating m_spk_managers + const auto& it = m_cached_spks.find(script); + if (it != m_cached_spks.end()) { + // All spkms for a given script must already be able to make a SigningProvider for the script, so just return the first one. + Assume(it->second.at(0)->CanProvide(script, sigdata)); + return it->second.at(0)->GetSolvingProvider(script); } + + // Legacy wallet + if (IsLegacy() && GetLegacyScriptPubKeyMan()->CanProvide(script, sigdata)) return GetLegacyScriptPubKeyMan()->GetSolvingProvider(script); + return nullptr; } @@ -3582,15 +3605,16 @@ void CWallet::ConnectScriptPubKeyManNotifiers() } } -void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc) +DescriptorScriptPubKeyMan& CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc) { + DescriptorScriptPubKeyMan* spk_manager; if (IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) { - auto spk_manager = std::unique_ptr(new ExternalSignerScriptPubKeyMan(*this, desc, m_keypool_size)); - AddScriptPubKeyMan(id, std::move(spk_manager)); + spk_manager = new ExternalSignerScriptPubKeyMan(*this, desc, m_keypool_size); } else { - auto spk_manager = std::unique_ptr(new DescriptorScriptPubKeyMan(*this, desc, m_keypool_size)); - AddScriptPubKeyMan(id, std::move(spk_manager)); + spk_manager = new DescriptorScriptPubKeyMan(*this, desc, m_keypool_size); } + AddScriptPubKeyMan(id, std::unique_ptr(spk_manager)); + return *spk_manager; } void CWallet::SetupDescriptorScriptPubKeyMans(const CExtKey& master_key) @@ -3945,6 +3969,8 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error) if (ExtractDestination(script, dest)) not_migrated_dests.emplace(dest); } + Assume(!m_cached_spks.empty()); + for (auto& desc_spkm : data.desc_spkms) { if (m_spk_managers.count(desc_spkm->GetID()) > 0) { error = _("Error: Duplicate descriptors created during migration. Your wallet may be corrupted."); @@ -4428,4 +4454,17 @@ util::Result MigrateLegacyToDescriptor(const std::string& walle } return res; } + +void CWallet::CacheNewScriptPubKeys(const std::set& spks, ScriptPubKeyMan* spkm) +{ + for (const auto& script : spks) { + m_cached_spks[script].push_back(spkm); + } +} + +void CWallet::TopUpCallback(const std::set& spks, ScriptPubKeyMan* spkm) +{ + // Update scriptPubKey cache + CacheNewScriptPubKeys(spks, spkm); +} } // namespace wallet diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 8823144e14658..cb9c28a8f222f 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -422,6 +422,9 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati // Same as 'AddActiveScriptPubKeyMan' but designed for use within a batch transaction context void AddActiveScriptPubKeyManWithDb(WalletBatch& batch, uint256 id, OutputType type, bool internal); + //! Cache of descriptor ScriptPubKeys used for IsMine. Maps ScriptPubKey to set of spkms + std::unordered_map, SaltedSipHasher> m_cached_spks; + /** * Catch wallet up to current chain, scanning new blocks, updating the best * block locator and m_last_block_processed, and registering for @@ -994,7 +997,7 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati void ConnectScriptPubKeyManNotifiers(); //! Instantiate a descriptor ScriptPubKeyMan from the WalletDescriptor and load it - void LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc); + DescriptorScriptPubKeyMan& LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc); //! Adds the active ScriptPubKeyMan for the specified type and internal. Writes it to the wallet file //! @param[in] id The unique id for the ScriptPubKeyMan @@ -1045,6 +1048,11 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati //! Whether the (external) signer performs R-value signature grinding bool CanGrindR() const; + + //! Add scriptPubKeys for this ScriptPubKeyMan into the scriptPubKey cache + void CacheNewScriptPubKeys(const std::set& spks, ScriptPubKeyMan* spkm); + + void TopUpCallback(const std::set& spks, ScriptPubKeyMan* spkm) override; }; /** diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 4598052081ab6..56ab8c627d8b8 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include @@ -804,10 +808,10 @@ static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& bat strErr = strprintf("%s\nDetails: %s", strErr, e.what()); return DBErrors::UNKNOWN_DESCRIPTOR; } - pwallet->LoadDescriptorScriptPubKeyMan(id, desc); + DescriptorScriptPubKeyMan& spkm = pwallet->LoadDescriptorScriptPubKeyMan(id, desc); // Prior to doing anything with this spkm, verify ID compatibility - if (id != pwallet->GetDescriptorScriptPubKeyMan(desc)->GetID()) { + if (id != spkm.GetID()) { strErr = "The descriptor ID calculated by the wallet differs from the one in DB"; return DBErrors::CORRUPT; } diff --git a/src/warnings.cpp b/src/warnings.cpp index cb73c7aea2db4..84b021dad54ce 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -3,6 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#if defined(HAVE_CONFIG_H) +#include +#endif + #include #include diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 58ef1e761d830..8a95975184b11 100644 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -1263,6 +1263,10 @@ def run_test(self): b89a = self.update_block("89a", [tx]) self.send_blocks([b89a], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) + # Don't use v2transport for the large reorg, which is too slow with the unoptimized python ChaCha20 implementation + if self.options.v2transport: + self.nodes[0].disconnect_p2ps() + self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore(), supports_v2_p2p=False) self.log.info("Test a re-org of one week's worth of blocks (1088 blocks)") self.move_tip(88) diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py index 814eb21e6fe85..39cff7b7382a0 100644 --- a/test/functional/feature_maxuploadtarget.py +++ b/test/functional/feature_maxuploadtarget.py @@ -81,7 +81,8 @@ def run_test(self): p2p_conns = [] for _ in range(3): - p2p_conns.append(self.nodes[0].add_p2p_connection(TestP2PConn())) + # Don't use v2transport in this test (too slow with the unoptimized python ChaCha20 implementation) + p2p_conns.append(self.nodes[0].add_p2p_connection(TestP2PConn(), supports_v2_p2p=False)) # Now mine a big block mine_large_block(self, self.wallet, self.nodes[0]) @@ -173,7 +174,7 @@ def run_test(self): self.assert_uploadtarget_state(target_reached=False, serve_historical_blocks=False) # Reconnect to self.nodes[0] - peer = self.nodes[0].add_p2p_connection(TestP2PConn()) + peer = self.nodes[0].add_p2p_connection(TestP2PConn(), supports_v2_p2p=False) # Sending mempool message shouldn't disconnect peer, as total limit isn't reached yet peer.send_and_ping(msg_mempool()) diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index b81eae25064d3..05aa40bbfeffc 100644 --- a/test/functional/interface_rest.py +++ b/test/functional/interface_rest.py @@ -337,6 +337,9 @@ def run_test(self): assert_greater_than(json_obj['bytes'], 300) mempool_info = self.nodes[0].getmempoolinfo() + # pop unstable unbroadcastcount before check + for obj in [json_obj, mempool_info]: + obj.pop("unbroadcastcount") assert_equal(json_obj, mempool_info) # Check that there are our submitted transactions in the TX memory pool diff --git a/test/functional/p2p_ibd_stalling.py b/test/functional/p2p_ibd_stalling.py index 0eb37fa92f6e4..830f374d6322b 100755 --- a/test/functional/p2p_ibd_stalling.py +++ b/test/functional/p2p_ibd_stalling.py @@ -80,7 +80,8 @@ def run_test(self): # Need to wait until 1023 blocks are received - the magic total bytes number is a workaround in lack of an rpc # returning the number of downloaded (but not connected) blocks. - self.wait_until(lambda: self.total_bytes_recv_for_blocks() == 172761) + bytes_recv = 172761 if not self.options.v2transport else 169692 + self.wait_until(lambda: self.total_bytes_recv_for_blocks() == bytes_recv) self.all_sync_send_with_ping(peers) # If there was a peer marked for stalling, it would get disconnected diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py index 4916d36ab7015..40a69936bccca 100644 --- a/test/functional/p2p_invalid_messages.py +++ b/test/functional/p2p_invalid_messages.py @@ -109,6 +109,9 @@ def test_duplicate_version_msg(self): self.nodes[0].disconnect_p2ps() def test_magic_bytes(self): + # Skip with v2, magic bytes are v1-specific + if self.options.v2transport: + return self.log.info("Test message with invalid magic bytes disconnects peer") conn = self.nodes[0].add_p2p_connection(P2PDataStore()) with self.nodes[0].assert_debug_log(['Header error: Wrong MessageStart ffffffff received']): @@ -120,6 +123,9 @@ def test_magic_bytes(self): self.nodes[0].disconnect_p2ps() def test_checksum(self): + # Skip with v2, the checksum is v1-specific + if self.options.v2transport: + return self.log.info("Test message with invalid checksum logs an error") conn = self.nodes[0].add_p2p_connection(P2PDataStore()) with self.nodes[0].assert_debug_log(['Header error: Wrong checksum (badmsg, 2 bytes), expected 78df0a04 was ffffffff']): @@ -137,7 +143,11 @@ def test_checksum(self): def test_size(self): self.log.info("Test message with oversized payload disconnects peer") conn = self.nodes[0].add_p2p_connection(P2PDataStore()) - with self.nodes[0].assert_debug_log(['Header error: Size too large (badmsg, 4000001 bytes)']): + error_msg = ( + ['V2 transport error: packet too large (4000014 bytes)'] if self.options.v2transport + else ['Header error: Size too large (badmsg, 4000001 bytes)'] + ) + with self.nodes[0].assert_debug_log(error_msg): msg = msg_unrecognized(str_data="d" * (VALID_DATA_LIMIT + 1)) msg = conn.build_message(msg) conn.send_raw_message(msg) @@ -147,15 +157,26 @@ def test_size(self): def test_msgtype(self): self.log.info("Test message with invalid message type logs an error") conn = self.nodes[0].add_p2p_connection(P2PDataStore()) - with self.nodes[0].assert_debug_log(['Header error: Invalid message type']): + if self.options.v2transport: + msgtype = 99 # not defined msg = msg_unrecognized(str_data="d") - msg = conn.build_message(msg) - # Modify msgtype - msg = msg[:7] + b'\x00' + msg[7 + 1:] - conn.send_raw_message(msg) - conn.sync_with_ping(timeout=1) - # Check that traffic is accounted for (24 bytes header + 2 bytes payload) - assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26) + contents = msgtype.to_bytes(1, 'big') + msg.serialize() + tmsg = conn.v2_state.v2_enc_packet(contents, ignore=False) + with self.nodes[0].assert_debug_log(['V2 transport error: invalid message type']): + conn.send_raw_message(tmsg) + conn.sync_with_ping(timeout=1) + # Check that traffic is accounted for (20 bytes plus 3 bytes contents) + assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 23) + else: + with self.nodes[0].assert_debug_log(['Header error: Invalid message type']): + msg = msg_unrecognized(str_data="d") + msg = conn.build_message(msg) + # Modify msgtype + msg = msg[:7] + b'\x00' + msg[7 + 1:] + conn.send_raw_message(msg) + conn.sync_with_ping(timeout=1) + # Check that traffic is accounted for (24 bytes header + 2 bytes payload) + assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26) self.nodes[0].disconnect_p2ps() def test_addrv2(self, label, required_log_messages, raw_addrv2): @@ -306,8 +327,10 @@ def test_noncontinuous_headers_msg(self): def test_resource_exhaustion(self): self.log.info("Test node stays up despite many large junk messages") - conn = self.nodes[0].add_p2p_connection(P2PDataStore()) - conn2 = self.nodes[0].add_p2p_connection(P2PDataStore()) + # Don't use v2 here - the non-optimised encryption would take too long to encrypt + # the large messages + conn = self.nodes[0].add_p2p_connection(P2PDataStore(), supports_v2_p2p=False) + conn2 = self.nodes[0].add_p2p_connection(P2PDataStore(), supports_v2_p2p=False) msg_at_size = msg_unrecognized(str_data="b" * VALID_DATA_LIMIT) assert len(msg_at_size.serialize()) == MAX_PROTOCOL_MESSAGE_LENGTH diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py index b4fa5099d8758..80d7b6e9ae363 100644 --- a/test/functional/p2p_timeouts.py +++ b/test/functional/p2p_timeouts.py @@ -69,11 +69,8 @@ def run_test(self): with self.nodes[0].assert_debug_log(['Unsupported message "ping" prior to verack from peer=0']): no_verack_node.send_message(msg_ping()) - # With v2, non-version messages before the handshake would be interpreted as part of the key exchange. - # Therefore, don't execute this part of the test if v2transport is chosen. - if not self.options.v2transport: - with self.nodes[0].assert_debug_log(['non-version message before version handshake. Message "ping" from peer=1']): - no_version_node.send_message(msg_ping()) + with self.nodes[0].assert_debug_log(['non-version message before version handshake. Message "ping" from peer=1']): + no_version_node.send_message(msg_ping()) self.mock_forward(1) assert "version" in no_verack_node.last_message @@ -83,14 +80,20 @@ def run_test(self): assert no_send_node.is_connected no_verack_node.send_message(msg_ping()) - if not self.options.v2transport: - no_version_node.send_message(msg_ping()) - - expected_timeout_logs = [ - "version handshake timeout peer=0", - f"socket no message in first 3 seconds, {'0' if self.options.v2transport else '1'} 0 peer=1", - "socket no message in first 3 seconds, 0 0 peer=2", - ] + no_version_node.send_message(msg_ping()) + + if self.options.v2transport: + expected_timeout_logs = [ + "version handshake timeout peer=0", + "version handshake timeout peer=1", + "version handshake timeout peer=2", + ] + else: + expected_timeout_logs = [ + "version handshake timeout peer=0", + "socket no message in first 3 seconds, 1 0 peer=1", + "socket no message in first 3 seconds, 0 0 peer=2", + ] with self.nodes[0].assert_debug_log(expected_msgs=expected_timeout_logs): self.mock_forward(2) diff --git a/test/functional/p2p_v2_earlykeyresponse.py b/test/functional/p2p_v2_earlykeyresponse.py index 1f570e6010f8d..32d2e1148a940 100755 --- a/test/functional/p2p_v2_earlykeyresponse.py +++ b/test/functional/p2p_v2_earlykeyresponse.py @@ -75,7 +75,7 @@ def run_test(self): self.log.info('Sending first 4 bytes of ellswift which match network magic') self.log.info('If a response is received, assertion failure would happen in our custom data_received() function') # send happens in `initiate_v2_handshake()` in `connection_made()` - peer1 = node0.add_p2p_connection(PeerEarlyKey(), wait_for_verack=False, send_version=False, supports_v2_p2p=True) + peer1 = node0.add_p2p_connection(PeerEarlyKey(), wait_for_verack=False, send_version=False, supports_v2_p2p=True, wait_for_v2_handshake=False) self.wait_until(lambda: peer1.connection_opened) self.log.info('Sending remaining ellswift and garbage which are different from V1_PREFIX. Since a response is') self.log.info('expected now, our custom data_received() function wouldn\'t result in assertion failure') diff --git a/test/functional/p2p_v2_transport.py b/test/functional/p2p_v2_transport.py index ec43fc5a97c45..fe2449124d9f1 100755 --- a/test/functional/p2p_v2_transport.py +++ b/test/functional/p2p_v2_transport.py @@ -12,6 +12,7 @@ from test_framework.util import ( assert_equal, p2p_port, + assert_raises_rpc_error ) @@ -59,6 +60,11 @@ def run_test(self): # V1 nodes can sync with each other assert_equal(self.nodes[2].getblockcount(), 0) assert_equal(self.nodes[3].getblockcount(), 0) + + # addnode rpc error when v2transport requested but not enabled + ip_port = "127.0.0.1:{}".format(p2p_port(3)) + assert_raises_rpc_error(-8, "Error: v2transport requested but not enabled (see -v2transport)", self.nodes[2].addnode, node=ip_port, command='add', v2transport=True) + with self.nodes[2].assert_debug_log(expected_msgs=[], unexpected_msgs=[sending_handshake, downgrading_to_v1]): self.connect_nodes(2, 3, peer_advertises_v2=False) diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index afb75ab208fe4..b4a58df5b2e29 100644 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -117,6 +117,9 @@ def test_getpeerinfo(self): peer_info = self.nodes[0].getpeerinfo()[no_version_peer_id] peer_info.pop("addr") peer_info.pop("addrbind") + # The next two fields will vary for v2 connections because we send a rng-based number of decoy messages + peer_info.pop("bytesrecv") + peer_info.pop("bytessent") assert_equal( peer_info, { @@ -125,9 +128,7 @@ def test_getpeerinfo(self): "addr_relay_enabled": False, "bip152_hb_from": False, "bip152_hb_to": False, - "bytesrecv": 0, "bytesrecv_per_msg": {}, - "bytessent": 0, "bytessent_per_msg": {}, "connection_type": "inbound", "conntime": no_version_peer_conntime, @@ -136,8 +137,8 @@ def test_getpeerinfo(self): "inflight": [], "last_block": 0, "last_transaction": 0, - "lastrecv": 0, - "lastsend": 0, + "lastrecv": 0 if not self.options.v2transport else no_version_peer_conntime, + "lastsend": 0 if not self.options.v2transport else no_version_peer_conntime, "minfeefilter": Decimal("0E-8"), "network": "not_publicly_routable", "permissions": [], @@ -145,13 +146,13 @@ def test_getpeerinfo(self): "relaytxes": False, "services": "0000000000000000", "servicesnames": [], - "session_id": "", + "session_id": "" if not self.options.v2transport else no_version_peer.v2_state.peer['session_id'].hex(), "startingheight": -1, "subver": "", "synced_blocks": -1, "synced_headers": -1, "timeoffset": 0, - "transport_protocol_type": "v1" if not self.options.v2transport else "detecting", + "transport_protocol_type": "v1" if not self.options.v2transport else "v2", "version": 0, }, ) diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 1b7b328559a35..9f23161ac821a 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -747,7 +747,7 @@ def SegwitV0SignatureMsg(script, txTo, inIdx, hashtype, amount): ss += struct.pack(" PathBuf { PathBuf::from(check_output(git().args(["rev-parse", "--show-toplevel"])).unwrap()) } +/// Return all subtree paths +fn get_subtrees() -> Vec<&'static str> { + vec![ + "src/crc32c", + "src/crypto/ctaes", + "src/leveldb", + "src/minisketch", + "src/secp256k1", + ] +} + +/// Return the pathspecs to exclude all subtrees +fn get_pathspecs_exclude_subtrees() -> Vec { + get_subtrees() + .iter() + .map(|s| format!(":(exclude){}", s)) + .collect() +} + fn lint_subtree() -> LintResult { // This only checks that the trees are pure subtrees, it is not doing a full // check with -r to not have to fetch all the remotes. let mut good = true; - for subtree in [ - "src/crypto/ctaes", - "src/secp256k1", - "src/minisketch", - "src/leveldb", - "src/crc32c", - ] { + for subtree in get_subtrees() { good &= Command::new("test/lint/git-subtree-check.sh") .arg(subtree) .status() @@ -82,6 +95,102 @@ fs:: namespace, which has unsafe filesystem functions marked as deleted. } } +fn lint_includes_build_config() -> LintResult { + let config_path = "./src/config/bitcoin-config.h.in"; + let include_directive = "#include "; + if !Path::new(config_path).is_file() { + assert!(Command::new("./autogen.sh") + .status() + .expect("command error") + .success()); + } + let defines_regex = format!( + r"^\s*(?!//).*({})", + check_output(Command::new("grep").args(["undef ", "--", config_path])) + .expect("grep failed") + .lines() + .map(|line| { + line.split("undef ") + .nth(1) + .unwrap_or_else(|| panic!("Could not extract name in line: {line}")) + }) + .collect::>() + .join("|") + ); + let print_affected_files = |mode: bool| { + // * mode==true: Print files which use the define, but lack the include + // * mode==false: Print files which lack the define, but use the include + let defines_files = check_output( + git() + .args([ + "grep", + "--perl-regexp", + if mode { + "--files-with-matches" + } else { + "--files-without-match" + }, + &defines_regex, + "--", + "*.cpp", + "*.h", + ]) + .args(get_pathspecs_exclude_subtrees()) + .args([ + // These are exceptions which don't use bitcoin-config.h, rather the Makefile.am adds + // these cppflags manually. + ":(exclude)src/crypto/sha256_arm_shani.cpp", + ":(exclude)src/crypto/sha256_avx2.cpp", + ":(exclude)src/crypto/sha256_sse41.cpp", + ":(exclude)src/crypto/sha256_x86_shani.cpp", + ]), + ) + .expect("grep failed"); + git() + .args([ + "grep", + if mode { + "--files-without-match" + } else { + "--files-with-matches" + }, + include_directive, + "--", + ]) + .args(defines_files.lines()) + .status() + .expect("command error") + .success() + }; + let missing = print_affected_files(true); + if missing { + return Err(format!( + r#" +^^^ +One or more files use a symbol declared in the bitcoin-config.h header. However, they are not +including the header. This is problematic, because the header may or may not be indirectly +included. If the indirect include were to be intentionally or accidentally removed, the build could +still succeed, but silently be buggy. For example, a slower fallback algorithm could be picked, +even though bitcoin-config.h indicates that a faster feature is available and should be used. + +If you are unsure which symbol is used, you can find it with this command: +git grep --perl-regexp '{}' -- file_name + "#, + defines_regex + )); + } + let redundant = print_affected_files(false); + if redundant { + return Err(r#" +^^^ +None of the files use a symbol declared in the bitcoin-config.h header. However, they are including +the header. Consider removing the unused include. + "# + .to_string()); + } + Ok(()) +} + fn lint_doc() -> LintResult { if Command::new("test/lint/check-doc.py") .status() @@ -123,6 +232,7 @@ fn main() -> ExitCode { let test_list: Vec<(&str, LintFn)> = vec![ ("subtree check", lint_subtree), ("std::filesystem check", lint_std_filesystem), + ("build config includes check", lint_includes_build_config), ("-help=1 documentation check", lint_doc), ("lint-*.py scripts", lint_all), ]; @@ -134,7 +244,7 @@ fn main() -> ExitCode { // chdir to root before each lint test env::set_current_dir(&git_root).unwrap(); if let Err(err) = lint_fn() { - println!("{err}\n^---- Failure generated from {lint_name}!"); + println!("{err}\n^---- ⚠️ Failure generated from {lint_name}!"); test_failed = true; } }